module DuckRecord::AttributeAssignment

Public Instance Methods

assign_attributes(new_attributes) click to toggle source
# File lib/duck_record/attribute_assignment.rb, line 14
def assign_attributes(new_attributes)
  unless new_attributes.respond_to?(:stringify_keys)
    raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
  end
  return if new_attributes.nil? || new_attributes.empty?

  attributes = new_attributes.stringify_keys
  _assign_attributes(sanitize_for_mass_assignment(attributes))
end
attributes=(attributes) click to toggle source

Alias for ActiveModel::AttributeAssignment#assign_attributes. See ActiveModel::AttributeAssignment.

# File lib/duck_record/attribute_assignment.rb, line 10
def attributes=(attributes)
  assign_attributes(attributes)
end

Private Instance Methods

_assign_attribute(k, v) click to toggle source
# File lib/duck_record/attribute_assignment.rb, line 110
def _assign_attribute(k, v)
  if respond_to?("#{k}=")
    public_send("#{k}=", v)
  else
    raise UnknownAttributeError.new(self, k)
  end
end
_assign_attributes(attributes) click to toggle source
# File lib/duck_record/attribute_assignment.rb, line 26
def _assign_attributes(attributes)
  multi_parameter_attributes  = {}
  nested_parameter_attributes = {}

  attributes.each do |k, v|
    if k.include?("(")
      multi_parameter_attributes[k] = attributes.delete(k)
    elsif v.is_a?(Hash)
      nested_parameter_attributes[k] = attributes.delete(k)
    end
  end

  attributes.each do |k, v|
    _assign_attribute(k, v)
  end

  unless nested_parameter_attributes.empty?
    assign_nested_parameter_attributes(nested_parameter_attributes)
  end

  unless multi_parameter_attributes.empty?
    assign_multiparameter_attributes(multi_parameter_attributes)
  end
end
assign_multiparameter_attributes(pairs) click to toggle source

Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done by calling new on the column type or aggregation type (through composed_of) object with these parameters. So having the pairs written_on(1) = “2004”, written_on(2) = “6”, written_on(3) = “24”, will instantiate written_on (a date type) with Date.new(“2004”, “6”, “24”). You can also specify a typecast character in the parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and f for Float. If all the values for a given attribute are empty, the attribute will be set to nil.

# File lib/duck_record/attribute_assignment.rb, line 62
def assign_multiparameter_attributes(pairs)
  execute_callstack_for_multiparameter_attributes(
    extract_callstack_for_multiparameter_attributes(pairs)
  )
end
assign_nested_parameter_attributes(pairs) click to toggle source

Assign any deferred nested attributes after the base attributes have been set.

# File lib/duck_record/attribute_assignment.rb, line 52
def assign_nested_parameter_attributes(pairs)
  pairs.each { |k, v| _assign_attribute(k, v) }
end
execute_callstack_for_multiparameter_attributes(callstack) click to toggle source
# File lib/duck_record/attribute_assignment.rb, line 68
def execute_callstack_for_multiparameter_attributes(callstack)
  errors = []
  callstack.each do |name, values_with_empty_parameters|
    begin
      if values_with_empty_parameters.each_value.all?(&:nil?)
        values = nil
      else
        values = values_with_empty_parameters
      end
      send("#{name}=", values)
    rescue => ex
      errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
    end
  end
  unless errors.empty?
    error_descriptions = errors.map(&:message).join(",")
    raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
  end
end
extract_callstack_for_multiparameter_attributes(pairs) click to toggle source
# File lib/duck_record/attribute_assignment.rb, line 88
def extract_callstack_for_multiparameter_attributes(pairs)
  attributes = {}

  pairs.each do |(multiparameter_name, value)|
    attribute_name = multiparameter_name.split("(").first
    attributes[attribute_name] ||= {}

    parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
    attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
  end

  attributes
end
find_parameter_position(multiparameter_name) click to toggle source
# File lib/duck_record/attribute_assignment.rb, line 106
def find_parameter_position(multiparameter_name)
  multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
end
type_cast_attribute_value(multiparameter_name, value) click to toggle source
# File lib/duck_record/attribute_assignment.rb, line 102
def type_cast_attribute_value(multiparameter_name, value)
  multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
end