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
2514 def associations
2515   @associations ||= {}
2516 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
2521 def freeze
2522   associations
2523   super
2524   associations.freeze
2525   self
2526 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
2531 def _apply_association_options(opts, ds)
2532   unless ds.kind_of?(AssociationDatasetMethods)
2533     ds = opts.apply_dataset_changes(ds)
2534   end
2535   ds = ds.clone(:model_object => self)
2536   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2537   # block method is private
2538   ds = send(opts[:block_method], ds) if opts[:block_method]
2539   ds
2540 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
2543 def _associated_dataset(opts, dynamic_opts)
2544   ds = public_send(opts.dataset_method)
2545   if callback = dynamic_opts[:callback]
2546     ds = callback.call(ds)
2547   end
2548   ds
2549 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
2552 def _associated_object_loader(opts, dynamic_opts)
2553   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2554     loader
2555   end
2556 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2559 def _dataset(opts)
2560   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2561   ds = if opts[:dataset_opt_arity] == 1
2562     # dataset_opt_method is private
2563     send(opts[:dataset_opt_method], opts)
2564   else
2565     send(opts[:dataset_opt_method])
2566   end
2567   _apply_association_options(opts, ds)
2568 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
2571 def _join_table_dataset(opts)
2572   ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
2573   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2574 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
2578 def _load_associated_object(opts, dynamic_opts)
2579   _load_associated_object_array(opts, dynamic_opts).first
2580 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
2589 def _load_associated_object_array(opts, dynamic_opts)
2590   if loader = _associated_object_loader(opts, dynamic_opts)
2591     loader.all(*opts.predicate_key_values(self))
2592   else
2593     ds = _associated_dataset(opts, dynamic_opts)
2594     if ds.opts[:no_results]
2595       []
2596     else
2597       ds.all
2598     end
2599   end
2600 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
2583 def _load_associated_object_via_primary_key(opts)
2584   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2585 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
2604 def _load_associated_objects(opts, dynamic_opts=OPTS)
2605   if opts.can_have_associated_objects?(self)
2606     if opts.returns_array?
2607       _load_associated_object_array(opts, dynamic_opts)
2608     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2609       _load_associated_object_via_primary_key(opts)
2610     else
2611       _load_associated_object(opts, dynamic_opts)
2612     end
2613   elsif opts.returns_array?
2614     []
2615   end
2616 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

Calls superclass method
     # File lib/sequel/model/associations.rb
