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 2497 def associations 2498 @associations ||= {} 2499 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 2504 def freeze 2505 associations 2506 super 2507 associations.freeze 2508 self 2509 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 2514 def _apply_association_options(opts, ds) 2515 unless ds.kind_of?(AssociationDatasetMethods) 2516 ds = opts.apply_dataset_changes(ds) 2517 end 2518 ds = ds.clone(:model_object => self) 2519 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2520 # block method is private 2521 ds = send(opts[:block_method], ds) if opts[:block_method] 2522 ds 2523 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2526 def _associated_dataset(opts, dynamic_opts) 2527 ds = public_send(opts.dataset_method) 2528 if callback = dynamic_opts[:callback] 2529 ds = callback.call(ds) 2530 end 2531 ds 2532 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2535 def _associated_object_loader(opts, dynamic_opts) 2536 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2537 loader 2538 end 2539 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2542 def _dataset(opts) 2543 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2544 ds = if opts[:dataset_opt_arity] == 1 2545 # dataset_opt_method is private 2546 send(opts[:dataset_opt_method], opts) 2547 else 2548 send(opts[:dataset_opt_method]) 2549 end 2550 _apply_association_options(opts, ds) 2551 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2554 def _join_table_dataset(opts) 2555 ds = (opts[:join_table_db] || model.db).from(opts.join_table_source) 2556 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2557 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 2561 def _load_associated_object(opts, dynamic_opts) 2562 _load_associated_object_array(opts, dynamic_opts).first 2563 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2572 def _load_associated_object_array(opts, dynamic_opts) 2573 if loader = _associated_object_loader(opts, dynamic_opts) 2574 loader.all(*opts.predicate_key_values(self)) 2575 else 2576 ds = _associated_dataset(opts, dynamic_opts) 2577 if ds.opts[:no_results] 2578 [] 2579 else 2580 ds.all 2581 end 2582 end 2583 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2566 def _load_associated_object_via_primary_key(opts) 2567 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2568 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 2587 def _load_associated_objects(opts, dynamic_opts=OPTS) 2588 if opts.can_have_associated_objects?(self) 2589 if opts.returns_array? 2590 _load_associated_object_array(opts, dynamic_opts) 2591 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2592 _load_associated_object_via_primary_key(opts) 2593 else 2594 _load_associated_object(opts, dynamic_opts) 2595 end 2596 elsif opts.returns_array? 2597 [] 2598 end 2599 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2602 def _refresh_set_values(hash) 2603 @associations.clear if @associations 2604 super 2605 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2844 def _set_associated_object(opts, o) 2845 a = associations[opts[:name]] 2846 reciprocal = opts.reciprocal 2847 if set_associated_object_if_same? 2848 if reciprocal 2849 remove_reciprocal = a && (a != o || a.associations[reciprocal] != self) 2850 add_reciprocal = o && o.associations[reciprocal] != self 2851 end 2852 else 2853 return if a && a == o 2854 if reciprocal 2855 remove_reciprocal = a 2856 add_reciprocal = o 2857 end 2858 end 2859 run_association_callbacks(opts, :before_set, o) 2860 remove_reciprocal_object(opts, a) if remove_reciprocal 2861 # Allow calling private _setter method 2862 send(opts[:_setter_method], o) 2863 associations[opts[:name]] = o 2864 add_reciprocal_object(opts, o) if add_reciprocal 2865 run_association_callbacks(opts, :after_set, o) 2866 o 2867 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2608 def add_associated_object(opts, o, *args) 2609 o = make_add_associated_object(opts, o) 2610 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2611 ensure_associated_primary_key(opts, o, *args) 2612 return if run_association_callbacks(opts, :before_add, o) == false 2613 # Allow calling private _add method 2614 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2615 if array = associations[opts[:name]] and !array.include?(o) 2616 array.push(o) 2617 end 2618 add_reciprocal_object(opts, o) 2619 run_association_callbacks(opts, :after_add, o) 2620 o 2621 end
Add/Set the current object to/as the given object’s reciprocal association.
# File lib/sequel/model/associations.rb 2627 def add_reciprocal_object(opts, o) 2628 return if o.frozen? 2629 return unless reciprocal = opts.reciprocal 2630 if opts.reciprocal_array? 2631 if array = o.associations[reciprocal] and !array.include?(self) 2632 array.push(self) 2633 end 2634 else 2635 o.associations[reciprocal] = self 2636 end 2637 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 2641 def array_uniq!(a) 2642 a.uniq! 2643 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2647 def change_column_value(column, value) 2648 if assocs = model.autoreloading_associations[column] 2649 vals = @values 2650 if new? 2651 # Do deeper checking for new objects, so that associations are 2652 # not deleted when values do not change. This code is run at 2653 # a higher level for existing objects. 2654 if value == (c = vals[column]) && value.class == c.class 2655 # If the value is the same, there is no reason to delete 2656 # the related associations, so exit early in that case. 2657 return super 2658 end 2659 2660 only_delete_nil = c.nil? 2661 elsif vals[column].nil? 2662 only_delete_nil = true 2663 end 2664 2665 if only_delete_nil 2666 # If the current foreign key value is nil, but the association 2667 # is already present in the cache, it was probably added to the 2668 # cache for a reason, and we do not want to delete it in that case. 2669 # However, we still want to delete associations with nil values 2670 # to remove the cached false negative. 2671 assocs.each{|a| associations.delete(a) if associations[a].nil?} 2672 else 2673 assocs.each{|a| associations.delete(a)} 2674 end 2675 end 2676 super 2677 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 2682 def ensure_associated_primary_key(opts, o, *args) 2683 if opts.need_associated_primary_key? 2684 o.save(:validate=>opts[:validate]) if o.new? 2685 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2686 end 2687 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2690 def initialize_copy(other) 2691 super 2692 @associations = Hash[@associations] if @associations 2693 self 2694 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2707 def load_associated_objects(opts, dynamic_opts, &block) 2708 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2709 name = opts[:name] 2710 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2711 associations[name] 2712 else 2713 objs = _load_associated_objects(opts, dynamic_opts) 2714 if opts.set_reciprocal_to_self? 2715 if opts.returns_array? 2716 objs.each{|o| add_reciprocal_object(opts, o)} 2717 elsif objs 2718 add_reciprocal_object(opts, objs) 2719 end 2720 end 2721 2722 # If the current object is frozen, you can't update the associations 2723 # cache. This can cause issues for after_load procs that expect 2724 # the objects to be already cached in the associations, but 2725 # unfortunately that case cannot be handled. 2726 associations[name] = objs unless frozen? 2727 run_association_callbacks(opts, :after_load, objs) 2728 frozen? ? objs : associations[name] 2729 end 2730 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 2697 def load_association_objects_options(dynamic_opts, &block) 2698 if block 2699 dynamic_opts = Hash[dynamic_opts] 2700 dynamic_opts[:callback] = block 2701 end 2702 2703 dynamic_opts 2704 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2733 def load_with_primary_key_lookup?(opts, dynamic_opts) 2734 opts[:type] == :many_to_one && 2735 !dynamic_opts[:callback] && 2736 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2737 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 2743 def make_add_associated_object(opts, o) 2744 klass = opts.associated_class 2745 2746 case o 2747 when Hash 2748 klass.new(o) 2749 when Integer, String, Array 2750 klass.with_pk!(o) 2751 when klass 2752 o 2753 else 2754 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2755 end 2756 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2759 def remove_all_associated_objects(opts, *args) 2760 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2761 # Allow calling private _remove_all method 2762 send(opts[:_remove_all_method], *args) 2763 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2764 associations[opts[:name]] = [] 2765 ret 2766 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2772 def remove_associated_object(opts, o, *args) 2773 klass = opts.associated_class 2774 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2775 o = remove_check_existing_object_from_pk(opts, o, *args) 2776 elsif !o.is_a?(klass) 2777 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2778 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2779 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2780 end 2781 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2782 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2783 return if run_association_callbacks(opts, :before_remove, o) == false 2784 # Allow calling private _remove method 2785 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2786 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2787 remove_reciprocal_object(opts, o) 2788 run_association_callbacks(opts, :after_remove, o) 2789 o 2790 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 2798 def remove_check_existing_object_from_pk(opts, o, *args) 2799 key = o 2800 pkh = opts.associated_class.qualified_primary_key_hash(key) 2801 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2802 o 2803 end
Remove/unset the current object from/as the given object’s reciprocal association.
# File lib/sequel/model/associations.rb 2806 def remove_reciprocal_object(opts, o) 2807 return unless reciprocal = opts.reciprocal 2808 if opts.reciprocal_array? 2809 if array = o.associations[reciprocal] 2810 array.delete_if{|x| self === x} 2811 end 2812 else 2813 o.associations[reciprocal] = nil 2814 end 2815 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2818 def run_association_callbacks(reflection, callback_type, object) 2819 return unless cbs = reflection[callback_type] 2820 2821 begin 2822 cbs.each do |cb| 2823 case cb 2824 when Symbol 2825 # Allow calling private methods in association callbacks 2826 send(cb, object) 2827 when Proc 2828 cb.call(self, object) 2829 else 2830 raise Error, "callbacks should either be Procs or Symbols" 2831 end 2832 end 2833 rescue HookFailed 2834 # The reason we automatically set raise_error for singular associations is that 2835 # assignment in ruby always returns the argument instead of the result of the 2836 # method, so we can't return nil to signal that the association callback prevented 2837 # the modification 2838 return false unless raise_on_save_failure || !reflection.returns_array? 2839 raise 2840 end 2841 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2877 def set_associated_object(opts, o) 2878 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2879 _set_associated_object(opts, o) 2880 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 2872 def set_associated_object_if_same? 2873 @set_associated_object_if_same 2874 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2883 def set_one_through_one_associated_object(opts, o) 2884 raise(Error, "object #{inspect} does not have a primary key") unless pk 2885 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2886 _set_associated_object(opts, o) 2887 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2890 def set_one_to_one_associated_object(opts, o) 2891 raise(Error, "object #{inspect} does not have a primary key") unless pk 2892 _set_associated_object(opts, o) 2893 end