module Sequel::Plugins::NestedAttributes::InstanceMethods
Private Instance Methods
Check that the keys related to the association are not modified inside the block. Does not use an ensure block, so callers should be careful.
# File lib/sequel/plugins/nested_attributes.rb, line 146 def nested_attributes_check_key_modifications(reflection, obj) keys = reflection.associated_object_keys.map{|x| obj.send(x)} yield unless keys == reflection.associated_object_keys.map{|x| obj.send(x)} raise(Error, "Modifying association dependent key(s) when updating associated objects is not allowed") end end
Create a new associated object with the given attributes, validate it when the parent is validated, and save it when the object is saved. Returns the object created.
# File lib/sequel/plugins/nested_attributes.rb, line 157 def nested_attributes_create(reflection, attributes) obj = reflection.associated_class.new nested_attributes_set_attributes(reflection, obj, attributes) after_validation_hook{validate_associated_object(reflection, obj)} if reflection.returns_array? send(reflection[:name]) << obj after_save_hook{send(reflection.add_method, obj)} else associations[reflection[:name]] = obj # Because we are modifying the associations cache manually before the # setter is called, we still want to run the setter code even though # the cached value will be the same as the given value. @set_associated_object_if_same = true # Don't need to validate the object twice if :validate association option is not false # and don't want to validate it at all if it is false. if reflection[:type] == :many_to_one before_save_hook{send(reflection.setter_method, obj.save(:validate=>false))} else after_save_hook{send(reflection.setter_method, obj)} end end obj end
Take an array or hash of attribute hashes and set each one individually. If a hash is provided it, sort it by key and then use the values. If there is a limit on the nested attributes for this association, make sure the length of the attributes_list is not greater than the limit.
# File lib/sequel/plugins/nested_attributes.rb, line 187 def nested_attributes_list_setter(reflection, attributes_list) attributes_list = attributes_list.sort_by{|x| x.to_s}.map{|k,v| v} if attributes_list.is_a?(Hash) if (limit = reflection[:nested_attributes][:limit]) && attributes_list.length > limit raise(Error, "number of nested attributes (#{attributes_list.length}) exceeds the limit (#{limit})") end attributes_list.each{|a| nested_attributes_setter(reflection, a)} end
Remove the given associated object from the current object. If the :destroy option is given, destroy the object after disassociating it (unless destroying the object would automatically disassociate it). Returns the object removed.
# File lib/sequel/plugins/nested_attributes.rb, line 199 def nested_attributes_remove(reflection, obj, opts={}) if !opts[:destroy] || reflection.remove_before_destroy? before_save_hook do if reflection.returns_array? send(reflection.remove_method, obj) else send(reflection.setter_method, nil) end end end after_save_hook{obj.destroy} if opts[:destroy] obj end
Set the fields in the obj based on the association, only allowing specific :fields if configured.
# File lib/sequel/plugins/nested_attributes.rb, line 215 def nested_attributes_set_attributes(reflection, obj, attributes) if fields = reflection[:nested_attributes][:fields] fields = fields.call(obj) if fields.respond_to?(:call) obj.set_only(attributes, fields) else obj.set(attributes) end end
Modify the associated object based on the contents of the attributes hash:
-
If a :transform block was given to nested_attributes, use it to modify the attribute hash.
-
If a block was given to nested_attributes, call it with the attributes and return immediately if the block returns true.
-
If a primary key exists in the attributes hash and it matches an associated object:
** If _delete is a key in the hash and the :destroy option is used, destroy the matching associated object. ** If _remove is a key in the hash and the :remove option is used, disassociated the matching associated object. ** Otherwise, update the matching associated object with the contents of the hash.
-
If a primary key exists in the attributes hash but it does not match an associated object, either raise an error, create a new object or ignore the hash, depending on the :unmatched_pk option.
-
If no primary key exists in the attributes hash, create a new object.
# File lib/sequel/plugins/nested_attributes.rb, line 234 def nested_attributes_setter(reflection, attributes) if a = reflection[:nested_attributes][:transform] attributes = a.call(self, attributes) end return if (b = reflection[:nested_attributes][:reject_if]) && b.call(attributes) modified! klass = reflection.associated_class sym_keys = Array(klass.primary_key) str_keys = sym_keys.map{|k| k.to_s} if (pk = attributes.values_at(*sym_keys)).all? || (pk = attributes.values_at(*str_keys)).all? pk = pk.map{|k| k.to_s} obj = Array(send(reflection[:name])).find{|x| Array(x.pk).map{|k| k.to_s} == pk} end if obj attributes = attributes.dup.delete_if{|k,v| str_keys.include? k.to_s} if reflection[:nested_attributes][:destroy] && klass.db.send(:typecast_value_boolean, attributes.delete(:_delete) || attributes.delete('_delete')) nested_attributes_remove(reflection, obj, :destroy=>true) elsif reflection[:nested_attributes][:remove] && klass.db.send(:typecast_value_boolean, attributes.delete(:_remove) || attributes.delete('_remove')) nested_attributes_remove(reflection, obj) else nested_attributes_update(reflection, obj, attributes) end elsif pk.all? && reflection[:nested_attributes][:unmatched_pk] != :create if reflection[:nested_attributes][:unmatched_pk] == :raise raise(Error, "no matching associated object with given primary key (association: #{reflection[:name]}, pk: #{pk})") end else nested_attributes_create(reflection, attributes) end end
Update the given object with the attributes, validating it when the parent object is validated and saving it when the parent is saved. Returns the object updated.
# File lib/sequel/plugins/nested_attributes.rb, line 268 def nested_attributes_update(reflection, obj, attributes) nested_attributes_update_attributes(reflection, obj, attributes) after_validation_hook{validate_associated_object(reflection, obj)} # Don't need to validate the object twice if :validate association option is not false # and don't want to validate it at all if it is false. after_save_hook{obj.save_changes(:validate=>false)} obj end
Update the attributes for the given object related to the current object through the association.
# File lib/sequel/plugins/nested_attributes.rb, line 278 def nested_attributes_update_attributes(reflection, obj, attributes) nested_attributes_check_key_modifications(reflection, obj) do nested_attributes_set_attributes(reflection, obj, attributes) end end
Validate the given associated object, adding any validation error messages from the given object to the parent object.
# File lib/sequel/plugins/nested_attributes.rb, line 286 def validate_associated_object(reflection, obj) return if reflection[:validate] == false association = reflection[:name] obj.errors.full_messages.each{|m| errors.add(association, m)} unless obj.valid? end