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

Return an association dataset for the given association reflection

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

Clear the associations cache when refreshing

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