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
2324 def associations
2325   @associations ||= {}
2326 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
2331 def freeze
2332   associations
2333   super
2334   associations.freeze
2335   self
2336 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
2341 def _apply_association_options(opts, ds)
2342   unless ds.kind_of?(AssociationDatasetMethods)
2343     ds = opts.apply_dataset_changes(ds)
2344   end
2345   ds = ds.clone(:model_object => self)
2346   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2347   # block method is private
2348   ds = send(opts[:block_method], ds) if opts[:block_method]
2349   ds
2350 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
2353 def _associated_dataset(opts, dynamic_opts)
2354   ds = public_send(opts.dataset_method)
2355   if callback = dynamic_opts[:callback]
2356     ds = callback.call(ds)
2357   end
2358   ds
2359 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
2362 def _associated_object_loader(opts, dynamic_opts)
2363   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2364     loader
2365   end
2366 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2369 def _dataset(opts)
2370   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2371   ds = if opts[:dataset_opt_arity] == 1
2372     # dataset_opt_method is private
2373     send(opts[:dataset_opt_method], opts)
2374   else
2375     send(opts[:dataset_opt_method])
2376   end
2377   _apply_association_options(opts, ds)
2378 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
2381 def _join_table_dataset(opts)
2382   ds = model.db.from(opts.join_table_source)
2383   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2384 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
2388 def _load_associated_object(opts, dynamic_opts)
2389   _load_associated_object_array(opts, dynamic_opts).first
2390 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
2399 def _load_associated_object_array(opts, dynamic_opts)
2400   if loader = _associated_object_loader(opts, dynamic_opts)
2401     loader.all(*opts.predicate_key_values(self))
2402   else
2403     _associated_dataset(opts, dynamic_opts).all
2404   end
2405 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
2393 def _load_associated_object_via_primary_key(opts)
2394   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2395 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
2409 def _load_associated_objects(opts, dynamic_opts=OPTS)
2410   if opts.can_have_associated_objects?(self)
2411     if opts.returns_array?
2412       _load_associated_object_array(opts, dynamic_opts)
2413     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2414       _load_associated_object_via_primary_key(opts)
2415     else
2416       _load_associated_object(opts, dynamic_opts)
2417     end
2418   elsif opts.returns_array?
2419     []
2420   end
2421 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

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