module Sequel::Model::Associations::InstanceMethods
Instance methods used to implement the associations support.
Public Instance Methods
The currently cached associations. A hash with the keys being the association name symbols and the values being the associated object or nil (many_to_one), or the array of associated objects (*_to_many).
# File lib/sequel/model/associations.rb 2262 def associations 2263 @associations ||= {} 2264 end
Freeze the associations cache when freezing the object. Note that retrieving associations after freezing will still work in most cases, but the associations will not be cached in the association cache.
# File lib/sequel/model/associations.rb 2269 def freeze 2270 associations 2271 super 2272 associations.freeze 2273 self 2274 end
Private Instance Methods
Apply the association options such as :order and :limit to the given dataset, returning a modified dataset.
# File lib/sequel/model/associations.rb 2279 def _apply_association_options(opts, ds) 2280 unless ds.kind_of?(AssociationDatasetMethods) 2281 ds = opts.apply_dataset_changes(ds) 2282 end 2283 ds = ds.clone(:model_object => self) 2284 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2285 ds = instance_exec(ds, &opts[:block]) if opts[:block] 2286 ds 2287 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2290 def _associated_dataset(opts, dynamic_opts) 2291 ds = public_send(opts.dataset_method) 2292 if callback = dynamic_opts[:callback] 2293 ds = callback.call(ds) 2294 end 2295 ds 2296 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2299 def _associated_object_loader(opts, dynamic_opts) 2300 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2301 loader 2302 end 2303 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2306 def _dataset(opts) 2307 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2308 ds = if opts[:dataset].arity == 1 2309 instance_exec(opts, &opts[:dataset]) 2310 else 2311 instance_exec(&opts[:dataset]) 2312 end 2313 _apply_association_options(opts, ds) 2314 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2317 def _join_table_dataset(opts) 2318 ds = model.db.from(opts.join_table_source) 2319 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2320 end
Return the associated single object for the given association reflection and dynamic options (or nil if no associated object).
# File lib/sequel/model/associations.rb 2324 def _load_associated_object(opts, dynamic_opts) 2325 _load_associated_object_array(opts, dynamic_opts).first 2326 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2335 def _load_associated_object_array(opts, dynamic_opts) 2336 if loader = _associated_object_loader(opts, dynamic_opts) 2337 loader.all(*opts.predicate_key_values(self)) 2338 else 2339 _associated_dataset(opts, dynamic_opts).all 2340 end 2341 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2329 def _load_associated_object_via_primary_key(opts) 2330 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2331 end
Return the associated objects from the dataset, without association callbacks, reciprocals, and caching. Still apply the dynamic callback if present.
# File lib/sequel/model/associations.rb 2345 def _load_associated_objects(opts, dynamic_opts=OPTS) 2346 if opts.can_have_associated_objects?(self) 2347 if opts.returns_array? 2348 _load_associated_object_array(opts, dynamic_opts) 2349 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2350 _load_associated_object_via_primary_key(opts) 2351 else 2352 _load_associated_object(opts, dynamic_opts) 2353 end 2354 elsif opts.returns_array? 2355 [] 2356 end 2357 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2360 def _refresh_set_values(hash) 2361 @associations.clear if @associations 2362 super 2363 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2568 def _set_associated_object(opts, o) 2569 a = associations[opts[:name]] 2570 return if a && a == o && !set_associated_object_if_same? 2571 run_association_callbacks(opts, :before_set, o) 2572 remove_reciprocal_object(opts, a) if a 2573 # Allow calling private _setter method 2574 send(opts[:_setter_method], o) 2575 associations[opts[:name]] = o 2576 add_reciprocal_object(opts, o) if o 2577 run_association_callbacks(opts, :after_set, o) 2578 o 2579 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2366 def add_associated_object(opts, o, *args) 2367 o = make_add_associated_object(opts, o) 2368 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2369 ensure_associated_primary_key(opts, o, *args) 2370 return if run_association_callbacks(opts, :before_add, o) == false 2371 # Allow calling private _add method 2372 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2373 if array = associations[opts[:name]] and !array.include?(o) 2374 array.push(o) 2375 end 2376 add_reciprocal_object(opts, o) 2377 run_association_callbacks(opts, :after_add, o) 2378 o 2379 end
Add/Set the current object to/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2382 def add_reciprocal_object(opts, o) 2383 return if o.frozen? 2384 return unless reciprocal = opts.reciprocal 2385 if opts.reciprocal_array? 2386 if array = o.associations[reciprocal] and !array.include?(self) 2387 array.push(self) 2388 end 2389 else 2390 o.associations[reciprocal] = self 2391 end 2392 end
Call uniq! on the given array. This is used by the :uniq option, and is an actual method for memory reasons.
# File lib/sequel/model/associations.rb 2396 def array_uniq!(a) 2397 a.uniq! 2398 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2402 def change_column_value(column, value) 2403 if assocs = model.autoreloading_associations[column] 2404 assocs.each{|a| associations.delete(a)} 2405 end 2406 super 2407 end
Save the associated object if the associated object needs a primary key and the associated object is new and does not have one. Raise an error if the object still does not have a primary key
# File lib/sequel/model/associations.rb 2412 def ensure_associated_primary_key(opts, o, *args) 2413 if opts.need_associated_primary_key? 2414 o.save(:validate=>opts[:validate]) if o.new? 2415 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2416 end 2417 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2420 def initialize_copy(other) 2421 super 2422 @associations = Hash[@associations] if @associations 2423 self 2424 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2437 def load_associated_objects(opts, dynamic_opts, &block) 2438 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2439 name = opts[:name] 2440 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2441 associations[name] 2442 else 2443 objs = _load_associated_objects(opts, dynamic_opts) 2444 if opts.set_reciprocal_to_self? 2445 if opts.returns_array? 2446 objs.each{|o| add_reciprocal_object(opts, o)} 2447 elsif objs 2448 add_reciprocal_object(opts, objs) 2449 end 2450 end 2451 2452 # If the current object is frozen, you can't update the associations 2453 # cache. This can cause issues for after_load procs that expect 2454 # the objects to be already cached in the associations, but 2455 # unfortunately that case cannot be handled. 2456 associations[name] = objs unless frozen? 2457 run_association_callbacks(opts, :after_load, objs) 2458 frozen? ? objs : associations[name] 2459 end 2460 end
If a block is given, assign it as the :callback option in the hash, and return the hash.
# File lib/sequel/model/associations.rb 2427 def load_association_objects_options(dynamic_opts, &block) 2428 if block 2429 dynamic_opts = Hash[dynamic_opts] 2430 dynamic_opts[:callback] = block 2431 end 2432 2433 dynamic_opts 2434 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2463 def load_with_primary_key_lookup?(opts, dynamic_opts) 2464 opts[:type] == :many_to_one && 2465 !dynamic_opts[:callback] && 2466 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2467 end
Convert the input of the add_* association method into an associated object. For hashes, this creates a new object using the hash. For integers, strings, and arrays, assume the value specifies a primary key, and lookup an existing object with that primary key. Otherwise, if the object is not already an instance of the class, raise an exception.
# File lib/sequel/model/associations.rb 2473 def make_add_associated_object(opts, o) 2474 klass = opts.associated_class 2475 2476 case o 2477 when Hash 2478 klass.new(o) 2479 when Integer, String, Array 2480 klass.with_pk!(o) 2481 when klass 2482 o 2483 else 2484 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2485 end 2486 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2489 def remove_all_associated_objects(opts, *args) 2490 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2491 # Allow calling private _remove_all method 2492 send(opts[:_remove_all_method], *args) 2493 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2494 associations[opts[:name]] = [] 2495 ret 2496 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2499 def remove_associated_object(opts, o, *args) 2500 klass = opts.associated_class 2501 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2502 o = remove_check_existing_object_from_pk(opts, o, *args) 2503 elsif !o.is_a?(klass) 2504 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2505 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2506 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2507 end 2508 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2509 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2510 return if run_association_callbacks(opts, :before_remove, o) == false 2511 # Allow calling private _remove method 2512 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2513 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2514 remove_reciprocal_object(opts, o) 2515 run_association_callbacks(opts, :after_remove, o) 2516 o 2517 end
Check that the object from the associated table specified by the primary key is currently associated to the receiver. If it is associated, return the object, otherwise raise an error.
# File lib/sequel/model/associations.rb 2522 def remove_check_existing_object_from_pk(opts, o, *args) 2523 key = o 2524 pkh = opts.associated_class.qualified_primary_key_hash(key) 2525 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2526 o 2527 end
Remove/unset the current object from/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2530 def remove_reciprocal_object(opts, o) 2531 return unless reciprocal = opts.reciprocal 2532 if opts.reciprocal_array? 2533 if array = o.associations[reciprocal] 2534 array.delete_if{|x| self === x} 2535 end 2536 else 2537 o.associations[reciprocal] = nil 2538 end 2539 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2542 def run_association_callbacks(reflection, callback_type, object) 2543 return unless cbs = reflection[callback_type] 2544 2545 begin 2546 cbs.each do |cb| 2547 case cb 2548 when Symbol 2549 # Allow calling private methods in association callbacks 2550 send(cb, object) 2551 when Proc 2552 cb.call(self, object) 2553 else 2554 raise Error, "callbacks should either be Procs or Symbols" 2555 end 2556 end 2557 rescue HookFailed 2558 # The reason we automatically set raise_error for singular associations is that 2559 # assignment in ruby always returns the argument instead of the result of the 2560 # method, so we can't return nil to signal that the association callback prevented 2561 # the modification 2562 return false unless raise_on_save_failure || !reflection.returns_array? 2563 raise 2564 end 2565 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2589 def set_associated_object(opts, o) 2590 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2591 _set_associated_object(opts, o) 2592 end
Whether run the associated object setter code if passed the same object as the one already cached in the association. Usually not set (so nil), can be set on a per-object basis if necessary.
# File lib/sequel/model/associations.rb 2584 def set_associated_object_if_same? 2585 @set_associated_object_if_same 2586 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2595 def set_one_through_one_associated_object(opts, o) 2596 raise(Error, "object #{inspect} does not have a primary key") unless pk 2597 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2598 _set_associated_object(opts, o) 2599 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2602 def set_one_to_one_associated_object(opts, o) 2603 raise(Error, "object #{inspect} does not have a primary key") unless pk 2604 _set_associated_object(opts, o) 2605 end