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
2262 def associations
2263   @associations ||= {}
2264 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
2269 def freeze
2270   associations
2271   super
2272   associations.freeze
2273   self
2274 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
2279 def _apply_association_options(opts, ds)
2280   unless ds.kind_of?(AssociationDatasetMethods)
2281     ds = opts.apply_dataset_changes(ds)
2282   end
2283   ds = ds.clone(:model_object => self)
2284   ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2285   ds = instance_exec(ds, &opts[:block]) if opts[:block]
2286   ds
2287 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
2290 def _associated_dataset(opts, dynamic_opts)
2291   ds = public_send(opts.dataset_method)
2292   if callback = dynamic_opts[:callback]
2293     ds = callback.call(ds)
2294   end
2295   ds
2296 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
2299 def _associated_object_loader(opts, dynamic_opts)
2300   if !dynamic_opts[:callback] && (loader = opts.placeholder_loader)
2301     loader
2302   end
2303 end
_dataset(opts) click to toggle source

Return an association dataset for the given association reflection

     # File lib/sequel/model/associations.rb
2306 def _dataset(opts)
2307   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2308   ds = if opts[:dataset].arity == 1
2309     instance_exec(opts, &opts[:dataset])
2310   else
2311     instance_exec(&opts[:dataset])
2312   end
2313   _apply_association_options(opts, ds)
2314 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
2317 def _join_table_dataset(opts)
2318   ds = model.db.from(opts.join_table_source)
2319   opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2320 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
2324 def _load_associated_object(opts, dynamic_opts)
2325   _load_associated_object_array(opts, dynamic_opts).first
2326 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
2335 def _load_associated_object_array(opts, dynamic_opts)
2336   if loader = _associated_object_loader(opts, dynamic_opts)
2337     loader.all(*opts.predicate_key_values(self))
2338   else
2339     _associated_dataset(opts, dynamic_opts).all
2340   end
2341 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
2329 def _load_associated_object_via_primary_key(opts)
2330   opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| get_column_value(c)} : get_column_value(fk)))
2331 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
2345 def _load_associated_objects(opts, dynamic_opts=OPTS)
2346   if opts.can_have_associated_objects?(self)
2347     if opts.returns_array?
2348       _load_associated_object_array(opts, dynamic_opts)
2349     elsif load_with_primary_key_lookup?(opts, dynamic_opts)
2350       _load_associated_object_via_primary_key(opts)
2351     else
2352       _load_associated_object(opts, dynamic_opts)
2353     end
2354   elsif opts.returns_array?
2355     []
2356   end
2357 end
_refresh_set_values(hash) click to toggle source

Clear the associations cache when refreshing

Calls superclass method
     # File lib/sequel/model/associations.rb
