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
2697 def associations
2698   @associations ||= {}
2699 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
2704 def freeze
2705   associations
2706   super
2707   associations.freeze
2708   self
2709 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
2714 def _apply_association_options(opts, ds)
2715   unless ds.kind_of?(AssociationDatasetMethods)
2716     ds = opts.apply_dataset_changes(ds)
2717   end
2718   ds = ds.clone(:model_object => self)
2719   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2720   # block method is private
2721   ds = send(opts[:block_method], ds) if opts[:block_method]
2722   ds
2723 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
2726 def _associated_dataset(opts, dynamic_opts)
2727   ds = public_send(opts.dataset_method)
2728   if callback = dynamic_opts[:callback]
2729     ds = callback.call(ds)
2730   end
2731   ds
2732 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
2735 def _associated_object_loader(opts, dynamic_opts)
2736   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2737     loader
2738   end
2739 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2742 def _dataset(opts)
2743   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2744   ds = if opts[:dataset_opt_arity] == 1
2745     # dataset_opt_method is private
2746     send(opts[:dataset_opt_method], opts)
2747   else
2748     send(opts[:dataset_opt_method])
2749   end
2750   _apply_association_options(opts, ds)
2751 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
2754 def _join_table_dataset(opts)
2755   ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
2756   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2757 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
2761 def _load_associated_object(opts, dynamic_opts)
2762   _load_associated_object_array(opts, dynamic_opts).first
2763 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
2772 def _load_associated_object_array(opts, dynamic_opts)
2773   if loader = _associated_object_loader(opts, dynamic_opts)
2774     loader.all(*opts.predicate_key_values(self))
2775   else
2776     ds = _associated_dataset(opts, dynamic_opts)
2777     if ds.opts[:no_results]
2778       []
2779     else
2780       ds.all
2781     end
2782   end
2783 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
2766 def _load_associated_object_via_primary_key(opts)
2767   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2768 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
2787 def _load_associated_objects(opts, dynamic_opts=OPTS)
2788   if opts.can_have_associated_objects?(self)
2789     if opts.returns_array?
2790       _load_associated_object_array(opts, dynamic_opts)
2791     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2792       _load_associated_object_via_primary_key(opts)
2793     else
2794       _load_associated_object(opts, dynamic_opts)
2795     end
2796   elsif opts.returns_array?
2797     []
2798   end
2799 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

Calls superclass method
     # File lib/sequel/model/associations.rb
