module ActiveScaffold::Constraints

Protected Instance Methods

active_scaffold_constraints() click to toggle source

Returns the current constraints

# File lib/active_scaffold/constraints.rb, line 7
def active_scaffold_constraints
  @active_scaffold_constraints ||= active_scaffold_session_storage[:constraints] || {}
end
apply_constraints_to_record(record, options = {}) click to toggle source

Applies constraints to the given record.

Searches through the known columns for association columns. If the given constraint is an association, it assumes that the constraint value is an id. It then does a association.klass.find with the value and adds the associated object to the record.

For some operations ActiveRecord will automatically update the database. That's not always ok. If it is ok (e.g. you're in a transaction), then set :allow_autosave to true.

# File lib/active_scaffold/constraints.rb, line 149
def apply_constraints_to_record(record, options = {})
  options[:allow_autosave] = false if options[:allow_autosave].nil?

  active_scaffold_constraints.each do |k, v|
    column = active_scaffold_config.columns[k]
    if column and column.association
      if column.plural_association?
        record.send("#{k}").send(:<<, column.association.klass.find(v))
      elsif column.association.options[:polymorphic]
        record.send("#{k}=", params[:parent_model].constantize.find(v))
      else # regular singular association
        record.send("#{k}=", column.association.klass.find(v))

        # setting the belongs_to side of a has_one isn't safe. if the has_one was already
        # specified, rails won't automatically clear out the previous associated record.
        #
        # note that we can't take the extra step to correct this unless we're permitted to
        # run operations where activerecord auto-saves the object.
        reverse = column.association.klass.reflect_on_association(column.association.reverse)
        if reverse.macro == :has_one and options[:allow_autosave]
          record.send(k).send("#{column.association.reverse}=", record)
        end
      end
    else
      record.send("#{k}=", v)
    end
  end
end
condition_from_association_constraint(association, value) click to toggle source

We do NOT want to use .search_sql. If anything, search_sql will refer to a human-searchable value on the associated record.

# File lib/active_scaffold/constraints.rb, line 92
def condition_from_association_constraint(association, value)
  # when the reverse association is a :belongs_to, the id for the associated object only exists as
  # the primary_key on the other table. so for :has_one and :has_many (when the reverse is :belongs_to),
  # we have to use the other model's primary_key.
  #
  # please see the relevant tests for concrete examples.
  field = if [:has_one, :has_many].include?(association.macro)
    association.klass.primary_key
  elsif [:has_and_belongs_to_many].include?(association.macro)
    association.association_foreign_key
  else
    association.options[:foreign_key] || association.name.to_s.foreign_key
  end

  table = case association.macro
    when :has_and_belongs_to_many
    association.options[:join_table]

    when :belongs_to
    active_scaffold_config.model.table_name

    else
    association.table_name
  end

  if association.options[:primary_key]
    value = association.klass.find(value).send(association.options[:primary_key])
  end

  condition = constraint_condition_for("#{table}.#{field}", value)
  if association.options[:polymorphic]
    begin
      parent_scaffold = "#{session_info[:parent_scaffold].to_s.camelize}Controller".constantize
      condition = merge_conditions(
        condition,
        constraint_condition_for("#{table}.#{association.name}_type", parent_scaffold.active_scaffold_config.model_id.to_s)
      )
    rescue ActiveScaffold::ControllerNotFound
      nil
    end
  end

  condition
end
conditions_from_constraints() click to toggle source

Returns search conditions based on the current scaffold constraints.

Supports constraints based on either a column name (in which case it checks for an association or just uses the search_sql) or a database field name.

All of this work is primarily to support nested scaffolds in a manner generally useful for other embedded scaffolds.

# File lib/active_scaffold/constraints.rb, line 46
def conditions_from_constraints
  conditions = nil
  active_scaffold_constraints.each do |k, v|
    column = active_scaffold_config.columns[k]
    constraint_condition = if column
      # Assume this is a multi-level association constraint.
      # example:
      #   data model: Park -> Den -> Bear
      #   constraint: :den => {:park => 5}
      if v.is_a? Hash
        far_association = column.association.klass.reflect_on_association(v.keys.first)
        field = far_association.klass.primary_key
        table = far_association.table_name

        active_scaffold_includes.concat([{k => v.keys.first}]) # e.g. {:den => :park}
        constraint_condition_for("#{table}.#{field}", v.values.first)

      # association column constraint
      elsif column.association
        if column.association.macro == :has_and_belongs_to_many
          active_scaffold_habtm_joins.concat column.includes
        else
          active_scaffold_includes.concat column.includes
        end
        condition_from_association_constraint(column.association, v)

      # regular column constraints
      elsif column.searchable?
        active_scaffold_includes.concat column.includes
        constraint_condition_for(column.search_sql, v)
      end
    # unknown-to-activescaffold-but-real-database-column constraint
    elsif active_scaffold_config.model.column_names.include? k.to_s
      constraint_condition_for(k.to_s, v)
    else
      raise ActiveScaffold::MalformedConstraint, constraint_error(active_scaffold_config.model, k), caller
    end

    conditions = merge_conditions(conditions, constraint_condition)
  end

  conditions
end
constraint_error(klass, column_name) click to toggle source
# File lib/active_scaffold/constraints.rb, line 137
def constraint_error(klass, column_name)
  "Malformed constraint `#{klass}##{column_name}'. If it's a legitimate column, and you are using a nested scaffold, please specify or double-check the reverse association name."
end
register_constraints_with_action_columns(association_constrained_fields = [], exclude_actions = []) click to toggle source

For each enabled action, adds the constrained columns to the ActionColumns object (if it exists). This lets the ActionColumns object skip constrained columns.

If the constraint value is a Hash, then we assume the constraint is a multi-level association constraint (the reverse of a has_many :through) and we do NOT register the constraint column.

# File lib/active_scaffold/constraints.rb, line 25
def register_constraints_with_action_columns(association_constrained_fields = [], exclude_actions = [])
  constrained_fields = active_scaffold_constraints.reject{|k, v| v.is_a? Hash}.keys.collect{|k| k.to_sym}
  constrained_fields = constrained_fields | association_constrained_fields
  if self.class.uses_active_scaffold?
    # we actually want to do this whether constrained_fields exist or not, so that we can reset the array when they don't
    active_scaffold_config.actions.each do |action_name|
      next if exclude_actions.include?(action_name)
      action = active_scaffold_config.send(action_name)
      next unless action.respond_to? :columns
      action.columns.constraint_columns = constrained_fields
    end
  end
end
set_active_scaffold_constraints() click to toggle source
# File lib/active_scaffold/constraints.rb, line 11
def set_active_scaffold_constraints
  associations_by_params = {}
  active_scaffold_config.model.reflect_on_all_associations.each do |association|
    associations_by_params[association.klass.name.foreign_key] = association.name unless association.options[:polymorphic]
  end
  params.each do |key, value|
    active_scaffold_constraints[associations_by_params[key]] = value if associations_by_params.include? key
  end
end

Private Instance Methods

constraint_condition_for(sql, value) click to toggle source
# File lib/active_scaffold/constraints.rb, line 180
def constraint_condition_for(sql, value)
  value.nil? ? "#{sql} IS NULL" : ["#{sql} = ?", value]
end