module Sequel::Plugins::NestedAttributes::InstanceMethods
Public Instance Methods
Source
# 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
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.
Private Instance Methods
Source
# 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
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.
Source
# 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
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.
Source
# 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
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.
Source
# 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
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.
Source
# 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
Set the fields in the obj based on the association, only allowing specific :fields if configured.
Source
# 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
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.
Source
# 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
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.
Source
# 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
Update the attributes for the given object related to the current object through the association.