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
The name of the association.
@return [ Symbol ] The name of the association.
@since 7.0
The options on this association.
@return [ Hash ] The options.
@since 7.0
Public Class Methods
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
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
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
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 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
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
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
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
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 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
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
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
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
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
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
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
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
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
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
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
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
- 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
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
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
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
# 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
# File lib/mongoid/association/relatable.rb, line 457 def default_inverse @default_inverse ||= klass.relations[inverse_klass.name.underscore] end
# File lib/mongoid/association/relatable.rb, line 381 def define_autosaver! if autosave? Association::Referenced::AutoSave.define_autosave!(self) end end
# File lib/mongoid/association/relatable.rb, line 387 def define_builder! Association::Builders.define_builder!(self) end
# File lib/mongoid/association/relatable.rb, line 415 def define_counter_cache_callbacks! if counter_cached? Association::Referenced::CounterCache.define_callbacks!(self) end end
# File lib/mongoid/association/relatable.rb, line 391 def define_creator! Association::Builders.define_creator!(self) end
# File lib/mongoid/association/relatable.rb, line 421 def define_dependency! if dependent Association::Depending.define_dependency!(self) end end
# File lib/mongoid/association/relatable.rb, line 403 def define_existence_check! Association::Accessors.define_existence_check!(self) end
# File lib/mongoid/association/relatable.rb, line 395 def define_getter! Association::Accessors.define_getter!(self) end
# File lib/mongoid/association/relatable.rb, line 407 def define_ids_getter! Association::Accessors.define_ids_getter!(self) end
# File lib/mongoid/association/relatable.rb, line 411 def define_ids_setter! Association::Accessors.define_ids_setter!(self) end
# File lib/mongoid/association/relatable.rb, line 399 def define_setter! Association::Accessors.define_setter!(self) end
# File lib/mongoid/association/relatable.rb, line 375 def define_touchable! if touchable? Touchable.define_touchable!(self) end end
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
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
# File lib/mongoid/association/relatable.rb, line 441 def polymorph! if polymorphic? @owner_class.polymorphic = true end end
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
# File lib/mongoid/association/relatable.rb, line 371 def setup_index! @owner_class.index(index_spec, background: true) if indexed? end
# 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