2619 def _refresh_set_values(hash)
2620   @associations.clear if @associations
2621   super
2622 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
2861 def _set_associated_object(opts, o)
2862   a = associations[opts[:name]]
2863   reciprocal = opts.reciprocal
2864   if set_associated_object_if_same?
2865     if reciprocal
2866       remove_reciprocal = a && (a != o || a.associations[reciprocal] != self)
2867       add_reciprocal = o && o.associations[reciprocal] != self
2868     end
2869   else
2870     return if a && a == o
2871     if reciprocal
2872       remove_reciprocal = a
2873       add_reciprocal = o
2874     end
2875   end
2876   run_association_callbacks(opts, :before_set, o)
2877   remove_reciprocal_object(opts, a) if remove_reciprocal
2878   # Allow calling private _setter method
2879   send(opts[:_setter_method], o)
2880   associations[opts[:name]] = o
2881   add_reciprocal_object(opts, o) if add_reciprocal
2882   run_association_callbacks(opts, :after_set, o)
2883   o
2884 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
2625 def add_associated_object(opts, o, *args)
2626   o = make_add_associated_object(opts, o)
2627   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2628   ensure_associated_primary_key(opts, o, *args)
2629   return if run_association_callbacks(opts, :before_add, o) == false
2630   # Allow calling private _add method
2631   return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure?
2632   if array = associations[opts[:name]] and !array.include?(o)
2633     array.push(o)
2634   end
2635   add_reciprocal_object(opts, o)
2636   run_association_callbacks(opts, :after_add, o)
2637   o
2638 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
2644 def add_reciprocal_object(opts, o)
2645   return if o.frozen?
2646   return unless reciprocal = opts.reciprocal
2647   if opts.reciprocal_array?
2648     if array = o.associations[reciprocal] and !array.include?(self)
2649       array.push(self)
2650     end
2651   else
2652     o.associations[reciprocal] = self
2653   end
2654 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
2658 def array_uniq!(a)
2659   a.uniq!
2660 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
2664 def change_column_value(column, value)
2665   if assocs = model.autoreloading_associations[column]
2666     vals = @values
2667     if new?
2668       # Do deeper checking for new objects, so that associations are
2669       # not deleted when values do not change.  This code is run at
2670       # a higher level for existing objects.
2671       if value == (c = vals[column]) && value.class == c.class
2672         # If the value is the same, there is no reason to delete
2673         # the related associations, so exit early in that case.
2674         return super
2675       end
2676 
2677       only_delete_nil = c.nil?
2678     elsif vals[column].nil?
2679       only_delete_nil = true
2680     end
2681 
2682     if only_delete_nil
2683       # If the current foreign key value is nil, but the association
2684       # is already present in the cache, it was probably added to the
2685       # cache for a reason, and we do not want to delete it in that case.
2686       # However, we still want to delete associations with nil values
2687       # to remove the cached false negative.
2688       assocs.each{|a| associations.delete(a) if associations[a].nil?}
2689     else
2690       assocs.each{|a| associations.delete(a)}
2691     end
2692   end
2693   super
2694 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
2699 def ensure_associated_primary_key(opts, o, *args)
2700   if opts.need_associated_primary_key?
2701     o.save(:validate=>opts[:validate]) if o.new?
2702     raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk
2703   end
2704 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
2707 def initialize_copy(other)
2708   super
2709   @associations = Hash[@associations] if @associations
2710   self
2711 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
2724 def load_associated_objects(opts, dynamic_opts, &block)
2725   dynamic_opts = load_association_objects_options(dynamic_opts, &block)
2726   name = opts[:name]
2727   if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload]
2728     associations[name]
2729   else
2730     objs = _load_associated_objects(opts, dynamic_opts)
2731     if opts.set_reciprocal_to_self?
2732       if opts.returns_array?
2733         objs.each{|o| add_reciprocal_object(opts, o)}
2734       elsif objs
2735         add_reciprocal_object(opts, objs)
2736       end
2737     end
2738 
2739     # If the current object is frozen, you can't update the associations
2740     # cache.  This can cause issues for after_load procs that expect
2741     # the objects to be already cached in the associations, but
2742     # unfortunately that case cannot be handled.
2743     associations[name] = objs unless frozen?
2744     run_association_callbacks(opts, :after_load, objs)
2745     frozen? ? objs : associations[name]
2746   end
2747 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
2714 def load_association_objects_options(dynamic_opts, &block)
2715   if block
2716     dynamic_opts = Hash[dynamic_opts]
2717     dynamic_opts[:callback] = block
2718   end
2719 
2720   dynamic_opts
2721 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
2750 def load_with_primary_key_lookup?(opts, dynamic_opts)
2751   opts[:type] == :many_to_one &&
2752     !dynamic_opts[:callback] && 
2753     opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key}
2754 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
2760 def make_add_associated_object(opts, o)
2761   klass = opts.associated_class
2762 
2763   case o
2764   when Hash
2765     klass.new(o)
2766   when Integer, String, Array
2767     klass.with_pk!(o)
2768   when klass
2769     o
2770   else 
2771     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2772   end
2773 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
2776 def remove_all_associated_objects(opts, *args)
2777   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2778   # Allow calling private _remove_all method
2779   send(opts[:_remove_all_method], *args)
2780   ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
2781   associations[opts[:name]] = []
2782   ret
2783 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
2789 def remove_associated_object(opts, o, *args)
2790   klass = opts.associated_class
2791   if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
2792     o = remove_check_existing_object_from_pk(opts, o, *args)
2793   elsif !o.is_a?(klass)
2794     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2795   elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty?
2796     raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
2797   end
2798   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2799   raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
2800   return if run_association_callbacks(opts, :before_remove, o) == false
2801   # Allow calling private _remove method
2802   return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure?
2803   associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
2804   remove_reciprocal_object(opts, o)
2805   run_association_callbacks(opts, :after_remove, o)
2806   o
2807 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
2815 def remove_check_existing_object_from_pk(opts, o, *args)
2816   key = o
2817   pkh = opts.associated_class.qualified_primary_key_hash(key)
2818   raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh)
2819   o
2820 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
2823 def remove_reciprocal_object(opts, o)
2824   return unless reciprocal = opts.reciprocal
2825   if opts.reciprocal_array?
2826     if array = o.associations[reciprocal]
2827       array.delete_if{|x| self === x}
2828     end
2829   else
2830     o.associations[reciprocal] = nil
2831   end
2832 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
2835 def run_association_callbacks(reflection, callback_type, object)
2836   return unless cbs = reflection[callback_type]
2837 
2838   begin
2839     cbs.each do |cb|
2840       case cb
2841       when Symbol
2842         # Allow calling private methods in association callbacks
2843         send(cb, object)
2844       when Proc
2845         cb.call(self, object)
2846       else
2847         raise Error, "callbacks should either be Procs or Symbols"
2848       end
2849     end
2850   rescue HookFailed
2851     # The reason we automatically set raise_error for singular associations is that
2852     # assignment in ruby always returns the argument instead of the result of the
2853     # method, so we can't return nil to signal that the association callback prevented
2854     # the modification
2855     return false unless raise_on_save_failure || !reflection.returns_array?
2856     raise
2857   end
2858 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
2894 def set_associated_object(opts, o)
2895   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2896   _set_associated_object(opts, o)
2897 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
2889 def set_associated_object_if_same?
2890   @set_associated_object_if_same
2891 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
2900 def set_one_through_one_associated_object(opts, o)
2901   raise(Error, "object #{inspect} does not have a primary key") unless pk
2902   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2903   _set_associated_object(opts, o)
2904 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
2907 def set_one_to_one_associated_object(opts, o)
2908   raise(Error, "object #{inspect} does not have a primary key") unless pk
2909   _set_associated_object(opts, o)
2910 end