module Sequel::Plugins::NestedAttributes::InstanceMethods
Public Instance Methods
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
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
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
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
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
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
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
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
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
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