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 2482 def associations 2483 @associations ||= {} 2484 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 2489 def freeze 2490 associations 2491 super 2492 associations.freeze 2493 self 2494 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 2499 def _apply_association_options(opts, ds) 2500 unless ds.kind_of?(AssociationDatasetMethods) 2501 ds = opts.apply_dataset_changes(ds) 2502 end 2503 ds = ds.clone(:model_object => self) 2504 ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? 2505 # block method is private 2506 ds = send(opts[:block_method], ds) if opts[:block_method] 2507 ds 2508 end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb 2511 def _associated_dataset(opts, dynamic_opts) 2512 ds = public_send(opts.dataset_method) 2513 if callback = dynamic_opts[:callback] 2514 ds = callback.call(ds) 2515 end 2516 ds 2517 end
A placeholder literalizer that can be used to load the association, or nil to not use one.
# File lib/sequel/model/associations.rb 2520 def _associated_object_loader(opts, dynamic_opts) 2521 if !dynamic_opts[:callback] && (loader = opts.placeholder_loader) 2522 loader 2523 end 2524 end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb 2527 def _dataset(opts) 2528 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2529 ds = if opts[:dataset_opt_arity] == 1 2530 # dataset_opt_method is private 2531 send(opts[:dataset_opt_method], opts) 2532 else 2533 send(opts[:dataset_opt_method]) 2534 end 2535 _apply_association_options(opts, ds) 2536 end
Dataset
for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb 2539 def _join_table_dataset(opts) 2540 ds = (opts[:join_table_db] || model.db).from(opts.join_table_source) 2541 opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds 2542 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 2546 def _load_associated_object(opts, dynamic_opts) 2547 _load_associated_object_array(opts, dynamic_opts).first 2548 end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb 2557 def _load_associated_object_array(opts, dynamic_opts) 2558 if loader = _associated_object_loader(opts, dynamic_opts) 2559 loader.all(*opts.predicate_key_values(self)) 2560 else 2561 ds = _associated_dataset(opts, dynamic_opts) 2562 if ds.opts[:no_results] 2563 [] 2564 else 2565 ds.all 2566 end 2567 end 2568 end
Return the associated single object using a primary key lookup on the associated class.
# File lib/sequel/model/associations.rb 2551 def _load_associated_object_via_primary_key(opts) 2552 opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk))) 2553 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 2572 def _load_associated_objects(opts, dynamic_opts=OPTS) 2573 if opts.can_have_associated_objects?(self) 2574 if opts.returns_array? 2575 _load_associated_object_array(opts, dynamic_opts) 2576 elsif load_with_primary_key_lookup?(opts, dynamic_opts) 2577 _load_associated_object_via_primary_key(opts) 2578 else 2579 _load_associated_object(opts, dynamic_opts) 2580 end 2581 elsif opts.returns_array? 2582 [] 2583 end 2584 end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb 2587 def _refresh_set_values(hash) 2588 @associations.clear if @associations 2589 super 2590 end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb 2829 def _set_associated_object(opts, o) 2830 a = associations[opts[:name]] 2831 reciprocal = opts.reciprocal 2832 if set_associated_object_if_same? 2833 if reciprocal 2834 remove_reciprocal = a && (a != o || a.associations[reciprocal] != self) 2835 add_reciprocal = o && o.associations[reciprocal] != self 2836 end 2837 else 2838 return if a && a == o 2839 if reciprocal 2840 remove_reciprocal = a 2841 add_reciprocal = o 2842 end 2843 end 2844 run_association_callbacks(opts, :before_set, o) 2845 remove_reciprocal_object(opts, a) if remove_reciprocal 2846 # Allow calling private _setter method 2847 send(opts[:_setter_method], o) 2848 associations[opts[:name]] = o 2849 add_reciprocal_object(opts, o) if add_reciprocal 2850 run_association_callbacks(opts, :after_set, o) 2851 o 2852 end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb 2593 def add_associated_object(opts, o, *args) 2594 o = make_add_associated_object(opts, o) 2595 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2596 ensure_associated_primary_key(opts, o, *args) 2597 return if run_association_callbacks(opts, :before_add, o) == false 2598 # Allow calling private _add method 2599 return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure? 2600 if array = associations[opts[:name]] and !array.include?(o) 2601 array.push(o) 2602 end 2603 add_reciprocal_object(opts, o) 2604 run_association_callbacks(opts, :after_add, o) 2605 o 2606 end
Add/Set the current object to/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2612 def add_reciprocal_object(opts, o) 2613 return if o.frozen? 2614 return unless reciprocal = opts.reciprocal 2615 if opts.reciprocal_array? 2616 if array = o.associations[reciprocal] and !array.include?(self) 2617 array.push(self) 2618 end 2619 else 2620 o.associations[reciprocal] = self 2621 end 2622 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 2626 def array_uniq!(a) 2627 a.uniq! 2628 end
If a foreign key column value changes, clear the related cached associations.
# File lib/sequel/model/associations.rb 2632 def change_column_value(column, value) 2633 if assocs = model.autoreloading_associations[column] 2634 vals = @values 2635 if new? 2636 # Do deeper checking for new objects, so that associations are 2637 # not deleted when values do not change. This code is run at 2638 # a higher level for existing objects. 2639 if value == (c = vals[column]) && value.class == c.class 2640 # If the value is the same, there is no reason to delete 2641 # the related associations, so exit early in that case. 2642 return super 2643 end 2644 2645 only_delete_nil = c.nil? 2646 elsif vals[column].nil? 2647 only_delete_nil = true 2648 end 2649 2650 if only_delete_nil 2651 # If the current foreign key value is nil, but the association 2652 # is already present in the cache, it was probably added to the 2653 # cache for a reason, and we do not want to delete it in that case. 2654 # However, we still want to delete associations with nil values 2655 # to remove the cached false negative. 2656 assocs.each{|a| associations.delete(a) if associations[a].nil?} 2657 else 2658 assocs.each{|a| associations.delete(a)} 2659 end 2660 end 2661 super 2662 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 2667 def ensure_associated_primary_key(opts, o, *args) 2668 if opts.need_associated_primary_key? 2669 o.save(:validate=>opts[:validate]) if o.new? 2670 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk 2671 end 2672 end
Duplicate the associations hash when duplicating the object.
# File lib/sequel/model/associations.rb 2675 def initialize_copy(other) 2676 super 2677 @associations = Hash[@associations] if @associations 2678 self 2679 end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb 2692 def load_associated_objects(opts, dynamic_opts, &block) 2693 dynamic_opts = load_association_objects_options(dynamic_opts, &block) 2694 name = opts[:name] 2695 if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload] 2696 associations[name] 2697 else 2698 objs = _load_associated_objects(opts, dynamic_opts) 2699 if opts.set_reciprocal_to_self? 2700 if opts.returns_array? 2701 objs.each{|o| add_reciprocal_object(opts, o)} 2702 elsif objs 2703 add_reciprocal_object(opts, objs) 2704 end 2705 end 2706 2707 # If the current object is frozen, you can't update the associations 2708 # cache. This can cause issues for after_load procs that expect 2709 # the objects to be already cached in the associations, but 2710 # unfortunately that case cannot be handled. 2711 associations[name] = objs unless frozen? 2712 run_association_callbacks(opts, :after_load, objs) 2713 frozen? ? objs : associations[name] 2714 end 2715 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 2682 def load_association_objects_options(dynamic_opts, &block) 2683 if block 2684 dynamic_opts = Hash[dynamic_opts] 2685 dynamic_opts[:callback] = block 2686 end 2687 2688 dynamic_opts 2689 end
Whether to use a simple primary key lookup on the associated class when loading.
# File lib/sequel/model/associations.rb 2718 def load_with_primary_key_lookup?(opts, dynamic_opts) 2719 opts[:type] == :many_to_one && 2720 !dynamic_opts[:callback] && 2721 opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key} 2722 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 2728 def make_add_associated_object(opts, o) 2729 klass = opts.associated_class 2730 2731 case o 2732 when Hash 2733 klass.new(o) 2734 when Integer, String, Array 2735 klass.with_pk!(o) 2736 when klass 2737 o 2738 else 2739 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2740 end 2741 end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb 2744 def remove_all_associated_objects(opts, *args) 2745 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2746 # Allow calling private _remove_all method 2747 send(opts[:_remove_all_method], *args) 2748 ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) 2749 associations[opts[:name]] = [] 2750 ret 2751 end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb 2757 def remove_associated_object(opts, o, *args) 2758 klass = opts.associated_class 2759 if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) 2760 o = remove_check_existing_object_from_pk(opts, o, *args) 2761 elsif !o.is_a?(klass) 2762 raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") 2763 elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty? 2764 raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") 2765 end 2766 raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk 2767 raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk 2768 return if run_association_callbacks(opts, :before_remove, o) == false 2769 # Allow calling private _remove method 2770 return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure? 2771 associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) 2772 remove_reciprocal_object(opts, o) 2773 run_association_callbacks(opts, :after_remove, o) 2774 o 2775 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 2783 def remove_check_existing_object_from_pk(opts, o, *args) 2784 key = o 2785 pkh = opts.associated_class.qualified_primary_key_hash(key) 2786 raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh) 2787 o 2788 end
Remove/unset the current object from/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb 2791 def remove_reciprocal_object(opts, o) 2792 return unless reciprocal = opts.reciprocal 2793 if opts.reciprocal_array? 2794 if array = o.associations[reciprocal] 2795 array.delete_if{|x| self === x} 2796 end 2797 else 2798 o.associations[reciprocal] = nil 2799 end 2800 end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb 2803 def run_association_callbacks(reflection, callback_type, object) 2804 return unless cbs = reflection[callback_type] 2805 2806 begin 2807 cbs.each do |cb| 2808 case cb 2809 when Symbol 2810 # Allow calling private methods in association callbacks 2811 send(cb, object) 2812 when Proc 2813 cb.call(self, object) 2814 else 2815 raise Error, "callbacks should either be Procs or Symbols" 2816 end 2817 end 2818 rescue HookFailed 2819 # The reason we automatically set raise_error for singular associations is that 2820 # assignment in ruby always returns the argument instead of the result of the 2821 # method, so we can't return nil to signal that the association callback prevented 2822 # the modification 2823 return false unless raise_on_save_failure || !reflection.returns_array? 2824 raise 2825 end 2826 end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb 2862 def set_associated_object(opts, o) 2863 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2864 _set_associated_object(opts, o) 2865 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 2857 def set_associated_object_if_same? 2858 @set_associated_object_if_same 2859 end
Set the given object as the associated object for the given one_through_one association reflection
# File lib/sequel/model/associations.rb 2868 def set_one_through_one_associated_object(opts, o) 2869 raise(Error, "object #{inspect} does not have a primary key") unless pk 2870 raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk 2871 _set_associated_object(opts, o) 2872 end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb 2875 def set_one_to_one_associated_object(opts, o) 2876 raise(Error, "object #{inspect} does not have a primary key") unless pk 2877 _set_associated_object(opts, o) 2878 end