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
2497 def associations
2498   @associations ||= {}
2499 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
2504 def freeze
2505   associations
2506   super
2507   associations.freeze
2508   self
2509 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
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
_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
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
_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
2535 def _associated_object_loader(opts, dynamic_opts)
2536   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2537     loader
2538   end
2539 end
_dataset(opts) click to toggle source

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
_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
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
_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
2561 def _load_associated_object(opts, dynamic_opts)
2562   _load_associated_object_array(opts, dynamic_opts).first
2563 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
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
_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
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
_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
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
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

Calls superclass method
     # File lib/sequel/model/associations.rb
2602 def _refresh_set_values(hash)
2603   @associations.clear if @associations
2604   super
2605 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
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_associated_object(opts, o, *args) click to toggle source

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_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
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
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
2641 def array_uniq!(a)
2642   a.uniq!
2643 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
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
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
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
initialize_copy(other) click to toggle source

Duplicate the associations hash when duplicating the object.

Calls superclass method
     # File lib/sequel/model/associations.rb
2690 def initialize_copy(other)
2691   super
2692   @associations = Hash[@associations] if @associations
2693   self
2694 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
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
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
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
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
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
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
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(opts, *args) click to toggle source

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

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
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
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_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
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_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
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_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
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
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
2872 def set_associated_object_if_same?
2873   @set_associated_object_if_same
2874 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
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_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
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