module ActiveRecord::AttributeAssignment
def _assign_attribute(k, v)
def _assign_attribute(k, v) public_send("#{k}=", v) rescue NoMethodError if respond_to?("#{k}=") raise else raise UnknownAttributeError.new(self, k) end end
def assign_attributes(new_attributes)
of this method is +false+ an ActiveModel::ForbiddenAttributesError
If the passed hash responds to permitted? method and the return value
keys matching the attribute names (which again matches the column names).
Allows you to set all the attributes by passing in a hash of attributes with
def assign_attributes(new_attributes) if !new_attributes.respond_to?(:stringify_keys) raise ArgumentError, "When assigning attributes, you must pass a hash as an argument." end return if new_attributes.blank? attributes = new_attributes.stringify_keys multi_parameter_attributes = [] nested_parameter_attributes = [] attributes = sanitize_for_mass_assignment(attributes) attributes.each do |k, v| if k.include?("(") multi_parameter_attributes << [ k, v ] elsif v.is_a?(Hash) nested_parameter_attributes << [ k, v ] else _assign_attribute(k, v) end end assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty? assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty? end
def assign_multiparameter_attributes(pairs)
parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum and
written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
by calling new on the column type or aggregation type (through composed_of) object with these parameters.
Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
def assign_multiparameter_attributes(pairs) execute_callstack_for_multiparameter_attributes( extract_callstack_for_multiparameter_attributes(pairs) ) end
def assign_nested_parameter_attributes(pairs)
def assign_nested_parameter_attributes(pairs) pairs.each { |k, v| _assign_attribute(k, v) } end
def execute_callstack_for_multiparameter_attributes(callstack)
def execute_callstack_for_multiparameter_attributes(callstack) errors = [] callstack.each do |name, values_with_empty_parameters| begin send("#{name}=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value) 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 { |ex| ex.message }.join(",") raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]" end end
def extract_callstack_for_multiparameter_attributes(pairs)
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
def find_parameter_position(multiparameter_name)
def find_parameter_position(multiparameter_name) multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i end
def type_cast_attribute_value(multiparameter_name, value)
def type_cast_attribute_value(multiparameter_name, value) multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value end