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 2465 def associations 2466 @associations ||= {} 2467 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 2472 def freeze 2473 associations 2474 super 2475 associations.freeze 2476 self 2477 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 2482 def _apply_association_options(opts, ds) 2483 unless ds.kind_of?(AssociationDatasetMethods) 2484 ds = opts.apply_dataset_changes(ds) 2485 end 2486 ds = ds.clone(:model_object => self) 2487 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2488 # block method is private 2489 ds = send(opts[:block_method], ds) if opts[:block_method] 2490 ds 2491 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2494 def _associated_dataset(opts, dynamic_opts) 2495 ds = public_send(opts.dataset_method) 2496 if callback = dynamic_opts[:callback] 2497 ds = callback.call(ds) 2498 end 2499 ds 2500 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2503 def _associated_object_loader(opts, dynamic_opts) 2504 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2505 loader 2506 end 2507 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2510 def _dataset(opts) 2511 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2512 ds = if opts[:dataset_opt_arity] == 1 2513 # dataset_opt_method is private 2514 send(opts[:dataset_opt_method], opts) 2515 else 2516 send(opts[:dataset_opt_method]) 2517 end 2518 _apply_association_options(opts, ds) 2519 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2522 def _join_table_dataset(opts) 2523 ds = (opts[:join_table_db] || model.db).from(opts.join_table_source) 2524 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2525 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 2529 def _load_associated_object(opts, dynamic_opts) 2530 _load_associated_object_array(opts, dynamic_opts).first 2531 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2540 def _load_associated_object_array(opts, dynamic_opts) 2541 if loader = _associated_object_loader(opts, dynamic_opts) 2542 loader.all(*opts.predicate_key_values(self)) 2543 else 2544 ds = _associated_dataset(opts, dynamic_opts) 2545 if ds.opts[:no_results] 2546 [] 2547 else 2548 ds.all 2549 end 2550 end 2551 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2534 def _load_associated_object_via_primary_key(opts) 2535 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2536 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 2555 def _load_associated_objects(opts, dynamic_opts=OPTS) 2556 if opts.can_have_associated_objects?(self) 2557 if opts.returns_array? 2558 _load_associated_object_array(opts, dynamic_opts) 2559 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2560 _load_associated_object_via_primary_key(opts) 2561 else 2562 _load_associated_object(opts, dynamic_opts) 2563 end 2564 elsif opts.returns_array? 2565 [] 2566 end 2567 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2570 def _refresh_set_values(hash) 2571 @associations.clear if @associations 2572 super 2573 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2812 def _set_associated_object(opts, o) 2813 a = associations[opts[:name]] 2814 reciprocal = opts.reciprocal 2815 if set_associated_object_if_same? 2816 if reciprocal 2817 remove_reciprocal = a && (a != o || a.associations[reciprocal] != self) 2818 add_reciprocal = o && o.associations[reciprocal] != self 2819 end 2820 else 2821 return if a && a == o 2822 if reciprocal 2823 remove_reciprocal = a 2824 add_reciprocal = o 2825 end 2826 end 2827 run_association_callbacks(opts, :before_set, o) 2828 remove_reciprocal_object(opts, a) if remove_reciprocal 2829 # Allow calling private _setter method 2830 send(opts[:_setter_method], o) 2831 associations[opts[:name]] = o 2832 add_reciprocal_object(opts, o) if add_reciprocal 2833 run_association_callbacks(opts, :after_set, o) 2834 o 2835 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2576 def add_associated_object(opts, o, *args) 2577 o = make_add_associated_object(opts, o) 2578 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2579 ensure_associated_primary_key(opts, o, *args) 2580 return if run_association_callbacks(opts, :before_add, o) == false 2581 # Allow calling private _add method 2582 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2583 if array = associations[opts[:name]] and !array.include?(o) 2584 array.push(o) 2585 end 2586 add_reciprocal_object(opts, o) 2587 run_association_callbacks(opts, :after_add, o) 2588 o 2589 end
Add/Set the current object to/as the given object’s reciprocal association.
# File lib/sequel/model/associations.rb 2595 def add_reciprocal_object(opts, o) 2596 return if o.frozen? 2597 return unless reciprocal = opts.reciprocal 2598 if opts.reciprocal_array? 2599 if array = o.associations[reciprocal] and !array.include?(self) 2600 array.push(self) 2601 end 2602 else 2603 o.associations[reciprocal] = self 2604 end 2605 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 2609 def array_uniq!(a) 2610 a.uniq! 2611 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2615 def change_column_value(column, value) 2616 if assocs = model.autoreloading_associations[column] 2617 vals = @values 2618 if new? 2619 # Do deeper checking for new objects, so that associations are 2620 # not deleted when values do not change. This code is run at 2621 # a higher level for existing objects. 2622 if value == (c = vals[column]) && value.class == c.class 2623 # If the value is the same, there is no reason to delete 2624 # the related associations, so exit early in that case. 2625 return super 2626 end 2627 2628 only_delete_nil = c.nil? 2629 elsif vals[column].nil? 2630 only_delete_nil = true 2631 end 2632 2633 if only_delete_nil 2634 # If the current foreign key value is nil, but the association 2635 # is already present in the cache, it was probably added to the 2636 # cache for a reason, and we do not want to delete it in that case. 2637 # However, we still want to delete associations with nil values 2638 # to remove the cached false negative. 2639 assocs.each{|a| associations.delete(a) if associations[a].nil?} 2640 else 2641 assocs.each{|a| associations.delete(a)} 2642 end 2643 end 2644 super 2645 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 2650 def ensure_associated_primary_key(opts, o, *args) 2651 if opts.need_associated_primary_key? 2652 o.save(:validate=>opts[:validate]) if o.new? 2653 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2654 end 2655 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2658 def initialize_copy(other) 2659 super 2660 @associations = Hash[@associations] if @associations 2661 self 2662 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2675 def load_associated_objects(opts, dynamic_opts, &block) 2676 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2677 name = opts[:name] 2678 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2679 associations[name] 2680 else 2681 objs = _load_associated_objects(opts, dynamic_opts) 2682 if opts.set_reciprocal_to_self? 2683 if opts.returns_array? 2684 objs.each{|o| add_reciprocal_object(opts, o)} 2685 elsif objs 2686 add_reciprocal_object(opts, objs) 2687 end 2688 end 2689 2690 # If the current object is frozen, you can't update the associations 2691 # cache. This can cause issues for after_load procs that expect 2692 # the objects to be already cached in the associations, but 2693 # unfortunately that case cannot be handled. 2694 associations[name] = objs unless frozen? 2695 run_association_callbacks(opts, :after_load, objs) 2696 frozen? ? objs : associations[name] 2697 end 2698 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 2665 def load_association_objects_options(dynamic_opts, &block) 2666 if block 2667 dynamic_opts = Hash[dynamic_opts] 2668 dynamic_opts[:callback] = block 2669 end 2670 2671 dynamic_opts 2672 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2701 def load_with_primary_key_lookup?(opts, dynamic_opts) 2702 opts[:type] == :many_to_one && 2703 !dynamic_opts[:callback] && 2704 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2705 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 2711 def make_add_associated_object(opts, o) 2712 klass = opts.associated_class 2713 2714 case o 2715 when Hash 2716 klass.new(o) 2717 when Integer, String, Array 2718 klass.with_pk!(o) 2719 when klass 2720 o 2721 else 2722 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2723 end 2724 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2727 def remove_all_associated_objects(opts, *args) 2728 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2729 # Allow calling private _remove_all method 2730 send(opts[:_remove_all_method], *args) 2731 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2732 associations[opts[:name]] = [] 2733 ret 2734 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2740 def remove_associated_object(opts, o, *args) 2741 klass = opts.associated_class 2742 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2743 o = remove_check_existing_object_from_pk(opts, o, *args) 2744 elsif !o.is_a?(klass) 2745 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2746 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2747 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2748 end 2749 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2750 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2751 return if run_association_callbacks(opts, :before_remove, o) == false 2752 # Allow calling private _remove method 2753 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2754 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2755 remove_reciprocal_object(opts, o) 2756 run_association_callbacks(opts, :after_remove, o) 2757 o 2758 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 2766 def remove_check_existing_object_from_pk(opts, o, *args) 2767 key = o 2768 pkh = opts.associated_class.qualified_primary_key_hash(key) 2769 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2770 o 2771 end
Remove/unset the current object from/as the given object’s reciprocal association.
# File lib/sequel/model/associations.rb 2774 def remove_reciprocal_object(opts, o) 2775 return unless reciprocal = opts.reciprocal 2776 if opts.reciprocal_array? 2777 if array = o.associations[reciprocal] 2778 array.delete_if{|x| self === x} 2779 end 2780 else 2781 o.associations[reciprocal] = nil 2782 end 2783 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2786 def run_association_callbacks(reflection, callback_type, object) 2787 return unless cbs = reflection[callback_type] 2788 2789 begin 2790 cbs.each do |cb| 2791 case cb 2792 when Symbol 2793 # Allow calling private methods in association callbacks 2794 send(cb, object) 2795 when Proc 2796 cb.call(self, object) 2797 else 2798 raise Error, "callbacks should either be Procs or Symbols" 2799 end 2800 end 2801 rescue HookFailed 2802 # The reason we automatically set raise_error for singular associations is that 2803 # assignment in ruby always returns the argument instead of the result of the 2804 # method, so we can't return nil to signal that the association callback prevented 2805 # the modification 2806 return false unless raise_on_save_failure || !reflection.returns_array? 2807 raise 2808 end 2809 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2845 def set_associated_object(opts, o) 2846 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2847 _set_associated_object(opts, o) 2848 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 2840 def set_associated_object_if_same? 2841 @set_associated_object_if_same 2842 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2851 def set_one_through_one_associated_object(opts, o) 2852 raise(Error, "object #{inspect} does not have a primary key") unless pk 2853 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2854 _set_associated_object(opts, o) 2855 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2858 def set_one_to_one_associated_object(opts, o) 2859 raise(Error, "object #{inspect} does not have a primary key") unless pk 2860 _set_associated_object(opts, o) 2861 end