module Mongoid::Association::Relatable

This module provides behaviors shared between Association types.

@since 7.0

Constants

PRIMARY_KEY_DEFAULT

The primary key default.

@return [ String ] The primary key field default.

@since 7.0

SHARED_OPTIONS

The options shared between all association types.

@return [ Array<Symbol> ] The shared options.

@since 7.0

Attributes

name[R]

The name of the association.

@return [ Symbol ] The name of the association.

@since 7.0

options[R]

The options on this association.

@return [ Hash ] The options.

@since 7.0

Public Class Methods

new(_class, name, opts = {}, &block) click to toggle source

Initialize the Association.

@param [ Class ] _class The class of the model who owns this association. @param [ Symbol ] name The name of the association. @param [ Hash ] opts The association options. @param [ Block ] block The optional block.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 58
def initialize(_class, name, opts = {}, &block)
  @owner_class = _class
  @name = name
  @options = opts
  @extension = nil

  @module_path = _class.name ? _class.name.split('::')[0..-2].join('::') : ''
  @module_path << '::' unless @module_path.empty?

  create_extension!(&block)
  validate!
end

Public Instance Methods

==(other) click to toggle source

Compare this association to another.

@return [ Object ] The object to compare to this association.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 76
def ==(other)
  relation_class_name == other.relation_class_name &&
    inverse_class_name == other.inverse_class_name &&
      name == other.name &&
        options == other.options
end
bindable?(doc) click to toggle source

Whether trying to bind an object using this association should raise an error.

@param [ Document ] doc The document to be bound.

@return [ true, false ] Whether the document can be bound.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 113
def bindable?(doc); false; end
class_name()
Alias for: relation_class_name
counter_cache_column_name() click to toggle source

Get the counter cache column name.

@return [ String ] The counter cache column name.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 324
def counter_cache_column_name
  @counter_cache_column_name ||= (@options[:counter_cache].is_a?(String) ||
      @options[:counter_cache].is_a?(Symbol)) ?
      @options[:counter_cache] : "#{inverse || inverse_class_name.demodulize.underscore.pluralize}_count"
end
create_relation(owner, target) click to toggle source

Create an association proxy object using the owner and target.

@param [ Document ] owner The document this association hangs off of. @param [ Document, Array<Document> ] target The target (parent) of the

association.

@return [ Proxy ]

@since 7.0

# File lib/mongoid/association/relatable.rb, line 306
def create_relation(owner, target)
  relation.new(owner, target, self)
end
destructive?() click to toggle source

Whether the dependent method is destructive.

@return [ true, false ] If the dependent method is destructive.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 315
def destructive?
  @destructive ||= !!(dependent && (dependent == :delete_all || dependent == :destroy))
end
extension() click to toggle source

Get the extension.

@return [ Module ] The extension module, if one has been defined.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 335
def extension
  @extension ||= @options[:extend]
end
foreign_key_check() click to toggle source

Get the name of the method to check if the foreign key has changed.

@example Get the foreign key check method.

association.foreign_key_check

@return [ String ] The foreign key check.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 293
def foreign_key_check
  @foreign_key_check ||= "#{foreign_key}_changed?" if (stores_foreign_key? && foreign_key)
end
foreign_key_setter() click to toggle source

The name of the foreign key setter method.

@return [ String ] The name of the foreign key setter.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 257
def foreign_key_setter
  # note: You can't check if this association stores foreign key
  # See HasOne and HasMany binding, they referenced foreign_key_setter
  @foreign_key_setter ||= "#{foreign_key}=" if foreign_key
end
get_callbacks(callback_type) click to toggle source

Get the callbacks for a given type.

@param [ Symbol ] callback_type The type of callback type.

@return [ Array<Proc, Symbol> ] A list of the callbacks, either method

names or Procs.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 91
def get_callbacks(callback_type)
  Array(options[callback_type])
end
inverse(other = nil) click to toggle source

Get the inverse name.

@return [ Symbol ] The inverse name.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 344
def inverse(other = nil)
  candidates = inverses(other)
  candidates.detect { |c| c } if candidates
end
inverse_association(other = nil) click to toggle source

Get the inverse’s association metadata.

@param [ Object ] other The other model class or model object to use when

determining inverses.

@return [ Association ] The inverse’s association metadata.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 142
def inverse_association(other = nil)
  (other || relation_class).relations[inverse(other)]
end
inverse_class() click to toggle source

The class of the object owning this association.

