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
2465 def associations
2466   @associations ||= {}
2467 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
2472 def freeze
2473   associations
2474   super
2475   associations.freeze
2476   self
2477 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
2482 def _apply_association_options(opts, ds)
2483   unless ds.kind_of?(AssociationDatasetMethods)
2484     ds = opts.apply_dataset_changes(ds)
2485   end
2486   ds = ds.clone(:model_object => self)
2487   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2488   # block method is private
2489   ds = send(opts[:block_method], ds) if opts[:block_method]
2490   ds
2491 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
2494 def _associated_dataset(opts, dynamic_opts)
2495   ds = public_send(opts.dataset_method)
2496   if callback = dynamic_opts[:callback]
2497     ds = callback.call(ds)
2498   end
2499   ds
2500 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
2503 def _associated_object_loader(opts, dynamic_opts)
2504   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2505     loader
2506   end
2507 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2510 def _dataset(opts)
2511   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2512   ds = if opts[:dataset_opt_arity] == 1
2513     # dataset_opt_method is private
2514     send(opts[:dataset_opt_method], opts)
2515   else
2516     send(opts[:dataset_opt_method])
2517   end
2518   _apply_association_options(opts, ds)
2519 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
2522 def _join_table_dataset(opts)
2523   ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
2524   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2525 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
2529 def _load_associated_object(opts, dynamic_opts)
2530   _load_associated_object_array(opts, dynamic_opts).first
2531 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
2540 def _load_associated_object_array(opts, dynamic_opts)
2541   if loader = _associated_object_loader(opts, dynamic_opts)
2542     loader.all(*opts.predicate_key_values(self))
2543   else
2544     ds = _associated_dataset(opts, dynamic_opts)
2545     if ds.opts[:no_results]
2546       []
2547     else
2548       ds.all
2549     end
2550   end
2551 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
2534 def _load_associated_object_via_primary_key(opts)
2535   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2536 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
2555 def _load_associated_objects(opts, dynamic_opts=OPTS)
2556   if opts.can_have_associated_objects?(self)
2557     if opts.returns_array?
2558       _load_associated_object_array(opts, dynamic_opts)
2559     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2560       _load_associated_object_via_primary_key(opts)
2561     else
2562       _load_associated_object(opts, dynamic_opts)
2563     end
2564   elsif opts.returns_array?
2565     []
2566   end
2567 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

Calls superclass method
     # File lib/sequel/model/associations.rb
