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 2522 def associations 2523 @associations ||= {} 2524 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 2529 def freeze 2530 associations 2531 super 2532 associations.freeze 2533 self 2534 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 2539 def _apply_association_options(opts, ds) 2540 unless ds.kind_of?(AssociationDatasetMethods) 2541 ds = opts.apply_dataset_changes(ds) 2542 end 2543 ds = ds.clone(:model_object => self) 2544 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2545 # block method is private 2546 ds = send(opts[:block_method], ds) if opts[:block_method] 2547 ds 2548 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2551 def _associated_dataset(opts, dynamic_opts) 2552 ds = public_send(opts.dataset_method) 2553 if callback = dynamic_opts[:callback] 2554 ds = callback.call(ds) 2555 end 2556 ds 2557 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2560 def _associated_object_loader(opts, dynamic_opts) 2561 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2562 loader 2563 end 2564 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2567 def _dataset(opts) 2568 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2569 ds = if opts[:dataset_opt_arity] == 1 2570 # dataset_opt_method is private 2571 send(opts[:dataset_opt_method], opts) 2572 else 2573 send(opts[:dataset_opt_method]) 2574 end 2575 _apply_association_options(opts, ds) 2576 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2579 def _join_table_dataset(opts) 2580 ds = (opts[:join_table_db] || model.db).from(opts.join_table_source) 2581 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2582 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 2586 def _load_associated_object(opts, dynamic_opts) 2587 _load_associated_object_array(opts, dynamic_opts).first 2588 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2597 def _load_associated_object_array(opts, dynamic_opts) 2598 if loader = _associated_object_loader(opts, dynamic_opts) 2599 loader.all(*opts.predicate_key_values(self)) 2600 else 2601 ds = _associated_dataset(opts, dynamic_opts) 2602 if ds.opts[:no_results] 2603 [] 2604 else 2605 ds.all 2606 end 2607 end 2608 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2591 def _load_associated_object_via_primary_key(opts) 2592 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2593 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 2612 def _load_associated_objects(opts, dynamic_opts=OPTS) 2613 if opts.can_have_associated_objects?(self) 2614 if opts.returns_array? 2615 _load_associated_object_array(opts, dynamic_opts) 2616 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2617 _load_associated_object_via_primary_key(opts) 2618 else 2619 _load_associated_object(opts, dynamic_opts) 2620 end 2621 elsif opts.returns_array? 2622 [] 2623 end 2624 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2627 def _refresh_set_values(hash) 2628 @associations.clear if @associations 2629 super 2630 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2869 def _set_associated_object(opts, o) 2870 a = associations[opts[:name]] 2871 reciprocal = opts.reciprocal 2872 if set_associated_object_if_same? 2873 if reciprocal 2874 remove_reciprocal = a && (a != o || a.associations[reciprocal] != self) 2875 add_reciprocal = o && o.associations[reciprocal] != self 2876 end 2877 else 2878 return if a && a == o 2879 if reciprocal 2880 remove_reciprocal = a 2881 add_reciprocal = o 2882 end 2883 end 2884 run_association_callbacks(opts, :before_set, o) 2885 remove_reciprocal_object(opts, a) if remove_reciprocal 2886 # Allow calling private _setter method 2887 send(opts[:_setter_method], o) 2888 associations[opts[:name]] = o 2889 add_reciprocal_object(opts, o) if add_reciprocal 2890 run_association_callbacks(opts, :after_set, o) 2891 o 2892 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2633 def add_associated_object(opts, o, *args) 2634 o = make_add_associated_object(opts, o) 2635 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2636 ensure_associated_primary_key(opts, o, *args) 2637 return if run_association_callbacks(opts, :before_add, o) == false 2638 # Allow calling private _add method 2639 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2640 if array = associations[opts[:name]] and !array.include?(o) 2641 array.push(o) 2642 end 2643 add_reciprocal_object(opts, o) 2644 run_association_callbacks(opts, :after_add, o) 2645 o 2646 end
Add/Set the current object to/as the given object’s reciprocal association.
# File lib/sequel/model/associations.rb 2652 def add_reciprocal_object(opts, o) 2653 return if o.frozen? 2654 return unless reciprocal = opts.reciprocal 2655 if opts.reciprocal_array? 2656 if array = o.associations[reciprocal] and !array.include?(self) 2657 array.push(self) 2658 end 2659 else 2660 o.associations[reciprocal] = self 2661 end 2662 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 2666 def array_uniq!(a) 2667 a.uniq! 2668 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2672 def change_column_value(column, value) 2673 if assocs = model.autoreloading_associations[column] 2674 vals = @values 2675 if new? 2676 # Do deeper checking for new objects, so that associations are 2677 # not deleted when values do not change. This code is run at 2678 # a higher level for existing objects. 2679 if value == (c = vals[column]) && value.class == c.class 2680 # If the value is the same, there is no reason to delete 2681 # the related associations, so exit early in that case. 2682 return super 2683 end 2684 2685 only_delete_nil = c.nil? 2686 elsif vals[column].nil? 2687 only_delete_nil = true 2688 end 2689 2690 if only_delete_nil 2691 # If the current foreign key value is nil, but the association 2692 # is already present in the cache, it was probably added to the 2693 # cache for a reason, and we do not want to delete it in that case. 2694 # However, we still want to delete associations with nil values 2695 # to remove the cached false negative. 2696 assocs.each{|a| associations.delete(a) if associations[a].nil?} 2697 else 2698 assocs.each{|a| associations.delete(a)} 2699 end 2700 end 2701 super 2702 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 2707 def ensure_associated_primary_key(opts, o, *args) 2708 if opts.need_associated_primary_key? 2709 o.save(:validate=>opts[:validate]) if o.new? 2710 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2711 end 2712 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2715 def initialize_copy(other) 2716 super 2717 @associations = Hash[@associations] if @associations 2718 self 2719 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2732 def load_associated_objects(opts, dynamic_opts, &block) 2733 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2734 name = opts[:name] 2735 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2736 associations[name] 2737 else 2738 objs = _load_associated_objects(opts, dynamic_opts) 2739 if opts.set_reciprocal_to_self? 2740 if opts.returns_array? 2741 objs.each{|o| add_reciprocal_object(opts, o)} 2742 elsif objs 2743 add_reciprocal_object(opts, objs) 2744 end 2745 end 2746 2747 # If the current object is frozen, you can't update the associations 2748 # cache. This can cause issues for after_load procs that expect 2749 # the objects to be already cached in the associations, but 2750 # unfortunately that case cannot be handled. 2751 associations[name] = objs unless frozen? 2752 run_association_callbacks(opts, :after_load, objs) 2753 frozen? ? objs : associations[name] 2754 end 2755 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 2722 def load_association_objects_options(dynamic_opts, &block) 2723 if block 2724 dynamic_opts = Hash[dynamic_opts] 2725 dynamic_opts[:callback] = block 2726 end 2727 2728 dynamic_opts 2729 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2758 def load_with_primary_key_lookup?(opts, dynamic_opts) 2759 opts[:type] == :many_to_one && 2760 !dynamic_opts[:callback] && 2761 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2762 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 2768 def make_add_associated_object(opts, o) 2769 klass = opts.associated_class 2770 2771 case o 2772 when Hash 2773 klass.new(o) 2774 when Integer, String, Array 2775 klass.with_pk!(o) 2776 when klass 2777 o 2778 else 2779 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2780 end 2781 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2784 def remove_all_associated_objects(opts, *args) 2785 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2786 # Allow calling private _remove_all method 2787 send(opts[:_remove_all_method], *args) 2788 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2789 associations[opts[:name]] = [] 2790 ret 2791 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2797 def remove_associated_object(opts, o, *args) 2798 klass = opts.associated_class 2799 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2800 o = remove_check_existing_object_from_pk(opts, o, *args) 2801 elsif !o.is_a?(klass) 2802 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2803 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2804 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2805 end 2806 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2807 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2808 return if run_association_callbacks(opts, :before_remove, o) == false 2809 # Allow calling private _remove method 2810 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2811 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2812 remove_reciprocal_object(opts, o) 2813 run_association_callbacks(opts, :after_remove, o) 2814 o 2815 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 2823 def remove_check_existing_object_from_pk(opts, o, *args) 2824 key = o 2825 pkh = opts.associated_class.qualified_primary_key_hash(key) 2826 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2827 o 2828 end
Remove/unset the current object from/as the given object’s reciprocal association.
# File lib/sequel/model/associations.rb 2831 def remove_reciprocal_object(opts, o) 2832 return unless reciprocal = opts.reciprocal 2833 if opts.reciprocal_array? 2834 if array = o.associations[reciprocal] 2835 array.delete_if{|x| self === x} 2836 end 2837 else 2838 o.associations[reciprocal] = nil 2839 end 2840 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2843 def run_association_callbacks(reflection, callback_type, object) 2844 return unless cbs = reflection[callback_type] 2845 2846 begin 2847 cbs.each do |cb| 2848 case cb 2849 when Symbol 2850 # Allow calling private methods in association callbacks 2851 send(cb, object) 2852 when Proc 2853 cb.call(self, object) 2854 else 2855 raise Error, "callbacks should either be Procs or Symbols" 2856 end 2857 end 2858 rescue HookFailed 2859 # The reason we automatically set raise_error for singular associations is that 2860 # assignment in ruby always returns the argument instead of the result of the 2861 # method, so we can't return nil to signal that the association callback prevented 2862 # the modification 2863 return false unless raise_on_save_failure || !reflection.returns_array? 2864 raise 2865 end 2866 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2902 def set_associated_object(opts, o) 2903 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2904 _set_associated_object(opts, o) 2905 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 2897 def set_associated_object_if_same? 2898 @set_associated_object_if_same 2899 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2908 def set_one_through_one_associated_object(opts, o) 2909 raise(Error, "object #{inspect} does not have a primary key") unless pk 2910 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2911 _set_associated_object(opts, o) 2912 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2915 def set_one_to_one_associated_object(opts, o) 2916 raise(Error, "object #{inspect} does not have a primary key") unless pk 2917 _set_associated_object(opts, o) 2918 end