module ActiveScaffold::Actions::Core

Public Class Methods

included(base) click to toggle source
# 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

render_field() click to toggle source
# 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

accepts?(*types) click to toggle source

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
action_confirmation_respond_to_html(confirm_action = action_name.to_sym) click to toggle source
# 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
action_update_respond_on_iframe() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 412
def action_update_respond_on_iframe
  responds_to_parent { action_update_respond_to_js }
end
action_update_respond_to_html() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 416
def action_update_respond_to_html
  redirect_to :action => 'index'
end
action_update_respond_to_js() click to toggle source
# 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
action_update_respond_to_json() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 428
def action_update_respond_to_json
  response_to_api(:json, list_columns_names)
end
action_update_respond_to_xml() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 424
def action_update_respond_to_xml
  response_to_api(:xml, list_columns_names)
end
active_scaffold_embedded_params() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 323
def active_scaffold_embedded_params
  params[:embedded] || {}
end
after_render_field(record, column) click to toggle source

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
association_columns(columns) click to toggle source
# File lib/active_scaffold/actions/core.rb, line 460
def association_columns(columns)
  columns.select { |col| active_scaffold_config.columns[col]&.association }
end
authorized_for?(options = {}) click to toggle source
# File lib/active_scaffold/actions/core.rb, line 172
def authorized_for?(options = {})
  active_scaffold_config.model.authorized_for?(options)
end
beginning_of_chain() click to toggle source

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
check_input_device() click to toggle source
# 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
clear_flashes() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 176
def clear_flashes
  flash.clear if request.xhr?
end
clear_storage() { || ... } click to toggle source
# 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
conditional_get_support?() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 450
def conditional_get_support?
  request.get? && active_scaffold_config.conditional_get_support
end
conditions_from_params() click to toggle source

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
controller_params?(value) click to toggle source
# File lib/active_scaffold/actions/core.rb, line 362
def controller_params?(value)
  value.is_a?(::ActionController::Parameters)
end
copy_attributes(orig, dst = nil) click to toggle source
# 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
default_formats() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 192
def default_formats
  %i[html js json xml]
end
each_marked_record(&block) click to toggle source
# File lib/active_scaffold/actions/core.rb, line 184
def each_marked_record(&block)
  active_scaffold_config.model.as_marked.each(&block)
end
embedded?() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 43
def embedded?
  params[:eid]
end
get_row(crud_type_or_security_options = :read) click to toggle source
# 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
grouped_search?() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 51
def grouped_search?
  false
end
hover_via_click?() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 354
def hover_via_click?
  session[:hover_supported] == false
end
loading_embedded?() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 39
def loading_embedded?
  @loading_embedded ||= active_scaffold_embedded_params.delete(:loading)
end
marked_records() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 188
def marked_records
  active_scaffold_session_storage['marked_records'] ||= {}
end
nested?() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 47
def nested?
  false
end
new_model() click to toggle source
# 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
objects_for_etag() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 432
def objects_for_etag
  @last_modified ||= @record.updated_at
  [@record, ('xhr' if request.xhr?)]
end
params_hash(value) click to toggle source
# 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
params_hash?(value) click to toggle source
# File lib/active_scaffold/actions/core.rb, line 358
def params_hash?(value)
  value.is_a?(Hash) || controller_params?(value)
end
parent_controller_name() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 124
def parent_controller_name
  "#{params[:parent_controller].camelize}Controller"
end
parent_sti_controller() click to toggle source
# 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
render_field_for_inplace_editing() click to toggle source
# 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
render_field_for_update_columns() click to toggle source
# 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
response_object() click to toggle source

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
response_status() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 210
def response_status
  if successful?
    action_name == 'create' ? 201 : 200
  else
    422
  end
end
response_to_api(format, columns_names, options = {}) click to toggle source
# 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
return_to_main() click to toggle source

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
set_vary_accept_header() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 334
def set_vary_accept_header
  response.headers['Vary'] = 'Accept'
end
setup_parent(record) click to toggle source
# 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
sti_nested_build_options(klass) click to toggle source
# 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
subform_child_association() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 120
def subform_child_association
  params[:child_association].presence || @scope&.split(']')&.first&.sub(/^\[/, '').presence
end
successful=(val) click to toggle source
# File lib/active_scaffold/actions/core.rb, line 245
def successful=(val)
  @successful = val ? true : false
end
successful?() click to toggle source

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
touch_device?() click to toggle source
# File lib/active_scaffold/actions/core.rb, line 350
def touch_device?
  session[:input_device_type] == 'TOUCH'
end
updated_record_with_column(column, value, scope) click to toggle source
# 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
updated_record_with_form(columns, attributes, scope) click to toggle source
# 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
view_stale?() click to toggle source
# 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
virtual_columns(columns) click to toggle source
# 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

action_formats() click to toggle source
# 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
look_for_parent_sti_controller() click to toggle source
# 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
respond_method_for(action, format) click to toggle source
# 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
respond_to_action(action) click to toggle source
# 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