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
2306 def associations
2307   @associations ||= {}
2308 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
2313 def freeze
2314   associations
2315   super
2316   associations.freeze
2317   self
2318 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
2323 def _apply_association_options(opts, ds)
2324   unless ds.kind_of?(AssociationDatasetMethods)
2325     ds = opts.apply_dataset_changes(ds)
2326   end
2327   ds = ds.clone(:model_object => self)
2328   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2329   # block method is private
2330   ds = send(opts[:block_method], ds) if opts[:block_method]
2331   ds
2332 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
2335 def _associated_dataset(opts, dynamic_opts)
2336   ds = public_send(opts.dataset_method)
2337   if callback = dynamic_opts[:callback]
2338     ds = callback.call(ds)
2339   end
2340   ds
2341 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
2344 def _associated_object_loader(opts, dynamic_opts)
2345   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2346     loader
2347   end
2348 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2351 def _dataset(opts)
2352   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2353   ds = if opts[:dataset_opt_arity] == 1
2354     # dataset_opt_method is private
2355     send(opts[:dataset_opt_method], opts)
2356   else
2357     send(opts[:dataset_opt_method])
2358   end
2359   _apply_association_options(opts, ds)
2360 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
2363 def _join_table_dataset(opts)
2364   ds = model.db.from(opts.join_table_source)
2365   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2366 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
2370 def _load_associated_object(opts, dynamic_opts)
2371   _load_associated_object_array(opts, dynamic_opts).first
2372 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
2381 def _load_associated_object_array(opts, dynamic_opts)
2382   if loader = _associated_object_loader(opts, dynamic_opts)
2383     loader.all(*opts.predicate_key_values(self))
2384   else
2385     _associated_dataset(opts, dynamic_opts).all
2386   end
2387 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
2375 def _load_associated_object_via_primary_key(opts)
2376   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2377 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
2391 def _load_associated_objects(opts, dynamic_opts=OPTS)
2392   if opts.can_have_associated_objects?(self)
2393     if opts.returns_array?
2394       _load_associated_object_array(opts, dynamic_opts)
2395     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2396       _load_associated_object_via_primary_key(opts)
2397     else
2398       _load_associated_object(opts, dynamic_opts)
2399     end
2400   elsif opts.returns_array?
2401     []
2402   end
2403 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

Calls superclass method
     # File lib/sequel/model/associations.rb
