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