2360 def _refresh_set_values(hash)
2361   @associations.clear if @associations
2362   super
2363 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
2568 def _set_associated_object(opts, o)
2569   a = associations[opts[:name]]
2570   return if a && a == o && !set_associated_object_if_same?
2571   run_association_callbacks(opts, :before_set, o)
2572   remove_reciprocal_object(opts, a) if a
2573   # Allow calling private _setter method
2574   send(opts[:_setter_method], o)
2575   associations[opts[:name]] = o
2576   add_reciprocal_object(opts, o) if o
2577   run_association_callbacks(opts, :after_set, o)
2578   o
2579 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
2366 def add_associated_object(opts, o, *args)
2367   o = make_add_associated_object(opts, o)
2368   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2369   ensure_associated_primary_key(opts, o, *args)
2370   return if run_association_callbacks(opts, :before_add, o) == false
2371   # Allow calling private _add method
2372   return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure?
2373   if array = associations[opts[:name]] and !array.include?(o)
2374     array.push(o)
2375   end
2376   add_reciprocal_object(opts, o)
2377   run_association_callbacks(opts, :after_add, o)
2378   o
2379 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
2382 def add_reciprocal_object(opts, o)
2383   return if o.frozen?
2384   return unless reciprocal = opts.reciprocal
2385   if opts.reciprocal_array?
2386     if array = o.associations[reciprocal] and !array.include?(self)
2387       array.push(self)
2388     end
2389   else
2390     o.associations[reciprocal] = self
2391   end
2392 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
2396 def array_uniq!(a)
2397   a.uniq!
2398 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
2402 def change_column_value(column, value)
2403   if assocs = model.autoreloading_associations[column]
2404     assocs.each{|a| associations.delete(a)}
2405   end
2406   super
2407 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
2412 def ensure_associated_primary_key(opts, o, *args)
2413   if opts.need_associated_primary_key?
2414     o.save(:validate=>opts[:validate]) if o.new?
2415     raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk
2416   end
2417 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
2420 def initialize_copy(other)
2421   super
2422   @associations = Hash[@associations] if @associations
2423   self
2424 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
2437 def load_associated_objects(opts, dynamic_opts, &block)
2438   dynamic_opts = load_association_objects_options(dynamic_opts, &block)
2439   name = opts[:name]
2440   if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload]
2441     associations[name]
2442   else
2443     objs = _load_associated_objects(opts, dynamic_opts)
2444     if opts.set_reciprocal_to_self?
2445       if opts.returns_array?
2446         objs.each{|o| add_reciprocal_object(opts, o)}
2447       elsif objs
2448         add_reciprocal_object(opts, objs)
2449       end
2450     end
2451 
2452     # If the current object is frozen, you can't update the associations
2453     # cache.  This can cause issues for after_load procs that expect
2454     # the objects to be already cached in the associations, but
2455     # unfortunately that case cannot be handled.
2456     associations[name] = objs unless frozen?
2457     run_association_callbacks(opts, :after_load, objs)
2458     frozen? ? objs : associations[name]
2459   end
2460 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
2427 def load_association_objects_options(dynamic_opts, &block)
2428   if block
2429     dynamic_opts = Hash[dynamic_opts]
2430     dynamic_opts[:callback] = block
2431   end
2432 
2433   dynamic_opts
2434 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
2463 def load_with_primary_key_lookup?(opts, dynamic_opts)
2464   opts[:type] == :many_to_one &&
2465     !dynamic_opts[:callback] && 
2466     opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key}
2467 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
2473 def make_add_associated_object(opts, o)
2474   klass = opts.associated_class
2475 
2476   case o
2477   when Hash
2478     klass.new(o)
2479   when Integer, String, Array
2480     klass.with_pk!(o)
2481   when klass
2482     o
2483   else 
2484     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2485   end
2486 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
2489 def remove_all_associated_objects(opts, *args)
2490   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2491   # Allow calling private _remove_all method
2492   send(opts[:_remove_all_method], *args)
2493   ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
2494   associations[opts[:name]] = []
2495   ret
2496 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
2499 def remove_associated_object(opts, o, *args)
2500   klass = opts.associated_class
2501   if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
2502     o = remove_check_existing_object_from_pk(opts, o, *args)
2503   elsif !o.is_a?(klass)
2504     raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
2505   elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty?
2506     raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
2507   end
2508   raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2509   raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
2510   return if run_association_callbacks(opts, :before_remove, o) == false
2511   # Allow calling private _remove method
2512   return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure?
2513   associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
2514   remove_reciprocal_object(opts, o)
2515   run_association_callbacks(opts, :after_remove, o)
2516   o
2517 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
2522 def remove_check_existing_object_from_pk(opts, o, *args)
2523   key = o
2524   pkh = opts.associated_class.qualified_primary_key_hash(key)
2525   raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh)
2526   o
2527 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
2530 def remove_reciprocal_object(opts, o)
2531   return unless reciprocal = opts.reciprocal
2532   if opts.reciprocal_array?
2533     if array = o.associations[reciprocal]
2534       array.delete_if{|x| self === x}
2535     end
2536   else
2537     o.associations[reciprocal] = nil
2538   end
2539 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
2542 def run_association_callbacks(reflection, callback_type, object)
2543   return unless cbs = reflection[callback_type]
2544 
2545   begin
2546     cbs.each do |cb|
2547       case cb
2548       when Symbol
2549         # Allow calling private methods in association callbacks
2550         send(cb, object)
2551       when Proc
2552         cb.call(self, object)
2553       else
2554         raise Error, "callbacks should either be Procs or Symbols"
2555       end
2556     end
2557   rescue HookFailed
2558     # The reason we automatically set raise_error for singular associations is that
2559     # assignment in ruby always returns the argument instead of the result of the
2560     # method, so we can't return nil to signal that the association callback prevented
2561     # the modification
2562     return false unless raise_on_save_failure || !reflection.returns_array?
2563     raise
2564   end
2565 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
2589 def set_associated_object(opts, o)
2590   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2591   _set_associated_object(opts, o)
2592 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
2584 def set_associated_object_if_same?
2585   @set_associated_object_if_same
2586 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
2595 def set_one_through_one_associated_object(opts, o)
2596   raise(Error, "object #{inspect} does not have a primary key") unless pk
2597   raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
2598   _set_associated_object(opts, o)
2599 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
2602 def set_one_to_one_associated_object(opts, o)
2603   raise(Error, "object #{inspect} does not have a primary key") unless pk
2604   _set_associated_object(opts, o)
2605 end