Instance methods used to implement the associations support.
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, line 1442 def associations @associations ||= {} end
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.
# File lib/sequel/model/associations.rb, line 1449 def freeze associations.freeze super end
Formally used internally by the associations code, like pk but doesn't raise an Error if the model has no primary key. Not used any longer, deprecated.
# File lib/sequel/model/associations.rb, line 1462 def pk_or_nil key = primary_key key.is_a?(Array) ? key.map{|k| @values[k]} : @values[key] end
Clear the associations cache when refreshing
# File lib/sequel/model/associations.rb, line 1455 def set_values(hash) @associations.clear if @associations super end
Apply the association options such as :order and :limit to the given dataset, returning a modified dataset.
# File lib/sequel/model/associations.rb, line 1470 def _apply_association_options(opts, ds) unless ds.kind_of?(AssociationDatasetMethods) ds = opts.apply_dataset_changes(ds) end ds.model_object = self ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset? ds = instance_exec(ds, &opts[:block]) if opts[:block] ds end
Return a dataset for the association after applying any dynamic callback.
# File lib/sequel/model/associations.rb, line 1481 def _associated_dataset(opts, dynamic_opts) ds = send(opts.dataset_method) if callback = dynamic_opts[:callback] ds = callback.call(ds) end ds end
Return an association dataset for the given association reflection
# File lib/sequel/model/associations.rb, line 1490 def _dataset(opts) raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk ds = if opts[:dataset].arity == 1 instance_exec(opts, &opts[:dataset]) else instance_exec(&opts[:dataset]) end _apply_association_options(opts, ds) end
Dataset for the join table of the given many to many association reflection
# File lib/sequel/model/associations.rb, line 1501 def _join_table_dataset(opts) ds = model.db.from(opts.join_table_source) opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds end
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, line 1508 def _load_associated_object(opts, dynamic_opts) _load_associated_object_array(opts, dynamic_opts).first end
Load the associated objects for the given association reflection and dynamic options as an array.
# File lib/sequel/model/associations.rb, line 1514 def _load_associated_object_array(opts, dynamic_opts) _associated_dataset(opts, dynamic_opts).all end
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, line 1520 def _load_associated_objects(opts, dynamic_opts={}) if opts.can_have_associated_objects?(self) if opts.returns_array? _load_associated_object_array(opts, dynamic_opts) else _load_associated_object(opts, dynamic_opts) end elsif opts.returns_array? [] end end
Set the given object as the associated object for the given *_to_one association reflection
# File lib/sequel/model/associations.rb, line 1688 def _set_associated_object(opts, o) a = associations[opts[:name]] return if a && a == o && !set_associated_object_if_same? run_association_callbacks(opts, :before_set, o) remove_reciprocal_object(opts, a) if a send(opts._setter_method, o) associations[opts[:name]] = o add_reciprocal_object(opts, o) if o run_association_callbacks(opts, :after_set, o) o end
Add the given associated object to the given association
# File lib/sequel/model/associations.rb, line 1533 def add_associated_object(opts, o, *args) klass = opts.associated_class if o.is_a?(Hash) o = klass.new(o) elsif o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) o = klass[o] elsif !o.is_a?(klass) raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") end raise(Sequel::Error, "model object #{inspect} does not have a primary key") unless pk ensure_associated_primary_key(opts, o, *args) return if run_association_callbacks(opts, :before_add, o) == false send(opts._add_method, o, *args) if array = associations[opts[:name]] and !array.include?(o) array.push(o) end add_reciprocal_object(opts, o) run_association_callbacks(opts, :after_add, o) o end
Add/Set the current object to/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb, line 1555 def add_reciprocal_object(opts, o) return if o.frozen? return unless reciprocal = opts.reciprocal if opts.reciprocal_array? if array = o.associations[reciprocal] and !array.include?(self) array.push(self) end else o.associations[reciprocal] = self end end
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, line 1569 def array_uniq!(a) a.uniq! end
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, line 1576 def ensure_associated_primary_key(opts, o, *args) if opts.need_associated_primary_key? o.save(:validate=>opts[:validate]) if o.new? raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk end end
Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
# File lib/sequel/model/associations.rb, line 1584 def load_associated_objects(opts, dynamic_opts=nil) if dynamic_opts == true or dynamic_opts == false or dynamic_opts == nil dynamic_opts = {:reload=>dynamic_opts} elsif dynamic_opts.respond_to?(:call) dynamic_opts = {:callback=>dynamic_opts} end if block_given? dynamic_opts = dynamic_opts.merge(:callback=>Proc.new) end name = opts[:name] if associations.include?(name) and !dynamic_opts[:callback] and !dynamic_opts[:reload] associations[name] else objs = _load_associated_objects(opts, dynamic_opts) if opts.set_reciprocal_to_self? if opts.returns_array? objs.each{|o| add_reciprocal_object(opts, o)} elsif objs add_reciprocal_object(opts, objs) end end # If the current object is frozen, you can't update the associations # cache. This can cause issues for after_load procs that expect # the objects to be already cached in the associations, but # unfortunately that case cannot be handled. associations[name] = objs unless frozen? run_association_callbacks(opts, :after_load, objs) frozen? ? objs : associations[name] end end
Remove all associated objects from the given association
# File lib/sequel/model/associations.rb, line 1617 def remove_all_associated_objects(opts, *args) raise(Sequel::Error, "model object #{inspect} does not have a primary key") unless pk send(opts._remove_all_method, *args) ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name]) associations[opts[:name]] = [] ret end
Remove the given associated object from the given association
# File lib/sequel/model/associations.rb, line 1626 def remove_associated_object(opts, o, *args) klass = opts.associated_class if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array) o = remove_check_existing_object_from_pk(opts, o, *args) elsif !o.is_a?(klass) raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}") elsif opts.remove_should_check_existing? && send(opts.dataset_method).where(o.pk_hash).empty? raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}") end raise(Sequel::Error, "model object #{inspect} does not have a primary key") unless pk raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk return if run_association_callbacks(opts, :before_remove, o) == false send(opts._remove_method, o, *args) associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name]) remove_reciprocal_object(opts, o) run_association_callbacks(opts, :after_remove, o) o end
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, line 1648 def remove_check_existing_object_from_pk(opts, o, *args) key = o pkh = opts.associated_class.qualified_primary_key_hash(key) raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = send(opts.dataset_method).first(pkh) o end
Remove/unset the current object from/as the given object's reciprocal association.
# File lib/sequel/model/associations.rb, line 1656 def remove_reciprocal_object(opts, o) return unless reciprocal = opts.reciprocal if opts.reciprocal_array? if array = o.associations[reciprocal] array.delete_if{|x| self === x} end else o.associations[reciprocal] = nil end end
Run the callback for the association with the object.
# File lib/sequel/model/associations.rb, line 1668 def run_association_callbacks(reflection, callback_type, object) raise_error = raise_on_save_failure || !reflection.returns_array? stop_on_false = [:before_add, :before_remove, :before_set].include?(callback_type) reflection[callback_type].each do |cb| res = case cb when Symbol send(cb, object) when Proc cb.call(self, object) else raise Error, "callbacks should either be Procs or Symbols" end if res == false and stop_on_false raise(BeforeHookFailed, "Unable to modify association for #{inspect}: one of the #{callback_type} hooks returned false") if raise_error return false end end end
Set the given object as the associated object for the given many_to_one association reflection
# File lib/sequel/model/associations.rb, line 1708 def set_associated_object(opts, o) raise(Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk _set_associated_object(opts, o) end
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, line 1703 def set_associated_object_if_same? @set_associated_object_if_same end
Set the given object as the associated object for the given one_to_one association reflection
# File lib/sequel/model/associations.rb, line 1714 def set_one_to_one_associated_object(opts, o) raise(Error, "object #{inspect} does not have a primary key") unless pk _set_associated_object(opts, o) end