2570 def _refresh_set_values(hash)
2571   @associations.clear if @associations
2572   super
2573 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
2812 def _set_associated_object(opts, o)
2813   a = associations[opts[:name]]
2814   reciprocal = opts.reciprocal
2815   if set_associated_object_if_same?
2816     if reciprocal
2817       remove_reciprocal = a && (a != o || a.associations[reciprocal] != self)
2818       add_reciprocal = o && o.associations[reciprocal] != self
2819     end
2820   else
2821     return if a && a == o
2822     if reciprocal
2823       remove_reciprocal = a
2824       add_reciprocal = o
2825     end
2826   end
2827   run_association_callbacks(opts, :before_set, o)
2828   remove_reciprocal_object(opts, a) if remove_reciprocal
2829   # Allow calling private _setter method
2830   send(opts[:_setter_method], o)
2831   associations[opts[:name]] = o
2832   add_reciprocal_object(opts, o) if add_reciprocal
2833   run_association_callbacks(opts, :after_set, o)
2834   o
2835 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
2576 def add_associated_object(opts, o, *args)
2577   o = make_add_associated_object(opts, o)
2578   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2579   ensure_associated_primary_key(opts, o, *args)
2580   return if run_association_callbacks(opts, :before_add, o) == false
2581   # Allow calling private _add method
2582   return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure?
2583   if array = associations[opts[:name]] and !array.include?(o)
2584     array.push(o)
2585   end
2586   add_reciprocal_object(opts, o)
2587   run_association_callbacks(opts, :after_add, o)
2588   o
2589 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
2595 def add_reciprocal_object(opts, o)
2596   return if o.frozen?
2597   return unless reciprocal = opts.reciprocal
2598   if opts.reciprocal_array?
2599     if array = o.associations[reciprocal] and !array.include?(self)
2600       array.push(self)
2601     end
2602   else
2603     o.associations[reciprocal] = self
2604   end
2605 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
2609 def array_uniq!(a)
2610   a.uniq!
2611 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
2615 def change_column_value(column, value)
2616   if assocs = model.autoreloading_associations[column]
2617     vals = @values
2618     if new?
2619       # Do deeper checking for new objects, so that associations are
2620       # not deleted when values do not change.  This code is run at
2621       # a higher level for existing objects.
2622       if value == (c = vals[column]) && value.class == c.class
2623         # If the value is the same, there is no reason to delete
2624         # the related associations, so exit early in that case.
2625         return super
2626       end
2627 
2628       only_delete_nil = c.nil?
2629     elsif vals[column].nil?
2630       only_delete_nil = true
2631     end
2632 
2633     if only_delete_nil
2634       # If the current foreign key value is nil, but the association
2635       # is already present in the cache, it was probably added to the
2636       # cache for a reason, and we do not want to delete it in that case.
2637       # However, we still want to delete associations with nil values
2638       # to remove the cached false negative.
2639       assocs.each{|a| associations.delete(a) if associations[a].nil?}
2640     else
2641       assocs.each{|a| associations.delete(a)}
2642     end
2643   end
2644   super
2645 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
2650 def ensure_associated_primary_key(opts, o, *args)
2651   if opts.need_associated_primary_key?
2652     o.save(:validate=>opts[:validate]) if o.new?
2653     raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk
2654   end
2655 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
2658 def initialize_copy(other)
2659   super
2660   @associations = Hash[@associations] if @associations
2661   self
2662 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
2675 def load_associated_objects(opts, dynamic_opts, &block)
2676   dynamic_opts = load_association_objects_options(dynamic_opts, &block)
2677   name = opts[:name]
2678   if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload]
2679     associations[name]
2680   else
2681     objs = _load_associated_objects(opts, dynamic_opts)
2682     if opts.set_reciprocal_to_self?
2683       if opts.returns_array?
2684         objs.each{|o| add_reciprocal_object(opts, o)}
2685       elsif objs
2686         add_reciprocal_object(opts, objs)
2687       end
2688     end
2689 
2690     # If the current object is frozen, you can't update the associations
2691     # cache.  This can cause issues for after_load procs that expect
2692     # the objects to be already cached in the associations, but
2693     # unfortunately that case cannot be handled.
2694     associations[name] = objs unless frozen?
2695     run_association_callbacks(opts, :after_load, objs)
2696     frozen? ? objs : associations[name]
2697   end
2698 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
2665 def load_association_objects_options(dynamic_opts, &block)
2666   if block
2667     dynamic_opts = Hash[dynamic_opts]
2668     dynamic_opts[:callback] = block
2669   end
2670 
2671   dynamic_opts
2672 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
2701 def load_with_primary_key_lookup?(opts, dynamic_opts)
2702   opts[:type] == :many_to_one &&
2703     !dynamic_opts[:callback] && 
2704     opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key}
2705 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
2711 def make_add_associated_object(opts, o)
2712   klass = opts.associated_class
2713 
2714   case o
2715   when Hash
2716     klass.new(o)
2717   when Integer, String, Array
2718     klass.with_pk!(o)
2719   when klass
2720     o
2721   else 
2722     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2723   end
2724 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
2727 def remove_all_associated_objects(opts, *args)
2728   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2729   # Allow calling private _remove_all method
2730   send(opts[:_remove_all_method], *args)
2731   ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
2732   associations[opts[:name]] = []
2733   ret
2734 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
2740 def remove_associated_object(opts, o, *args)
2741   klass = opts.associated_class
2742   if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
2743     o = remove_check_existing_object_from_pk(opts, o, *args)
2744   elsif !o.is_a?(klass)
2745     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2746   elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty?
2747     raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
2748   end
2749   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2750   raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
2751   return if run_association_callbacks(opts, :before_remove, o) == false
2752   # Allow calling private _remove method
2753   return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure?
2754   associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
2755   remove_reciprocal_object(opts, o)
2756   run_association_callbacks(opts, :after_remove, o)
2757   o
2758 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
2766 def remove_check_existing_object_from_pk(opts, o, *args)
2767   key = o
2768   pkh = opts.associated_class.qualified_primary_key_hash(key)
2769   raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh)
2770   o
2771 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
2774 def remove_reciprocal_object(opts, o)
2775   return unless reciprocal = opts.reciprocal
2776   if opts.reciprocal_array?
2777     if array = o.associations[reciprocal]
2778       array.delete_if{|x| self === x}
2779     end
2780   else
2781     o.associations[reciprocal] = nil
2782   end
2783 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
2786 def run_association_callbacks(reflection, callback_type, object)
2787   return unless cbs = reflection[callback_type]
2788 
2789   begin
2790     cbs.each do |cb|
2791       case cb
2792       when Symbol
2793         # Allow calling private methods in association callbacks
2794         send(cb, object)
2795       when Proc
2796         cb.call(self, object)
2797       else
2798         raise Error, "callbacks should either be Procs or Symbols"
2799       end
2800     end
2801   rescue HookFailed
2802     # The reason we automatically set raise_error for singular associations is that
2803     # assignment in ruby always returns the argument instead of the result of the
2804     # method, so we can't return nil to signal that the association callback prevented
2805     # the modification
2806     return false unless raise_on_save_failure || !reflection.returns_array?
2807     raise
2808   end
2809 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
2845 def set_associated_object(opts, o)
2846   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2847   _set_associated_object(opts, o)
2848 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
2840 def set_associated_object_if_same?
2841   @set_associated_object_if_same
2842 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
2851 def set_one_through_one_associated_object(opts, o)
2852   raise(Error, "object #{inspect} does not have a primary key") unless pk
2853   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2854   _set_associated_object(opts, o)
2855 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
2858 def set_one_to_one_associated_object(opts, o)
2859   raise(Error, "object #{inspect} does not have a primary key") unless pk
2860   _set_associated_object(opts, o)
2861 end