module ActiveScaffold::Actions::Core
Public Class Methods
# File lib/active_scaffold/actions/core.rb, line 3 def self.included(base) base.class_eval do before_action :set_vary_accept_header before_action :check_input_device before_action :register_constraints_with_action_columns, :unless => :nested? after_action :clear_flashes after_action :dl_cookie around_action :clear_storage rescue_from ActiveScaffold::RecordNotAllowed, ActiveScaffold::ActionNotAllowed, :with => :deny_access end base.helper_method :active_scaffold_config base.helper_method :successful? base.helper_method :nested? base.helper_method :grouped_search? base.helper_method :embedded? base.helper_method :loading_embedded? base.helper_method :calculate_query base.helper_method :new_model base.helper_method :touch_device? base.helper_method :hover_via_click? end
Public Instance Methods
# File lib/active_scaffold/actions/core.rb, line 25 def render_field if request.get? || request.head? render_field_for_inplace_editing respond_to do |format| format.js { render :action => 'render_field_inplace', :layout => false } end else render_field_for_update_columns respond_to { |format| format.js } end end
Protected Instance Methods
Returns true if the client accepts one of the MIME types passed to it ex: accepts? :html, :xml
# File lib/active_scaffold/actions/core.rb, line 198 def accepts?(*types) request.accepts.compact.each do |priority| # Because IE always sends */* in the accepts header and we assume # that if you really wanted XML or something else you would say so # explicitly, we will assume */* to only ask for :html return types.include?(:html) if priority == Mime::ALL return true if types.include?(priority.to_sym) end false end
# File lib/active_scaffold/actions/core.rb, line 407 def action_confirmation_respond_to_html(confirm_action = action_name.to_sym) link = active_scaffold_config.action_links[confirm_action] render :action => 'action_confirmation', :locals => {:record => @record, :link => link} end
# File lib/active_scaffold/actions/core.rb, line 412 def action_update_respond_on_iframe responds_to_parent { action_update_respond_to_js } end
# File lib/active_scaffold/actions/core.rb, line 416 def action_update_respond_to_html redirect_to :action => 'index' end
# File lib/active_scaffold/actions/core.rb, line 420 def action_update_respond_to_js render :action => 'on_action_update', :formats => [:js], :layout => false end
# File lib/active_scaffold/actions/core.rb, line 428 def action_update_respond_to_json response_to_api(:json, list_columns_names) end
# File lib/active_scaffold/actions/core.rb, line 424 def action_update_respond_to_xml response_to_api(:xml, list_columns_names) end
# File lib/active_scaffold/actions/core.rb, line 323 def active_scaffold_embedded_params params[:embedded] || {} end
override this method if you want to do something after render_field
# File lib/active_scaffold/actions/core.rb, line 170 def after_render_field(record, column); end
# File lib/active_scaffold/actions/core.rb, line 460 def association_columns(columns) columns.select { |col| active_scaffold_config.columns[col]&.association } end
Overide this method on your controller to provide model with named scopes
# File lib/active_scaffold/actions/core.rb, line 258 def beginning_of_chain active_scaffold_config.model end
# File lib/active_scaffold/actions/core.rb, line 338 def check_input_device return unless session[:input_device_type].nil? return if request.env['HTTP_USER_AGENT'].nil? if request.env['HTTP_USER_AGENT'].match?(/(iPhone|iPod|iPad)/i) session[:input_device_type] = 'TOUCH' session[:hover_supported] = false else session[:input_device_type] = 'MOUSE' session[:hover_supported] = true end end
# File lib/active_scaffold/actions/core.rb, line 176 def clear_flashes flash.clear if request.xhr? end
# File lib/active_scaffold/actions/core.rb, line 327 def clear_storage yield if block_given? ensure session_index = active_scaffold_session_storage_key session.delete(session_index) if session[session_index].blank? end
# File lib/active_scaffold/actions/core.rb, line 450 def conditional_get_support? request.get? && active_scaffold_config.conditional_get_support end
Builds search conditions by search params for column names. This allows urls like “contacts/list?company_id=5”.
# File lib/active_scaffold/actions/core.rb, line 263 def conditions_from_params @conditions_from_params ||= begin conditions = [{}] params.except(:controller, :action, :page, :sort, :sort_direction, :format, :id).each do |key, value| distinct = true if key.match?(/!$/) column = active_scaffold_config._columns_hash[key.to_s[0..(distinct ? -2 : -1)]] next unless column key = column.name.to_sym not_string = %i[string text].exclude?(column.type) next if active_scaffold_constraints[key] next if nested? && nested.param_name == key range = %i[date datetime integer decimal float bigint].include?(column.type) && value.is_a?(String) && value.scan('..').size == 1 value = value.split('..') if range value = if value.is_a?(Array) value.map { |v| v == '' && not_string ? nil : ActiveScaffold::Core.column_type_cast(v, column) } elsif value == '' && (not_string || column.null) ActiveScaffold::Core.column_type_cast(column.default, column) else ActiveScaffold::Core.column_type_cast(value, column) end value = Range.new(*value) if range if distinct conditions << active_scaffold_config.model.arel_table[key].not_eq(value) else conditions[0][key] = value end end conditions end end
# File lib/active_scaffold/actions/core.rb, line 362 def controller_params?(value) value.is_a?(::ActionController::Parameters) end
# File lib/active_scaffold/actions/core.rb, line 154 def copy_attributes(orig, dst = nil) dst ||= orig.class.new orig.attributes.each { |attr, value| dst.send :write_attribute, attr, value } dst end
# File lib/active_scaffold/actions/core.rb, line 192 def default_formats %i[html js json xml] end
# File lib/active_scaffold/actions/core.rb, line 184 def each_marked_record(&block) active_scaffold_config.model.as_marked.each(&block) end
# File lib/active_scaffold/actions/core.rb, line 43 def embedded? params[:eid] end
# File lib/active_scaffold/actions/core.rb, line 317 def get_row(crud_type_or_security_options = :read) klass = beginning_of_chain klass = klass.preload(active_scaffold_preload) unless active_scaffold_config.mongoid? @record = find_if_allowed(params[:id], crud_type_or_security_options, klass) end
# File lib/active_scaffold/actions/core.rb, line 51 def grouped_search? false end
# File lib/active_scaffold/actions/core.rb, line 354 def hover_via_click? session[:hover_supported] == false end
# File lib/active_scaffold/actions/core.rb, line 39 def loading_embedded? @loading_embedded ||= active_scaffold_embedded_params.delete(:loading) end
# File lib/active_scaffold/actions/core.rb, line 188 def marked_records active_scaffold_session_storage['marked_records'] ||= {} end
# File lib/active_scaffold/actions/core.rb, line 47 def nested? false end
# File lib/active_scaffold/actions/core.rb, line 296 def new_model relation = beginning_of_chain if nested? && nested.plural_association? && nested.match_model?(active_scaffold_config.model) build_options = sti_nested_build_options(relation.klass) end relation.respond_to?(:build) ? relation.build(build_options || {}) : relation.new end
# File lib/active_scaffold/actions/core.rb, line 432 def objects_for_etag @last_modified ||= @record.updated_at [@record, ('xhr' if request.xhr?)] end
# File lib/active_scaffold/actions/core.rb, line 366 def params_hash(value) if controller_params?(value) value.to_unsafe_h.with_indifferent_access else value end end
# File lib/active_scaffold/actions/core.rb, line 358 def params_hash?(value) value.is_a?(Hash) || controller_params?(value) end
# File lib/active_scaffold/actions/core.rb, line 124 def parent_controller_name "#{params[:parent_controller].camelize}Controller" end
# File lib/active_scaffold/actions/core.rb, line 160 def parent_sti_controller return unless params[:parent_sti] unless defined? @parent_sti_controller controller = look_for_parent_sti_controller @parent_sti_controller = controller.controller_path == params[:parent_sti] ? controller : false end @parent_sti_controller end
call this method in your action_link action to simplify processing of actions eg for member action_link :fire process_action_link_action
do |record|
record.update_attributes(:fired => true) self.successful = true flash[:info] = 'Player fired'
end
# File lib/active_scaffold/actions/core.rb, line 381 def process_action_link_action(render_action = :action_update, crud_type_or_security_options = nil) if request.get? || request.head? # someone has disabled javascript, we have to show confirmation form first @record = find_if_allowed(params[:id], :read) if params[:id] respond_to_action(:action_confirmation) else @action_link = active_scaffold_config.action_links[action_name] if params[:id] crud_type_or_security_options ||= {:crud_type => request.delete? ? :delete : :update, :action => action_name} get_row(crud_type_or_security_options) if @record.nil? self.successful = false flash[:error] = as_(:no_authorization_for_action, :action => action_name) else yield @record end else if @action_link && respond_to?(@action_link.security_method, true) && !send(@action_link.security_method) raise ActiveScaffold::ActionNotAllowed end yield end respond_to_action(render_action) end end
# File lib/active_scaffold/actions/core.rb, line 55 def render_field_for_inplace_editing @column = active_scaffold_config.columns[params[:update_column]] @record = find_if_allowed(params[:id], :crud_type => :update, :column => params[:update_column]) end
# File lib/active_scaffold/actions/core.rb, line 60 def render_field_for_update_columns return if (@column = active_scaffold_config.columns[params.delete(:column)]).nil? @source_id = params.delete(:source_id) @columns = @column.update_columns || [] @scope = params.delete(:scope) if @scope @form_action = :subform elsif active_scaffold_config.actions.include? params[:form_action]&.to_sym @form_action = params.delete(:form_action).to_sym end @form_action ||= params[:id] ? :update : :create @main_columns = active_scaffold_config.send(@form_action).columns @columns << @column.name if @column.options[:refresh_link] && @columns.exclude?(@column.name) @record = if @column.send_form_on_update_column updated_record_with_form(@main_columns, params[:record] || params[:search], @scope) else updated_record_with_column(@column, params.delete(:value), @scope) end # if @scope has more than 2 ] then it's subform inside subform, and assign parent would fail (found associotion may be through association) setup_parent(@record) if main_form_controller && @scope && @scope.scan(']').size == 2 after_render_field(@record, @column) end
API response object that will be converted to XML/JSON using to_xxx
# File lib/active_scaffold/actions/core.rb, line 219 def response_object @response_object ||= successful? ? (@record || @records) : @record.errors end
# File lib/active_scaffold/actions/core.rb, line 210 def response_status if successful? action_name == 'create' ? 201 : 200 else 422 end end
# File lib/active_scaffold/actions/core.rb, line 223 def response_to_api(format, columns_names, options = {}) render( options.reverse_merge( format => response_object, :only => columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(columns_names), :methods => virtual_columns(columns_names), :status => response_status ) ) end
Redirect to the main page (override if the ActiveScaffold
is used as a component on another controllers page) for Javascript degradation
# File lib/active_scaffold/actions/core.rb, line 250 def return_to_main options = main_path_to_return # use url_for in case main_path_to_return returns Hash with status param, # which would be interpreted as status option to redirect_to instead of url param redirect_to options.is_a?(Hash) ? url_for(options) : options end
# File lib/active_scaffold/actions/core.rb, line 334 def set_vary_accept_header response.headers['Vary'] = 'Accept' end
# File lib/active_scaffold/actions/core.rb, line 128 def setup_parent(record) cfg = main_form_controller.active_scaffold_config association = cfg.columns[subform_child_association]&.association&.reverse_association return if association.nil? parent_model = cfg.model parent = parent_model.new copy_attributes(find_if_allowed(params[:parent_id], :read, parent_model), parent) if params[:parent_id] parent.id = params[:parent_id] parent = update_record_from_params(parent, cfg.send(params[:parent_id] ? :update : :create).columns, params[:record], true) if @column.send_form_on_update_column apply_constraints_to_record(parent) unless params[:parent_id] if association.collection? record.send(association.name) << parent else record.send("#{association.name}=", parent) end if params[:nested] # form in nested scaffold, set nested parent_record to parent nested = ActiveScaffold::DataStructures::NestedInfo.get(parent.class, params[:nested]) if nested&.child_association && !nested.child_association.polymorphic? apply_constraints_to_record(parent, constraints: {nested.child_association.name => nested.parent_id}) end end parent end
# File lib/active_scaffold/actions/core.rb, line 304 def sti_nested_build_options(klass) config = active_scaffold_config_for(klass) return unless config column = klass.inheritance_column return unless column && config._columns_hash[column] model_name = params.delete(column) # in new action inheritance_column must be in params model_name ||= params[:record]&.delete(column) # in create action must be inside record key model_name = model_name.camelize if model_name model_name ||= active_scaffold_config.model.name {column.to_sym => model_name} if model_name end
# File lib/active_scaffold/actions/core.rb, line 120 def subform_child_association params[:child_association].presence || @scope&.split(']')&.first&.sub(/^\[/, '').presence end
# File lib/active_scaffold/actions/core.rb, line 245 def successful=(val) @successful = val ? true : false end
Success is the existence of one or more model objects. Most actions circumvent this method by setting @success directly.
# File lib/active_scaffold/actions/core.rb, line 237 def successful? if @successful.nil? true else @successful end end
# File lib/active_scaffold/actions/core.rb, line 350 def touch_device? session[:input_device_type] == 'TOUCH' end
# File lib/active_scaffold/actions/core.rb, line 104 def updated_record_with_column(column, value, scope) record = params[:id] ? copy_attributes(find_if_allowed(params[:id], :read)) : new_model apply_constraints_to_record(record) unless scope || params[:id] create_association_with_parent record, true if nested? if @form_action == :field_search && value.is_a?(Array) && column.association&.singular? # don't assign value if it's an array and column is singular association, # e.g. value came from multi-select on search form # use instance variable so it's available in the view and helpers @value = value else update_column_from_params(record, column, value, true) end record.id = params[:id] record end
# File lib/active_scaffold/actions/core.rb, line 85 def updated_record_with_form(columns, attributes, scope) if attributes && scope attributes = scope.delete('[').split(']').inject(attributes) { |h, idx| h[idx] } id = attributes[:id] else id = params[:id] end # check permissions and support overriding to_param saved_record = find_if_allowed(id, :read) if id # call update_record_from_params with new_model # in other case some associations can be saved record = new_model copy_attributes(saved_record, record) if saved_record apply_constraints_to_record(record) unless scope create_association_with_parent record, true if nested? update_record_from_params(record, columns, attributes || {}, true) end
# File lib/active_scaffold/actions/core.rb, line 437 def view_stale? objects = objects_for_etag if objects.is_a?(Array) args = {:etag => objects.to_a} args[:last_modified] = @last_modified if @last_modified elsif objects.is_a?(Hash) args = {:last_modified => @last_modified}.merge(objects) else args = objects end stale?(args) end
# File lib/active_scaffold/actions/core.rb, line 454 def virtual_columns(columns) columns.reject do |col| active_scaffold_config._columns_hash[col.to_s] || active_scaffold_config.columns[col]&.association end end
Private Instance Methods
# File lib/active_scaffold/actions/core.rb, line 487 def action_formats @action_formats ||= if respond_to? "#{action_name}_formats", true send("#{action_name}_formats") else (default_formats + active_scaffold_config.formats).uniq end end
# File lib/active_scaffold/actions/core.rb, line 496 def look_for_parent_sti_controller klass = self.class.active_scaffold_config.model loop do klass = klass.superclass controller = self.class.active_scaffold_controller_for(klass) cfg = controller.active_scaffold_config if controller.uses_active_scaffold? next unless cfg&.add_sti_create_links? return controller if cfg.sti_children.map(&:to_s).include? self.class.active_scaffold_config.model.name.underscore end rescue ActiveScaffold::ControllerNotFound => ex logger.warn "#{ex.message} looking for parent_sti of #{self.class.active_scaffold_config.model.name}" nil end
# File lib/active_scaffold/actions/core.rb, line 478 def respond_method_for(action, format) if format == :html && params[:iframe] == 'true' method_name = "#{action}_respond_on_iframe" return method_name if respond_to?(method_name, true) end method_name = "#{action}_respond_to_#{format}" method_name if respond_to?(method_name, true) end
# File lib/active_scaffold/actions/core.rb, line 466 def respond_to_action(action) return unless !conditional_get_support? || view_stale? respond_to do |type| action_formats.each do |format| type.send(format) do method_name = respond_method_for(action, format) send(method_name) if method_name end end end end