2406 def _refresh_set_values(hash)
2407   @associations.clear if @associations
2408   super
2409 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
2639 def _set_associated_object(opts, o)
2640   a = associations[opts[:name]]
2641   reciprocal = opts.reciprocal
2642   if set_associated_object_if_same?
2643     if reciprocal
2644       remove_reciprocal = a && (a != o || a.associations[reciprocal] != self)
2645       add_reciprocal = o && o.associations[reciprocal] != self
2646     end
2647   else
2648     return if a && a == o
2649     if reciprocal
2650       remove_reciprocal = a
2651       add_reciprocal = o
2652     end
2653   end
2654   run_association_callbacks(opts, :before_set, o)
2655   remove_reciprocal_object(opts, a) if remove_reciprocal
2656   # Allow calling private _setter method
2657   send(opts[:_setter_method], o)
2658   associations[opts[:name]] = o
2659   add_reciprocal_object(opts, o) if add_reciprocal
2660   run_association_callbacks(opts, :after_set, o)
2661   o
2662 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
2412 def add_associated_object(opts, o, *args)
2413   o = make_add_associated_object(opts, o)
2414   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2415   ensure_associated_primary_key(opts, o, *args)
2416   return if run_association_callbacks(opts, :before_add, o) == false
2417   # Allow calling private _add method
2418   return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure?
2419   if array = associations[opts[:name]] and !array.include?(o)
2420     array.push(o)
2421   end
2422   add_reciprocal_object(opts, o)
2423   run_association_callbacks(opts, :after_add, o)
2424   o
2425 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
2428 def add_reciprocal_object(opts, o)
2429   return if o.frozen?
2430   return unless reciprocal = opts.reciprocal
2431   if opts.reciprocal_array?
2432     if array = o.associations[reciprocal] and !array.include?(self)
2433       array.push(self)
2434     end
2435   else
2436     o.associations[reciprocal] = self
2437   end
2438 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
2442 def array_uniq!(a)
2443   a.uniq!
2444 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
2448 def change_column_value(column, value)
2449   if assocs = model.autoreloading_associations[column]
2450     vals = @values
2451     if new?
2452       # Do deeper checking for new objects, so that associations are
2453       # not deleted when values do not change.  This code is run at
2454       # a higher level for existing objects.
2455       if value == (c = vals[column]) && value.class == c.class
2456         # If the value is the same, there is no reason to delete
2457         # the related associations, so exit early in that case.
2458         return super
2459       end
2460 
2461       only_delete_nil = c.nil?
2462     elsif vals[column].nil?
2463       only_delete_nil = true
2464     end
2465 
2466     if only_delete_nil
2467       # If the current foreign key value is nil, but the association
2468       # is already present in the cache, it was probably added to the
2469       # cache for a reason, and we do not want to delete it in that case.
2470       # However, we still want to delete associations with nil values
2471       # to remove the cached false negative.
2472       assocs.each{|a| associations.delete(a) if associations[a].nil?}
2473     else
2474       assocs.each{|a| associations.delete(a)}
2475     end
2476   end
2477   super
2478 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
2483 def ensure_associated_primary_key(opts, o, *args)
2484   if opts.need_associated_primary_key?
2485     o.save(:validate=>opts[:validate]) if o.new?
2486     raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk
2487   end
2488 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
2491 def initialize_copy(other)
2492   super
2493   @associations = Hash[@associations] if @associations
2494   self
2495 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
2508 def load_associated_objects(opts, dynamic_opts, &block)
2509   dynamic_opts = load_association_objects_options(dynamic_opts, &block)
2510   name = opts[:name]
2511   if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload]
2512     associations[name]
2513   else
2514     objs = _load_associated_objects(opts, dynamic_opts)
2515     if opts.set_reciprocal_to_self?
2516       if opts.returns_array?
2517         objs.each{|o| add_reciprocal_object(opts, o)}
2518       elsif objs
2519         add_reciprocal_object(opts, objs)
2520       end
2521     end
2522 
2523     # If the current object is frozen, you can't update the associations
2524     # cache.  This can cause issues for after_load procs that expect
2525     # the objects to be already cached in the associations, but
2526     # unfortunately that case cannot be handled.
2527     associations[name] = objs unless frozen?
2528     run_association_callbacks(opts, :after_load, objs)
2529     frozen? ? objs : associations[name]
2530   end
2531 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
2498 def load_association_objects_options(dynamic_opts, &block)
2499   if block
2500     dynamic_opts = Hash[dynamic_opts]
2501     dynamic_opts[:callback] = block
2502   end
2503 
2504   dynamic_opts
2505 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
2534 def load_with_primary_key_lookup?(opts, dynamic_opts)
2535   opts[:type] == :many_to_one &&
2536     !dynamic_opts[:callback] && 
2537     opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key}
2538 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
2544 def make_add_associated_object(opts, o)
2545   klass = opts.associated_class
2546 
2547   case o
2548   when Hash
2549     klass.new(o)
2550   when Integer, String, Array
2551     klass.with_pk!(o)
2552   when klass
2553     o
2554   else 
2555     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2556   end
2557 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
2560 def remove_all_associated_objects(opts, *args)
2561   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2562   # Allow calling private _remove_all method
2563   send(opts[:_remove_all_method], *args)
2564   ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
2565   associations[opts[:name]] = []
2566   ret
2567 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
2570 def remove_associated_object(opts, o, *args)
2571   klass = opts.associated_class
2572   if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
2573     o = remove_check_existing_object_from_pk(opts, o, *args)
2574   elsif !o.is_a?(klass)
2575     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2576   elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty?
2577     raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
2578   end
2579   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2580   raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
2581   return if run_association_callbacks(opts, :before_remove, o) == false
2582   # Allow calling private _remove method
2583   return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure?
2584   associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
2585   remove_reciprocal_object(opts, o)
2586   run_association_callbacks(opts, :after_remove, o)
2587   o
2588 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
2593 def remove_check_existing_object_from_pk(opts, o, *args)
2594   key = o
2595   pkh = opts.associated_class.qualified_primary_key_hash(key)
2596   raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh)
2597   o
2598 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
2601 def remove_reciprocal_object(opts, o)
2602   return unless reciprocal = opts.reciprocal
2603   if opts.reciprocal_array?
2604     if array = o.associations[reciprocal]
2605       array.delete_if{|x| self === x}
2606     end
2607   else
2608     o.associations[reciprocal] = nil
2609   end
2610 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
2613 def run_association_callbacks(reflection, callback_type, object)
2614   return unless cbs = reflection[callback_type]
2615 
2616   begin
2617     cbs.each do |cb|
2618       case cb
2619       when Symbol
2620         # Allow calling private methods in association callbacks
2621         send(cb, object)
2622       when Proc
2623         cb.call(self, object)
2624       else
2625         raise Error, "callbacks should either be Procs or Symbols"
2626       end
2627     end
2628   rescue HookFailed
2629     # The reason we automatically set raise_error for singular associations is that
2630     # assignment in ruby always returns the argument instead of the result of the
2631     # method, so we can't return nil to signal that the association callback prevented
2632     # the modification
2633     return false unless raise_on_save_failure || !reflection.returns_array?
2634     raise
2635   end
2636 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
2672 def set_associated_object(opts, o)
2673   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2674   _set_associated_object(opts, o)
2675 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
2667 def set_associated_object_if_same?
2668   @set_associated_object_if_same
2669 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
2678 def set_one_through_one_associated_object(opts, o)
2679   raise(Error, "object #{inspect} does not have a primary key") unless pk
2680   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2681   _set_associated_object(opts, o)
2682 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
2685 def set_one_to_one_associated_object(opts, o)
2686   raise(Error, "object #{inspect} does not have a primary key") unless pk
2687   _set_associated_object(opts, o)
2688 end