@return [ String ] The owning objects’ class.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 219
def inverse_class
  @owner_class
end
Also aliased as: inverse_klass
inverse_class_name() click to toggle source

The class name of the object owning this association.

@return [ String ] The owning objects’ class name.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 210
def inverse_class_name
  @inverse_class_name ||= @owner_class.name
end
inverse_klass()
Alias for: inverse_class
inverse_setter(other = nil) click to toggle source

The name of the inverse setter method.

@return [ String ] The name of the inverse setter.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 248
def inverse_setter(other = nil)
  @inverse_setter ||= "#{inverses(other).first}=" unless inverses(other).blank?
end
inverse_type() click to toggle source

Get the inverse type.

@return [ nil ] Default is nil for an association.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 151
def inverse_type; end
inverse_type_setter() click to toggle source

Gets the setter for the field that sets the type of document on a polymorphic association.

@example Get the inverse type setter.

association.inverse_type_setter

@return [ String ] The name of the setter.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 281
def inverse_type_setter
  @inverse_type_setter ||= inverse_type.__setter__
end
inverses(other = nil) click to toggle source

Get the inverse names.

@param [ Object ] other The other model class or model object to use when

determining inverses.

@return [ Array<Symbol> ] The list of inverse names.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 123
def inverses(other = nil)
  return [ inverse_of ] if inverse_of
  return [] if @options.key?(:inverse_of) && !inverse_of

  if polymorphic?
    polymorphic_inverses(other)
  else
    determine_inverses(other)
  end
end
key() click to toggle source

The foreign key field if this association stores a foreign key. Otherwise, the primary key.

@return [ Symbol, String ] The primary key.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 230
def key
  stores_foreign_key? ? foreign_key : primary_key
end
klass()
Alias for: relation_class
path(document) click to toggle source

The atomic path for this association.

@return [ Mongoid::Atomic::Paths::Root ] The atomic path object.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 268
def path(document)
  relation.path(document)
end
relation_class() click to toggle source

The class of the association object(s).

This method returns the class instance corresponding to relation_class_name, resolved relative to the host document class.

If the class does not exist, this method raises NameError. This can happen because the target class has not yet been defined. Note that polymorphic associations generally do not have a well defined target class because the target class can change from one object to another, and calling this method on a polymorphic association will generally fail with a NameError or produce misleading results (if a class does happen to be defined with the same name as the association name).

@return [ String ] The association objects’ class.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 197
def relation_class
  @klass ||= begin
    cls_name = @options[:class_name] || ActiveSupport::Inflector.classify(name)
    resolve_name(inverse_class, cls_name)
  end
end
Also aliased as: klass
relation_class_name() click to toggle source
The class name, possibly unqualified or

prefixed, of the association

object(s).

This method returns the class name as it is used in the association definition. If :class_name option is given in the association, the exact value of that option is returned here. If :class_name option is not given, the name of the class is calculated from association name but is not resolved to the actual class.

The class name returned by this method may not correspond to a defined class, either because the corresponding class has not been loaded yet, or because the association references a non-existent class altogether. To obtain the association class, use relation_class method.

@note The return value of this method should not be used to determine

whether two associations have the same target class, because the
return value is not always a fully qualified class name. To compare
classes, retrieve the class instance of the association target using
the +relation_class+ method.

@return [ String ] The association objects’ class name.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 176
def relation_class_name
  @class_name ||= @options[:class_name] || ActiveSupport::Inflector.classify(name)
end
Also aliased as: class_name
setter() click to toggle source

The name of the setter on this object for assigning an associated object.

@return [ String ] The setter name.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 239
def setter
  @setter ||= "#{name}="
end
type_setter() click to toggle source

Get the type setter. @note Only relevant for polymorphic associations that take the :as option.

@return [ String ] The type setter method.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 101
def type_setter
  @type_setter ||= type.__setter__
end
validate?() click to toggle source

Whether the associated object(s) should be validated.

@return [ true, false ] If the associated object(s)

should be validated.

@since 7.0

# File lib/mongoid/association/relatable.rb, line 355
def validate?
  @validate ||= if @options[:validate].nil?
                  validation_default
                else
                  !!@options[:validate]
                end
end

Private Instance Methods

create_extension!(&block) click to toggle source
# File lib/mongoid/association/relatable.rb, line 447
def create_extension!(&block)
  if block
    extension_module_name = "#{@owner_class.to_s.demodulize}#{name.to_s.camelize}RelationExtension"
    silence_warnings do
      @owner_class.const_set(extension_module_name, Module.new(&block))
    end
    @extension = "#{@owner_class}::#{extension_module_name}".constantize
  end
