123456789_123456789_123456789_123456789_123456789_

Module: Mongoid::Association::Relatable

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Defined in: lib/mongoid/association/relatable.rb

Overview

This module provides behaviors shared between ::Mongoid::Association types.

Constant Summary

Instance Attribute Summary

Options - Included

#autobuilding?

Whether the association is autobuilding.

#autosave

Options to save any loaded members and destroy members that are marked for destruction when the parent object is saved.

#autosave?

Alias for Options#autosave.

#cascading_callbacks?

Whether the association has callbacks cascaded down from the parent.

#counter_cached?

Whether the association is counter-cached.

#cyclic?

Is the association cyclic.

#forced_nil_inverse?

Whether the association has forced nil inverse (So no foreign keys are saved).

#indexed?

Whether to index the primary or foreign key field.

#polymorphic?

Whether this association is polymorphic.

#touchable?

Whether the association object should be automatically touched when its inverse object is updated.

Instance Method Summary

Options - Included

#as

Returns the name of the parent to a polymorphic child.

#dependent

Specify what happens to the associated object when the owner is destroyed.

#inverse_of

The name the owning object uses to refer to this association.

#order

The custom sorting options on the association.

#primary_key

::Mongoid assumes that the field used to hold the primary key of the association is id.

#store_as

The store_as option.

#touch_field

The field for saving the associated object’s type.

#type

The field for saving the associated object’s type.

Constrainable - Included

#convert_to_foreign_key

Convert the supplied object to the appropriate type to set as the foreign key for an association.

#convert_polymorphic

Instance Attribute Details

#destructive?true | false (readonly)

Whether the dependent method is destructive.

Returns:

  • (true | false)

    If the dependent method is destructive.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 268

def destructive?
  @destructive ||= !!(dependent && (dependent == :delete_all || dependent == :destroy))
end

#in_to?true | false (readonly)

Is this association an embedded_in or belongs_to association?

Returns:

  • (true | false)

    true if it is an embedded_in or belongs_to association, false if not.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 338

def in_to?
  [Referenced::BelongsTo, Embedded::EmbeddedIn].any? { |a| self.is_a?(a) }
end

#many?true | false (readonly)

Is this association an embeds_many or has_many association?

Returns:

  • (true | false)

    true if it is a *_many association, false if not.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 323

def many?
  [Referenced::HasMany, Embedded::EmbedsMany].any? { |a| self.is_a?(a) }
end

#nameSymbol (readonly)

The name of the association.

Returns:

  • (Symbol)

    The name of the association.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 33

attr_reader :name

#one?true | false (readonly)

Is this association an embeds_one or has_one association?

Returns:

  • (true | false)

    true if it is a *_one association, false if not.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 330

def one?
  [Referenced::HasOne, Embedded::EmbedsOne].any? { |a| self.is_a?(a) }
end

#optionsHash (readonly)

The options on this association.

Returns:

  • (Hash)

    The options.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 38

attr_reader :options

#owner_classClass (readonly)

The class that owns this association.

Returns:

  • (Class)

    The owner class.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 43

attr_reader :owner_class

#parent_inclusionsArray<String> (rw)

The associations above this one in the inclusion tree.

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 316

def parent_inclusions
  @parent_inclusions ||= []
end

#parent_inclusions=(value) ⇒ Array<String> (rw)

The associations above this one in the inclusion tree.

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 311

attr_writer :parent_inclusions

#validate?true | false (readonly)

Whether the associated object(s) should be validated.

Returns:

  • (true | false)

    If the associated object(s) should be validated.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 300

def validate?
  @validate ||= if @options[:validate].nil?
                  validation_default
                else
                  !!@options[:validate]
                end
end

Instance Method Details

#==(other) ⇒ Object

Compare this association to another.

Returns:

  • (Object)

    The object to compare to this association.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 67

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) ⇒ true | false

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

Parameters:

  • doc (Document)

    The document to be bound.

Returns:

  • (true | false)

    Whether the document can be bound.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 98

def bindable?(doc); false; end

#class_name

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 156

alias :class_name :relation_class_name

#counter_cache_column_nameString

Get the counter cache column name.

Returns:

  • (String)

    The counter cache column name.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 275

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_extension!(&block) (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 426

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

#create_relation(owner, target) ⇒ Proxy

Create an association proxy object using the owner and target.

Parameters:

  • owner (Document)

    The document this association hangs off of.

  • target (Document | Array<Document>)

    The target (parent) of the association.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 261

def create_relation(owner, target)
  relation.new(owner, target, self)
end

#default_inverse (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 436

def default_inverse
  @default_inverse ||= klass.relations[inverse_klass.name.underscore]
end

#define_autosaver! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 360

def define_autosaver!
  if autosave?
    Association::Referenced::AutoSave.define_autosave!(self)
  end
end

#define_builder! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 366

def define_builder!
  Association::Builders.define_builder!(self)
end

#define_counter_cache_callbacks! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 394

def define_counter_cache_callbacks!
  if counter_cached?
    Association::Referenced::CounterCache.define_callbacks!(self)
  end
end

#define_creator! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 370

def define_creator!
  Association::Builders.define_creator!(self)
end

#define_dependency! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 400

def define_dependency!
  if dependent
    Association::Depending.define_dependency!(self)
  end
end

#define_existence_check! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 382

def define_existence_check!
  Association::Accessors.define_existence_check!(self)
end

#define_getter! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 374

def define_getter!
  Association::Accessors.define_getter!(self)
end

#define_ids_getter! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 386

def define_ids_getter!
  Association::Accessors.define_ids_getter!(self)
end

#define_ids_setter! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 390

def define_ids_setter!
  Association::Accessors.define_ids_setter!(self)
end

#define_setter! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 378

def define_setter!
  Association::Accessors.define_setter!(self)
end

#define_touchable! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 354

def define_touchable!
  if touchable?
    Touchable.define_touchable!(self)
  end
end

#extensionModule

Get the extension.

Returns:

  • (Module)

    The extension module, if one has been defined.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 284

def extension
  @extension ||= @options[:extend]
end

#foreign_key_checkString

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

Examples:

Get the foreign key check method.

association.foreign_key_check

Returns:

  • (String)

    The foreign key check.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 250

def foreign_key_check
  @foreign_key_check ||= "#{foreign_key}_previously_changed?" if (stores_foreign_key? && foreign_key)
end

#foreign_key_setterString

The name of the foreign key setter method.

Returns:

  • (String)

    The name of the foreign key setter.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 220

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) ⇒ Array<Proc | Symbol>

