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
2321 def associations
2322   @associations ||= {}
2323 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
2328 def freeze
2329   associations
2330   super
2331   associations.freeze
2332   self
2333 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
2338 def _apply_association_options(opts, ds)
2339   unless ds.kind_of?(AssociationDatasetMethods)
2340     ds = opts.apply_dataset_changes(ds)
2341   end
2342   ds = ds.clone(:model_object => self)
2343   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2344   # block method is private
2345   ds = send(opts[:block_method], ds) if opts[:block_method]
2346   ds
2347 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
2350 def _associated_dataset(opts, dynamic_opts)
2351   ds = public_send(opts.dataset_method)
2352   if callback = dynamic_opts[:callback]
2353     ds = callback.call(ds)
2354   end
2355   ds
2356 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
2359 def _associated_object_loader(opts, dynamic_opts)
2360   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2361     loader
2362   end
2363 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2366 def _dataset(opts)
2367   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2368   ds = if opts[:dataset_opt_arity] == 1
2369     # dataset_opt_method is private
2370     send(opts[:dataset_opt_method], opts)
2371   else
2372     send(opts[:dataset_opt_method])
2373   end
2374   _apply_association_options(opts, ds)
2375 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
2378 def _join_table_dataset(opts)
2379   ds = model.db.from(opts.join_table_source)
2380   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2381 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
2385 def _load_associated_object(opts, dynamic_opts)
2386   _load_associated_object_array(opts, dynamic_opts).first
2387 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
2396 def _load_associated_object_array(opts, dynamic_opts)
2397   if loader = _associated_object_loader(opts, dynamic_opts)
2398     loader.all(*opts.predicate_key_values(self))
2399   else
2400     _associated_dataset(opts, dynamic_opts).all
2401   end
2402 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
2390 def _load_associated_object_via_primary_key(opts)
2391   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2392 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
2406 def _load_associated_objects(opts, dynamic_opts=OPTS)
2407   if opts.can_have_associated_objects?(self)
2408     if opts.returns_array?
2409       _load_associated_object_array(opts, dynamic_opts)
2410     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2411       _load_associated_object_via_primary_key(opts)
2412     else
2413       _load_associated_object(opts, dynamic_opts)
2414     end
2415   elsif opts.returns_array?
2416     []
2417   end
2418 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

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