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
2291 def associations
2292   @associations ||= {}
2293 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
2298 def freeze
2299   associations
2300   super
2301   associations.freeze
2302   self
2303 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
2308 def _apply_association_options(opts, ds)
2309   unless ds.kind_of?(AssociationDatasetMethods)
2310     ds = opts.apply_dataset_changes(ds)
2311   end
2312   ds = ds.clone(:model_object => self)
2313   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2314   # block method is private
2315   ds = send(opts[:block_method], ds) if opts[:block_method]
2316   ds
2317 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
2320 def _associated_dataset(opts, dynamic_opts)
2321   ds = public_send(opts.dataset_method)
2322   if callback = dynamic_opts[:callback]
2323     ds = callback.call(ds)
2324   end
2325   ds
2326 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
2329 def _associated_object_loader(opts, dynamic_opts)
2330   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2331     loader
2332   end
2333 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2336 def _dataset(opts)
2337   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2338   ds = if opts[:dataset_opt_arity] == 1
2339     # dataset_opt_method is private
2340     send(opts[:dataset_opt_method], opts)
2341   else
2342     send(opts[:dataset_opt_method])
2343   end
2344   _apply_association_options(opts, ds)
2345 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
2348 def _join_table_dataset(opts)
2349   ds = model.db.from(opts.join_table_source)
2350   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2351 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
2355 def _load_associated_object(opts, dynamic_opts)
2356   _load_associated_object_array(opts, dynamic_opts).first
2357 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
2366 def _load_associated_object_array(opts, dynamic_opts)
2367   if loader = _associated_object_loader(opts, dynamic_opts)
2368     loader.all(*opts.predicate_key_values(self))
2369   else
2370     _associated_dataset(opts, dynamic_opts).all
2371   end
2372 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
2360 def _load_associated_object_via_primary_key(opts)
2361   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2362 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
2376 def _load_associated_objects(opts, dynamic_opts=OPTS)
2377   if opts.can_have_associated_objects?(self)
2378     if opts.returns_array?
2379       _load_associated_object_array(opts, dynamic_opts)
2380     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2381       _load_associated_object_via_primary_key(opts)
2382     else
2383       _load_associated_object(opts, dynamic_opts)
2384     end
2385   elsif opts.returns_array?
2386     []
2387   end
2388 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

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