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

Return an association dataset for the given association reflection

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

Clear the associations cache when refreshing

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