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
2482 def associations
2483   @associations ||= {}
2484 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
2489 def freeze
2490   associations
2491   super
2492   associations.freeze
2493   self
2494 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
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
_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
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
_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
2520 def _associated_object_loader(opts, dynamic_opts)
2521   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2522     loader
2523   end
2524 end
_dataset(opts) click to toggle source

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

Clear the associations cache when refreshing

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

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_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
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
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
2626 def array_uniq!(a)
2627   a.uniq!
2628 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
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
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
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
initialize_copy(other) click to toggle source

Duplicate the associations hash when duplicating the object.

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

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

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
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
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_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
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_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
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_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
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
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
2857 def set_associated_object_if_same?
2858   @set_associated_object_if_same
2859 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
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_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
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