Get the callbacks for a given type.

Parameters:

  • callback_type (Symbol)

    The type of callback type.

Returns:

  • (Array<Proc | Symbol>)

    A list of the callbacks, either method names or Procs.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 80

def get_callbacks(callback_type)
  Array(options[callback_type])
end

#initialize(_class, name, opts = {}, &block)

Initialize the ::Mongoid::Association.

Parameters:

  • _class (Class)

    The class of the model who owns this association.

  • name (Symbol)

    The name of the association.

  • opts (Hash) (defaults to: {})

    The association options.

  • block (Block)

    The optional block.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 51

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

#inverse(other = nil) ⇒ Symbol

Get the inverse name.

Returns:

  • (Symbol)

    The inverse name.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 291

def inverse(other = nil)
  candidates = inverses(other)
  candidates.detect { |c| c } if candidates
end

#inverse_association(other = nil) ⇒ Relatable

Get the inverse’s association metadata.

Parameters:

  • other (Object) (defaults to: nil)

    The other model class or model object to use when determining inverses.

Returns:

  • (Relatable)

    The inverse’s association metadata.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 123

def inverse_association(other = nil)
  (other || relation_class).relations[inverse(other)]
end

#inverse_association_classes (private)

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.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 346

def inverse_association_classes
  Mongoid::Config.models.map { |m| inverse_association(m) }.compact.map(&:inverse_class)
end

#inverse_classString Also known as: #inverse_klass

The class of the object owning this association.

Returns:

  • (String)

    The owning objects’ class.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 190

def inverse_class
  @owner_class
end

#inverse_class_nameString

The class name of the object owning this association.

Returns:

  • (String)

    The owning objects’ class name.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 183

def inverse_class_name
  @inverse_class_name ||= @owner_class.name
end

#inverse_klass

Alias for #inverse_class.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 193

alias :inverse_klass :inverse_class

#inverse_setter(other = nil) ⇒ String

The name of the inverse setter method.

Returns:

  • (String)

    The name of the inverse setter.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 213

def inverse_setter(other = nil)
  @inverse_setter ||= "#{inverses(other).first}=" unless inverses(other).blank?
end

#inverse_typenil

Get the inverse type.

Returns:

  • (nil)

    Default is nil for an association.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 130

def inverse_type; end

#inverse_type_setterString

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

Examples:

Get the inverse type setter.

association.inverse_type_setter

Returns:

  • (String)

    The name of the setter.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 240

def inverse_type_setter
  @inverse_type_setter ||= "#{inverse_type}=" if inverse_type
end

#inverses(other = nil) ⇒ Array<Symbol>

Get the inverse names.

Parameters:

  • other (Object) (defaults to: nil)

    The other model class or model object to use when determining inverses.

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 106

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

#keySymbol | String

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

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 199

def key
  stores_foreign_key? ? foreign_key : primary_key
end

#klass

Alias for #relation_class.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 178

alias :klass :relation_class

#namespace_hierarchy(mod) (private)

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].

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 444

def namespace_hierarchy(mod)
  parent = Object
  hier = [parent]

  # name is not present on anonymous modules
  if mod.name
    mod.name.split('::').each do |part|
      parent = parent.const_get(part)
      hier << parent
    end
  end

  hier.reverse
end

#path(document) ⇒ Mongoid::Atomic::Paths::Root

The atomic path for this association.

Returns:

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 229

def path(document)
  relation.path(document)
end

#polymorph! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 420

def polymorph!
  if polymorphic?
    @owner_class.polymorphic = true
  end
end

#relation_classString Also known as: #klass

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).

Returns:

  • (String)

    The association objects’ class.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 172

def relation_class
  @klass ||= begin
    cls_name = @options[:class_name] || ActiveSupport::Inflector.classify(name)
    resolve_name(inverse_class, cls_name)
  end
end

#relation_class_nameString Also known as: #class_name

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.

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.

Returns:

  • (String)

    The association objects’ class name.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 153

def relation_class_name
  @class_name ||= @options[:class_name] || ActiveSupport::Inflector.classify(name)
end

#resolve_name(mod, name) (private)

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.

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

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 466

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

#setterString

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

Returns:

  • (String)

    The setter name.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 206

def setter
  @setter ||= "#{name}="
end

#setup_index! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 350

def setup_index!
  @owner_class.index(index_spec, background: true) if indexed?
end

#type_setterString

Note:

Only relevant for polymorphic associations that take the :as option.

Get the type setter.

Returns:

  • (String)

    The type setter method.

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 88

def type_setter
  @type_setter ||= "#{type}=" if type
end

#validate! (private)

[ GitHub ]

  
# File 'lib/mongoid/association/relatable.rb', line 406

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