module Sequel::Plugins::NestedAttributes::InstanceMethods

Public Instance Methods

set_nested_attributes(assoc, obj, opts=OPTS) click to toggle source

Set the nested attributes for the given association. obj should be an enumerable of multiple objects for plural associations. The opts hash can be used to override any of the default options set by the class-level nested_attributes call.

    # File lib/sequel/plugins/nested_attributes.rb
163 def set_nested_attributes(assoc, obj, opts=OPTS)
164   raise(Error, "no association named #{assoc} for #{model.inspect}") unless ref = model.association_reflection(assoc)
165   raise(Error, "nested attributes are not enabled for association #{assoc} for #{model.inspect}") unless meta = ref[:nested_attributes]
166   return if obj.nil? && meta[:reject_nil]
167   meta = meta.merge(opts)
168   meta[:reflection] = ref
169   if ref.returns_array?
170     nested_attributes_list_setter(meta, obj)
171   else
172     nested_attributes_setter(meta, obj)
173   end
174 end

Private Instance Methods

nested_attributes_check_key_modifications(meta, obj) { || ... } click to toggle source

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
180 def nested_attributes_check_key_modifications(meta, obj)
181   reflection = meta[:reflection]
182   keys = reflection.associated_object_keys.map{|x| obj.get_column_value(x)}
183   yield
184   unless keys == reflection.associated_object_keys.map{|x| obj.get_column_value(x)}
185     raise(Error, "Modifying association dependent key(s) when updating associated objects is not allowed")
186   end
187 end
nested_attributes_create(meta, attributes) click to toggle source

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
192 def nested_attributes_create(meta, attributes)
193   reflection = meta[:reflection]
194   obj = reflection.associated_class.new
195   nested_attributes_set_attributes(meta, obj, attributes)
196   delay_validate_associated_object(reflection, obj)
197   if reflection.returns_array?
198     public_send(reflection[:name]) << obj
199     obj.skip_validation_on_next_save!
200     after_save_hook{public_send(reflection[:add_method], obj)}
201   else
202     associations[reflection[:name]] = obj
203 
204     # Because we are modifying the associations cache manually before the
205     # setter is called, we still want to run the setter code even though
206     # the cached value will be the same as the given value.
207     @set_associated_object_if_same = true
208 
209     # Don't need to validate the object twice if :validate association option is not false
210     # and don't want to validate it at all if it is false.
211     if reflection[:type] == :many_to_one 
212       before_save_hook{public_send(reflection[:setter_method], obj.save(:validate=>false))}
213     else
214       after_save_hook do
215         obj.skip_validation_on_next_save!
216         public_send(reflection[:setter_method], obj)
217       end
218     end
219   end
220   add_reciprocal_object(reflection, obj)
221   obj
222 end
nested_attributes_list_setter(meta, attributes_list) click to toggle source

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
228 def nested_attributes_list_setter(meta, attributes_list)
229   attributes_list = attributes_list.sort.map{|k,v| v} if attributes_list.is_a?(Hash)
230   if (limit = meta[:limit]) && attributes_list.length > limit
231     raise(Error, "number of nested attributes (#{attributes_list.length}) exceeds the limit (#{limit})")
232   end
233   attributes_list.each{|a| nested_attributes_setter(meta, a)}
234 end
nested_attributes_remove(meta, obj, opts=OPTS) click to toggle source

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
240 def nested_attributes_remove(meta, obj, opts=OPTS)
241   reflection = meta[:reflection]
242   if !opts[:destroy] || reflection.remove_before_destroy?
243     before_save_hook do
244       if reflection.returns_array?
245         public_send(reflection[:remove_method], obj)
246       else
247         public_send(reflection[:setter_method], nil)
248       end
249     end
250   end
251   after_save_hook{obj.destroy} if opts[:destroy]
252   if reflection.returns_array?
253     associations[reflection[:name]].delete(obj)
254   end
255   obj
256 end
nested_attributes_set_attributes(meta, obj, attributes) click to toggle source

Set the fields in the obj based on the association, only allowing specific :fields if configured.

    # File lib/sequel/plugins/nested_attributes.rb
260 def nested_attributes_set_attributes(meta, obj, attributes)
261   if fields = meta[:fields]
262     fields = fields.call(obj) if fields.respond_to?(:call)
263     obj.set_fields(attributes, fields, :missing=>:skip)
264   else
265     obj.set(attributes)
266   end
267 end
nested_attributes_setter(meta, attributes) click to toggle source

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
279 def nested_attributes_setter(meta, attributes)
280   if a = meta[:transform]
281     attributes = a.call(self, attributes)
282   end
283   return if (b = meta[:reject_if]) && b.call(attributes)
284   modified!
285   reflection = meta[:reflection]
286   klass = reflection.associated_class
287   sym_keys = Array(klass.primary_key)
288   str_keys = sym_keys.map(&:to_s)
289   if (pk = attributes.values_at(*sym_keys)).all? || (pk = attributes.values_at(*str_keys)).all?
290     pk = pk.map(&:to_s)
291     obj = Array(public_send(reflection[:name])).find{|x| Array(x.pk).map(&:to_s) == pk}
292   end
293   if obj
294     unless (require_modification = meta[:require_modification]).nil?
295       obj.require_modification = require_modification
296     end
297     attributes = attributes.dup.delete_if{|k,v| str_keys.include? k.to_s}
298     if meta[:destroy] && klass.db.send(:typecast_value_boolean, attributes.delete(:_delete) || attributes.delete('_delete'))
299       nested_attributes_remove(meta, obj, :destroy=>true)
300     elsif meta[:remove] && klass.db.send(:typecast_value_boolean, attributes.delete(:_remove) || attributes.delete('_remove'))
301       nested_attributes_remove(meta, obj)
302     else
303       nested_attributes_update(meta, obj, attributes)
304     end
305   elsif pk.all? && meta[:unmatched_pk] != :create
306     if meta[:unmatched_pk] == :raise
307       raise(Error, "no matching associated object with given primary key (association: #{reflection[:name]}, pk: #{pk})")
308     end
309   else
310     nested_attributes_create(meta, attributes)
311   end
312 end
nested_attributes_update(meta, obj, attributes) click to toggle source

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
317 def nested_attributes_update(meta, obj, attributes)
318   nested_attributes_update_attributes(meta, obj, attributes)
319   delay_validate_associated_object(meta[:reflection], obj)
320   # Don't need to validate the object twice if :validate association option is not false
321   # and don't want to validate it at all if it is false.
322   after_save_hook{obj.save_changes(:validate=>false)}
323   obj
324 end
nested_attributes_update_attributes(meta, obj, attributes) click to toggle source

Update the attributes for the given object related to the current object through the association.

    # File lib/sequel/plugins/nested_attributes.rb
327 def nested_attributes_update_attributes(meta, obj, attributes)
328   nested_attributes_check_key_modifications(meta, obj) do
329     nested_attributes_set_attributes(meta, obj, attributes)
330   end
331 end