2802 def _refresh_set_values(hash)
2803   @associations.clear if @associations
2804   super
2805 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
3044 def _set_associated_object(opts, o)
3045   a = associations[opts[:name]]
3046   reciprocal = opts.reciprocal
3047   if set_associated_object_if_same?
3048     if reciprocal
3049       remove_reciprocal = a && (a != o || a.associations[reciprocal] != self)
3050       add_reciprocal = o && o.associations[reciprocal] != self
3051     end
3052   else
3053     return if a && a == o
3054     if reciprocal
3055       remove_reciprocal = a
3056       add_reciprocal = o
3057     end
3058   end
3059   run_association_callbacks(opts, :before_set, o)
3060   remove_reciprocal_object(opts, a) if remove_reciprocal
3061   # Allow calling private _setter method
3062   send(opts[:_setter_method], o)
3063   associations[opts[:name]] = o
3064   add_reciprocal_object(opts, o) if add_reciprocal
3065   run_association_callbacks(opts, :after_set, o)
3066   o
3067 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
2808 def add_associated_object(opts, o, *args)
2809   o = make_add_associated_object(opts, o)
2810   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2811   ensure_associated_primary_key(opts, o, *args)
2812   return if run_association_callbacks(opts, :before_add, o) == false
2813   # Allow calling private _add method
2814   return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure?
2815   if array = associations[opts[:name]] and !array.include?(o)
2816     array.push(o)
2817   end
2818   add_reciprocal_object(opts, o)
2819   run_association_callbacks(opts, :after_add, o)
2820   o
2821 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
2827 def add_reciprocal_object(opts, o)
2828   return if o.frozen?
2829   return unless reciprocal = opts.reciprocal
2830   if opts.reciprocal_array?
2831     if array = o.associations[reciprocal] and !array.include?(self)
2832       array.push(self)
2833     end
2834   else
2835     o.associations[reciprocal] = self
2836   end
2837 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
2841 def array_uniq!(a)
2842   a.uniq!
2843 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
2847 def change_column_value(column, value)
2848   if assocs = model.autoreloading_associations[column]
2849     vals = @values
2850     if new?
2851       # Do deeper checking for new objects, so that associations are
2852       # not deleted when values do not change.  This code is run at
2853       # a higher level for existing objects.
2854       if value == (c = vals[column]) && value.class == c.class
2855         # If the value is the same, there is no reason to delete
2856         # the related associations, so exit early in that case.
2857         return super
2858       end
2859 
2860       only_delete_nil = c.nil?
2861     elsif vals[column].nil?
2862       only_delete_nil = true
2863     end
2864 
2865     if only_delete_nil
2866       # If the current foreign key value is nil, but the association
2867       # is already present in the cache, it was probably added to the
2868       # cache for a reason, and we do not want to delete it in that case.
2869       # However, we still want to delete associations with nil values
2870       # to remove the cached false negative.
2871       assocs.each{|a| associations.delete(a) if associations[a].nil?}
2872     else
2873       assocs.each{|a| associations.delete(a)}
2874     end
2875   end
2876   super
2877 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
2882 def ensure_associated_primary_key(opts, o, *args)
2883   if opts.need_associated_primary_key?
2884     o.save(:validate=>opts[:validate]) if o.new?
2885     raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk
2886   end
2887 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
2890 def initialize_copy(other)
2891   super
2892   @associations = Hash[@associations] if @associations
2893   self
2894 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
2907 def load_associated_objects(opts, dynamic_opts, &block)
2908   dynamic_opts = load_association_objects_options(dynamic_opts, &block)
2909   name = opts[:name]
2910   if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload]
2911     associations[name]
2912   else
2913     objs = _load_associated_objects(opts, dynamic_opts)
2914     if opts.set_reciprocal_to_self?
2915       if opts.returns_array?
2916         objs.each{|o| add_reciprocal_object(opts, o)}
2917       elsif objs
2918         add_reciprocal_object(opts, objs)
2919       end
2920     end
2921 
2922     # If the current object is frozen, you can't update the associations
2923     # cache.  This can cause issues for after_load procs that expect
2924     # the objects to be already cached in the associations, but
2925     # unfortunately that case cannot be handled.
2926     associations[name] = objs unless frozen?
2927     run_association_callbacks(opts, :after_load, objs)
2928     frozen? ? objs : associations[name]
2929   end
2930 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
2897 def load_association_objects_options(dynamic_opts, &block)
2898   if block
2899     dynamic_opts = Hash[dynamic_opts]
2900     dynamic_opts[:callback] = block
2901   end
2902 
2903   dynamic_opts
2904 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
2933 def load_with_primary_key_lookup?(opts, dynamic_opts)
2934   opts[:type] == :many_to_one &&
2935     !dynamic_opts[:callback] && 
2936     opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key}
2937 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
2943 def make_add_associated_object(opts, o)
2944   klass = opts.associated_class
2945 
2946   case o
2947   when Hash
2948     klass.new(o)
2949   when Integer, String, Array
2950     klass.with_pk!(o)
2951   when klass
2952     o
2953   else 
2954     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2955   end
2956 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
2959 def remove_all_associated_objects(opts, *args)
2960   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2961   # Allow calling private _remove_all method
2962   send(opts[:_remove_all_method], *args)
2963   ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
2964   associations[opts[:name]] = []
2965   ret
2966 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
2972 def remove_associated_object(opts, o, *args)
2973   klass = opts.associated_class
2974   if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
2975     o = remove_check_existing_object_from_pk(opts, o, *args)
2976   elsif !o.is_a?(klass)
2977     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2978   elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty?
2979     raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
2980   end
2981   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2982   raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
2983   return if run_association_callbacks(opts, :before_remove, o) == false
2984   # Allow calling private _remove method
2985   return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure?
2986   associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
2987   remove_reciprocal_object(opts, o)
2988   run_association_callbacks(opts, :after_remove, o)
2989   o
2990 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
2998 def remove_check_existing_object_from_pk(opts, o, *args)
2999   key = o
3000   pkh = opts.associated_class.qualified_primary_key_hash(key)
3001   raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh)
3002   o
3003 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
3006 def remove_reciprocal_object(opts, o)
3007   return unless reciprocal = opts.reciprocal
3008   if opts.reciprocal_array?
3009     if array = o.associations[reciprocal]
3010       array.delete_if{|x| self === x}
3011     end
3012   else
3013     o.associations[reciprocal] = nil
3014   end
3015 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
3018 def run_association_callbacks(reflection, callback_type, object)
3019   return unless cbs = reflection[callback_type]
3020 
3021   begin
3022     cbs.each do |cb|
3023       case cb
3024       when Symbol
3025         # Allow calling private methods in association callbacks
3026         send(cb, object)
3027       when Proc
3028         cb.call(self, object)
3029       else
3030         raise Error, "callbacks should either be Procs or Symbols"
3031       end
3032     end
3033   rescue HookFailed
3034     # The reason we automatically set raise_error for singular associations is that
3035     # assignment in ruby always returns the argument instead of the result of the
3036     # method, so we can't return nil to signal that the association callback prevented
3037     # the modification
3038     return false unless raise_on_save_failure || !reflection.returns_array?
3039     raise
3040   end
3041 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
3077 def set_associated_object(opts, o)
3078   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
3079   _set_associated_object(opts, o)
3080 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
3072 def set_associated_object_if_same?
3073   @set_associated_object_if_same
3074 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
3083 def set_one_through_one_associated_object(opts, o)
3084   raise(Error, "object #{inspect} does not have a primary key") unless pk
3085   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
3086   _set_associated_object(opts, o)
3087 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
3090 def set_one_to_one_associated_object(opts, o)
3091   raise(Error, "object #{inspect} does not have a primary key") unless pk
3092   _set_associated_object(opts, o)
3093 end