module Sequel::Model::Associations::InstanceMethods

Instance methods used to implement the associations support.

Public Instance Methods

associations() click to toggle source

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() click to toggle source

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.

Calls superclass method
     # File lib/sequel/model/associations.rb
2529 def freeze
2530   associations
2531   super
2532   associations.freeze
2533   self
2534 end

Private Instance Methods

_apply_association_options(opts, ds) click to toggle source

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
_associated_dataset(opts, dynamic_opts) click to toggle source

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
_associated_object_loader(opts, dynamic_opts) click to toggle source

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
_dataset(opts) click to toggle source

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
_join_table_dataset(opts) click to toggle source

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
_load_associated_object(opts, dynamic_opts) click to toggle source

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_associated_object_array(opts, dynamic_opts) click to toggle source

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
_load_associated_object_via_primary_key(opts) click to toggle source

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
_load_associated_objects(opts, dynamic_opts=OPTS) click to toggle source

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
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

Calls superclass method
     # File lib/sequel/model/associations.rb
2627 def _refresh_set_values(hash)
2628   @associations.clear if @associations
2629   super
2630 end
_set_associated_object(opts, o) click to toggle source

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_associated_object(opts, o, *args) click to toggle source

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_reciprocal_object(opts, o) click to toggle source

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
array_uniq!(a) click to toggle source

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
change_column_value(column, value) click to toggle source

If a foreign key column value changes, clear the related cached associations.

Calls superclass method
     # 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
ensure_associated_primary_key(opts, o, *args) click to toggle source

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
initialize_copy(other) click to toggle source

Duplicate the associations hash when duplicating the object.

Calls superclass method
     # File lib/sequel/model/associations.rb
2715 def initialize_copy(other)
2716   super
2717   @associations = Hash[@associations] if @associations
2718   self
2719 end
load_associated_objects(opts, dynamic_opts, &block) click to toggle source

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
load_association_objects_options(dynamic_opts, &block) click to toggle source

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
load_with_primary_key_lookup?(opts, dynamic_opts) click to toggle source

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
make_add_associated_object(opts, o) click to toggle source

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(opts, *args) click to toggle source

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_associated_object(opts, o, *args) click to toggle source

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
remove_check_existing_object_from_pk(opts, o, *args) click to toggle source

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_reciprocal_object(opts, o) click to toggle source

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_association_callbacks(reflection, callback_type, object) click to toggle source

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_associated_object(opts, o) click to toggle source

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
set_associated_object_if_same?() click to toggle source

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_one_through_one_associated_object(opts, o) click to toggle source

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_one_to_one_associated_object(opts, o) click to toggle source

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