end
default_inverse() click to toggle source
# File lib/mongoid/association/relatable.rb, line 457
def default_inverse
  @default_inverse ||= klass.relations[inverse_klass.name.underscore]
end
define_autosaver!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 381
def define_autosaver!
  if autosave?
    Association::Referenced::AutoSave.define_autosave!(self)
  end
end
define_builder!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 387
def define_builder!
  Association::Builders.define_builder!(self)
end
define_counter_cache_callbacks!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 415
def define_counter_cache_callbacks!
  if counter_cached?
    Association::Referenced::CounterCache.define_callbacks!(self)
  end
end
define_creator!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 391
def define_creator!
  Association::Builders.define_creator!(self)
end
define_dependency!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 421
def define_dependency!
  if dependent
    Association::Depending.define_dependency!(self)
  end
end
define_existence_check!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 403
def define_existence_check!
  Association::Accessors.define_existence_check!(self)
end
define_getter!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 395
def define_getter!
  Association::Accessors.define_getter!(self)
end
define_ids_getter!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 407
def define_ids_getter!
  Association::Accessors.define_ids_getter!(self)
end
define_ids_setter!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 411
def define_ids_setter!
  Association::Accessors.define_ids_setter!(self)
end
define_setter!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 399
def define_setter!
  Association::Accessors.define_setter!(self)
end
define_touchable!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 375
def define_touchable!
  if touchable?
    Touchable.define_touchable!(self)
  end
end
inverse_association_classes() click to toggle source

Gets the model classes with inverse associations of this model. This is used to determine the classes on the other end of polymorphic associations with models.

# File lib/mongoid/association/relatable.rb, line 367
def inverse_association_classes
  Mongoid::Config.models.map { |m| inverse_association(m) }.compact.map(&:inverse_class)
end
namespace_hierarchy(mod) click to toggle source

Returns an array of classes/modules forming the namespace hierarchy where symbols referenced in the provided class/module would be looked up by Ruby. For example, if mod is Foo::Bar, this method would return [Foo::Bar, Foo, Object].

# File lib/mongoid/association/relatable.rb, line 465
def namespace_hierarchy(mod)
  parent = Object
  hier = [parent]
  mod.name.split('::').each do |part|
    parent = parent.const_get(part)
    hier << parent
  end
  hier.reverse
end
polymorph!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 441
def polymorph!
  if polymorphic?
    @owner_class.polymorphic = true
  end
end
resolve_name(mod, name) click to toggle source

Resolves the given class/module name in the context of the specified module, as Ruby would when a constant is referenced in the source.

@note This method can swallow exceptions produced during class loading,

because it rescues NameError internally. Since this method attempts
to load classes, failure during the loading process may also lead to
there being incomplete class definitions.
# File lib/mongoid/association/relatable.rb, line 482
def resolve_name(mod, name)
  cls = exc = nil
  parts = name.to_s.split('::')
  if parts.first == ''
    parts.shift
    hierarchy = [Object]
  else
    hierarchy = namespace_hierarchy(mod)
  end
  hierarchy.each do |ns|
    begin
      parts.each do |part|
        # Simple const_get sometimes pulls names out of weird scopes,
        # perhaps confusing the receiver (ns in this case) with the
        # local scope. Walk the class hierarchy ourselves one node
        # at a time by specifying false as the second argument.
        ns = ns.const_get(part, false)
      end
      cls = ns
      break
    rescue NameError => e
      if exc.nil?
        exc = e
      end
    end
  end
  if cls.nil?
    # Raise the first exception, this is from the most specific namespace
    raise exc
  end
  cls
end
setup_index!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 371
def setup_index!
  @owner_class.index(index_spec, background: true) if indexed?
end
validate!() click to toggle source
# File lib/mongoid/association/relatable.rb, line 427
def validate!
  @options.keys.each do |opt|
    unless self.class::VALID_OPTIONS.include?(opt)
      raise Errors::InvalidRelationOption.new(@owner_class, name, opt, self.class::VALID_OPTIONS)
    end
  end

  [name, "#{name}?".to_sym, "#{name}=".to_sym].each do |n|
    if Mongoid.destructive_fields.include?(n)
      raise Errors::InvalidRelation.new(@owner_class, n)
    end
  end
end