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 2306 def associations 2307 @associations ||= {} 2308 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 2313 def freeze 2314 associations 2315 super 2316 associations.freeze 2317 self 2318 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 2323 def _apply_association_options(opts, ds) 2324 unless ds.kind_of?(AssociationDatasetMethods) 2325 ds = opts.apply_dataset_changes(ds) 2326 end 2327 ds = ds.clone(:model_object => self) 2328 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2329 # block method is private 2330 ds = send(opts[:block_method], ds) if opts[:block_method] 2331 ds 2332 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2335 def _associated_dataset(opts, dynamic_opts) 2336 ds = public_send(opts.dataset_method) 2337 if callback = dynamic_opts[:callback] 2338 ds = callback.call(ds) 2339 end 2340 ds 2341 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2344 def _associated_object_loader(opts, dynamic_opts) 2345 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2346 loader 2347 end 2348 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2351 def _dataset(opts) 2352 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2353 ds = if opts[:dataset_opt_arity] == 1 2354 # dataset_opt_method is private 2355 send(opts[:dataset_opt_method], opts) 2356 else 2357 send(opts[:dataset_opt_method]) 2358 end 2359 _apply_association_options(opts, ds) 2360 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2363 def _join_table_dataset(opts) 2364 ds = model.db.from(opts.join_table_source) 2365 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2366 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 2370 def _load_associated_object(opts, dynamic_opts) 2371 _load_associated_object_array(opts, dynamic_opts).first 2372 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2381 def _load_associated_object_array(opts, dynamic_opts) 2382 if loader = _associated_object_loader(opts, dynamic_opts) 2383 loader.all(*opts.predicate_key_values(self)) 2384 else 2385 _associated_dataset(opts, dynamic_opts).all 2386 end 2387 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2375 def _load_associated_object_via_primary_key(opts) 2376 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2377 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 2391 def _load_associated_objects(opts, dynamic_opts=OPTS) 2392 if opts.can_have_associated_objects?(self) 2393 if opts.returns_array? 2394 _load_associated_object_array(opts, dynamic_opts) 2395 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2396 _load_associated_object_via_primary_key(opts) 2397 else 2398 _load_associated_object(opts, dynamic_opts) 2399 end 2400 elsif opts.returns_array? 2401 [] 2402 end 2403 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2406 def _refresh_set_values(hash) 2407 @associations.clear if @associations 2408 super 2409 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2639 def _set_associated_object(opts, o) 2640 a = associations[opts[:name]] 2641 reciprocal = opts.reciprocal 2642 if set_associated_object_if_same? 2643 if reciprocal 2644 remove_reciprocal = a && (a != o || a.associations[reciprocal] != self) 2645 add_reciprocal = o && o.associations[reciprocal] != self 2646 end 2647 else 2648 return if a && a == o 2649 if reciprocal 2650 remove_reciprocal = a 2651 add_reciprocal = o 2652 end 2653 end 2654 run_association_callbacks(opts, :before_set, o) 2655 remove_reciprocal_object(opts, a) if remove_reciprocal 2656 # Allow calling private _setter method 2657 send(opts[:_setter_method], o) 2658 associations[opts[:name]] = o 2659 add_reciprocal_object(opts, o) if add_reciprocal 2660 run_association_callbacks(opts, :after_set, o) 2661 o 2662 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2412 def add_associated_object(opts, o, *args) 2413 o = make_add_associated_object(opts, o) 2414 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2415 ensure_associated_primary_key(opts, o, *args) 2416 return if run_association_callbacks(opts, :before_add, o) == false 2417 # Allow calling private _add method 2418 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2419 if array = associations[opts[:name]] and !array.include?(o) 2420 array.push(o) 2421 end 2422 add_reciprocal_object(opts, o) 2423 run_association_callbacks(opts, :after_add, o) 2424 o 2425 end
Add/Set the current object to/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2428 def add_reciprocal_object(opts, o) 2429 return if o.frozen? 2430 return unless reciprocal = opts.reciprocal 2431 if opts.reciprocal_array? 2432 if array = o.associations[reciprocal] and !array.include?(self) 2433 array.push(self) 2434 end 2435 else 2436 o.associations[reciprocal] = self 2437 end 2438 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 2442 def array_uniq!(a) 2443 a.uniq! 2444 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2448 def change_column_value(column, value) 2449 if assocs = model.autoreloading_associations[column] 2450 vals = @values 2451 if new? 2452 # Do deeper checking for new objects, so that associations are 2453 # not deleted when values do not change. This code is run at 2454 # a higher level for existing objects. 2455 if value == (c = vals[column]) && value.class == c.class 2456 # If the value is the same, there is no reason to delete 2457 # the related associations, so exit early in that case. 2458 return super 2459 end 2460 2461 only_delete_nil = c.nil? 2462 elsif vals[column].nil? 2463 only_delete_nil = true 2464 end 2465 2466 if only_delete_nil 2467 # If the current foreign key value is nil, but the association 2468 # is already present in the cache, it was probably added to the 2469 # cache for a reason, and we do not want to delete it in that case. 2470 # However, we still want to delete associations with nil values 2471 # to remove the cached false negative. 2472 assocs.each{|a| associations.delete(a) if associations[a].nil?} 2473 else 2474 assocs.each{|a| associations.delete(a)} 2475 end 2476 end 2477 super 2478 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 2483 def ensure_associated_primary_key(opts, o, *args) 2484 if opts.need_associated_primary_key? 2485 o.save(:validate=>opts[:validate]) if o.new? 2486 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2487 end 2488 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2491 def initialize_copy(other) 2492 super 2493 @associations = Hash[@associations] if @associations 2494 self 2495 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2508 def load_associated_objects(opts, dynamic_opts, &block) 2509 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2510 name = opts[:name] 2511 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2512 associations[name] 2513 else 2514 objs = _load_associated_objects(opts, dynamic_opts) 2515 if opts.set_reciprocal_to_self? 2516 if opts.returns_array? 2517 objs.each{|o| add_reciprocal_object(opts, o)} 2518 elsif objs 2519 add_reciprocal_object(opts, objs) 2520 end 2521 end 2522 2523 # If the current object is frozen, you can't update the associations 2524 # cache. This can cause issues for after_load procs that expect 2525 # the objects to be already cached in the associations, but 2526 # unfortunately that case cannot be handled. 2527 associations[name] = objs unless frozen? 2528 run_association_callbacks(opts, :after_load, objs) 2529 frozen? ? objs : associations[name] 2530 end 2531 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 2498 def load_association_objects_options(dynamic_opts, &block) 2499 if block 2500 dynamic_opts = Hash[dynamic_opts] 2501 dynamic_opts[:callback] = block 2502 end 2503 2504 dynamic_opts 2505 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2534 def load_with_primary_key_lookup?(opts, dynamic_opts) 2535 opts[:type] == :many_to_one && 2536 !dynamic_opts[:callback] && 2537 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2538 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 2544 def make_add_associated_object(opts, o) 2545 klass = opts.associated_class 2546 2547 case o 2548 when Hash 2549 klass.new(o) 2550 when Integer, String, Array 2551 klass.with_pk!(o) 2552 when klass 2553 o 2554 else 2555 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2556 end 2557 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2560 def remove_all_associated_objects(opts, *args) 2561 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2562 # Allow calling private _remove_all method 2563 send(opts[:_remove_all_method], *args) 2564 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2565 associations[opts[:name]] = [] 2566 ret 2567 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2570 def remove_associated_object(opts, o, *args) 2571 klass = opts.associated_class 2572 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2573 o = remove_check_existing_object_from_pk(opts, o, *args) 2574 elsif !o.is_a?(klass) 2575 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2576 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2577 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2578 end 2579 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2580 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2581 return if run_association_callbacks(opts, :before_remove, o) == false 2582 # Allow calling private _remove method 2583 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2584 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2585 remove_reciprocal_object(opts, o) 2586 run_association_callbacks(opts, :after_remove, o) 2587 o 2588 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 2593 def remove_check_existing_object_from_pk(opts, o, *args) 2594 key = o 2595 pkh = opts.associated_class.qualified_primary_key_hash(key) 2596 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2597 o 2598 end
Remove/unset the current object from/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2601 def remove_reciprocal_object(opts, o) 2602 return unless reciprocal = opts.reciprocal 2603 if opts.reciprocal_array? 2604 if array = o.associations[reciprocal] 2605 array.delete_if{|x| self === x} 2606 end 2607 else 2608 o.associations[reciprocal] = nil 2609 end 2610 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2613 def run_association_callbacks(reflection, callback_type, object) 2614 return unless cbs = reflection[callback_type] 2615 2616 begin 2617 cbs.each do |cb| 2618 case cb 2619 when Symbol 2620 # Allow calling private methods in association callbacks 2621 send(cb, object) 2622 when Proc 2623 cb.call(self, object) 2624 else 2625 raise Error, "callbacks should either be Procs or Symbols" 2626 end 2627 end 2628 rescue HookFailed 2629 # The reason we automatically set raise_error for singular associations is that 2630 # assignment in ruby always returns the argument instead of the result of the 2631 # method, so we can't return nil to signal that the association callback prevented 2632 # the modification 2633 return false unless raise_on_save_failure || !reflection.returns_array? 2634 raise 2635 end 2636 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2672 def set_associated_object(opts, o) 2673 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2674 _set_associated_object(opts, o) 2675 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 2667 def set_associated_object_if_same? 2668 @set_associated_object_if_same 2669 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2678 def set_one_through_one_associated_object(opts, o) 2679 raise(Error, "object #{inspect} does not have a primary key") unless pk 2680 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2681 _set_associated_object(opts, o) 2682 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2685 def set_one_to_one_associated_object(opts, o) 2686 raise(Error, "object #{inspect} does not have a primary key") unless pk 2687 _set_associated_object(opts, o) 2688 end