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 new object.

    # File lib/sequel/plugins/nested_attributes.rb
192 def nested_attributes_create(meta, attributes)
193   obj = nested_attributes_new(meta, attributes)
194   reflection = meta[:reflection]
195   delay_validate_associated_object(reflection, obj)
196   if reflection.returns_array?
197     public_send(reflection[:name]) << obj
198     obj.skip_validation_on_next_save!
199     after_save_hook{public_send(reflection[:add_method], obj)}
200   else
201     associations[reflection[:name]] = obj
202 
203     # Because we are modifying the associations cache manually before the
204     # setter is called, we still want to run the setter code even though
205     # the cached value will be the same as the given value.
206     @set_associated_object_if_same = true
207 
208     # Don't need to validate the object twice if :validate association option is not false
209     # and don't want to validate it at all if it is false.
210     if reflection[:type] == :many_to_one 
211       before_save_hook{public_send(reflection[:setter_method], obj.save(:validate=>false))}
212     else
213       after_save_hook do
214         obj.skip_validation_on_next_save!
215         public_send(reflection[:setter_method], obj)
216       end
217     end
218   end
219   add_reciprocal_object(reflection, obj)
220   obj
221 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
227 def nested_attributes_list_setter(meta, attributes_list)
228   attributes_list = attributes_list.sort.map{|k,v| v} if attributes_list.is_a?(Hash)
229   if (limit = meta[:limit]) && attributes_list.length > limit
230     raise(Error, "number of nested attributes (#{attributes_list.length}) exceeds the limit (#{limit})")
231   end
232   attributes_list.each{|a| nested_attributes_setter(meta, a)}
233 end
nested_attributes_new(meta, attributes) click to toggle source

Returns a new object of the associated class with the given attributes set.

    # File lib/sequel/plugins/nested_attributes.rb
258 def nested_attributes_new(meta, attributes)
259   obj = meta[:reflection].associated_class.new
260   nested_attributes_set_attributes(meta, obj, attributes)
261 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
239 def nested_attributes_remove(meta, obj, opts=OPTS)
240   reflection = meta[:reflection]
241   if !opts[:destroy] || reflection.remove_before_destroy?
242     before_save_hook do
243       if reflection.returns_array?
244         public_send(reflection[:remove_method], obj)
245       else
246         public_send(reflection[:setter_method], nil)
247       end
248     end
249   end
250   after_save_hook{obj.destroy} if opts[:destroy]
251   if reflection.returns_array?
252     associations[reflection[:name]].delete(obj)
253   end
254   obj
255 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
265 def nested_attributes_set_attributes(meta, obj, attributes)
266   if fields = meta[:fields]
267     fields = fields.call(obj) if fields.respond_to?(:call)
268     obj.set_fields(attributes, fields, :missing=>:skip)
269   else
270     obj.set(attributes)
271   end
272 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
284 def nested_attributes_setter(meta, attributes)
285   if a = meta[:transform]
286     attributes = a.call(self, attributes)
287   end
288   return if (b = meta[:reject_if]) && b.call(attributes)
289   modified!
290   reflection = meta[:reflection]
291   klass = reflection.associated_class
292   sym_keys = Array(klass.primary_key)
293   str_keys = sym_keys.map(&:to_s)
294   if (pk = attributes.values_at(*sym_keys)).all? || (pk = attributes.values_at(*str_keys)).all?
295     pk = pk.map(&:to_s)
296     obj = Array(public_send(reflection[:name])).find{|x| Array(x.pk).map(&:to_s) == pk}
297   end
298   if obj
299     unless (require_modification = meta[:require_modification]).nil?
300       obj.require_modification = require_modification
301     end
302     attributes = attributes.dup.delete_if{|k,v| str_keys.include? k.to_s}
303     if meta[:destroy] && klass.db.send(:typecast_value_boolean, attributes.delete(:_delete) || attributes.delete('_delete'))
304       nested_attributes_remove(meta, obj, :destroy=>true)
305     elsif meta[:remove] && klass.db.send(:typecast_value_boolean, attributes.delete(:_remove) || attributes.delete('_remove'))
306       nested_attributes_remove(meta, obj)
307     else
308       nested_attributes_update(meta, obj, attributes)
309     end
310   elsif pk.all? && meta[:unmatched_pk] != :create
311     if meta[:unmatched_pk] == :raise
312       raise(Error, "no matching associated object with given primary key (association: #{reflection[:name]}, pk: #{pk})")
313     end
314   else
315     nested_attributes_create(meta, attributes)
316   end
317 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
322 def nested_attributes_update(meta, obj, attributes)
323   nested_attributes_update_attributes(meta, obj, attributes)
324   delay_validate_associated_object(meta[:reflection], obj)
325   # Don't need to validate the object twice if :validate association option is not false
326   # and don't want to validate it at all if it is false.
327   after_save_hook{obj.save_changes(:validate=>false)}
328   obj
329 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
332 def nested_attributes_update_attributes(meta, obj, attributes)
333   nested_attributes_check_key_modifications(meta, obj) do
334     nested_attributes_set_attributes(meta, obj, attributes)
335   end
336 end