Module: Mongoid::Association::Accessors

Extended by:
ActiveSupport::Concern
Included in:
Mongoid::Association
Defined in:
lib/mongoid/association/accessors.rb

Overview

This module contains all the behavior related to accessing associations through the getters and setters, and how to delegate to builders to create new ones.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.define_builder!(association) ⇒ Class

Defines a builder method for an embeds_one association. This is defined as #build_name.

Examples:

Person.define_builder!(association)

Parameters:

Returns:

  • (Class)

    The class being set up.



395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/mongoid/association/accessors.rb', line 395

def self.define_builder!(association)
  name = association.name
  association.inverse_class.tap do |klass|
    klass.re_define_method("build_#{name}") do |*args|
      attributes, _options = parse_args(*args)
      document = Factory.build(association.relation_class, attributes)
      _building do
        child = send("#{name}=", document)
        child.run_callbacks(:build)
        child
      end
    end
  end
end

.define_creator!(association) ⇒ Class

Defines a creator method for an embeds_one association. This is defined as #create_name. After the object is built it will immediately save.

Examples:

Person.define_creator!(association)

Parameters:

Returns:

  • (Class)

    The class being set up.



420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/mongoid/association/accessors.rb', line 420

def self.define_creator!(association)
  name = association.name
  association.inverse_class.tap do |klass|
    klass.re_define_method("create_#{name}") do |*args|
      attributes, _options = parse_args(*args)
      document = Factory.build(association.klass, attributes)
      doc = _assigning do
        send("#{name}=", document)
      end
      doc.save
      save if new_record? && association.stores_foreign_key?
      doc
    end
  end
end

.define_existence_check!(association) ⇒ Class

Adds the existence check for associations.

Examples:

Add the existence check.

Person.define_existence_check!(association)

Check if an association exists.

person = Person.new
person.has_game?
person.game?

Parameters:

Returns:

  • (Class)

    The model being set up.



277
278
279
280
281
282
283
284
285
286
287
# File 'lib/mongoid/association/accessors.rb', line 277

def self.define_existence_check!(association)
  name = association.name
  association.inverse_class.tap do |klass|
    klass.module_eval <<-END, __FILE__, __LINE__ + 1
        def #{name}?
          without_autobuild { !__send__(:#{name}).blank? }
        end
        alias :has_#{name}? :#{name}?
    END
  end
end

.define_getter!(association) ⇒ Class

Defines the getter for the association. Nothing too special here: just return the instance variable for the association if it exists or build the thing.

Examples:

Set up the getter for the association.

Person.define_getter!(association)

Parameters:

Returns:

  • (Class)

    The class being set up.



299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/mongoid/association/accessors.rb', line 299

def self.define_getter!(association)
  name = association.name
  association.inverse_class.tap do |klass|
    klass.re_define_method(name) do |reload = false|
      value = get_relation(name, association, nil, reload)
      next value unless value.nil? && !without_autobuild?
      next association.fallback if association.fallback?
      next send("build_#{name}") if association.autobuilding?

      value
    end
  end
end

.define_ids_getter!(association) ⇒ Class

Defines the getter for the ids of documents in the association. Should be specify only for referenced many associations.

Examples:

Set up the ids getter for the association.

Person.define_ids_getter!(association)

Parameters:

Returns:

  • (Class)

    The class being set up.



322
323
324
325
326
327
328
329
# File 'lib/mongoid/association/accessors.rb', line 322

def self.define_ids_getter!(association)
  ids_method = "#{association.name.to_s.singularize}_ids"
  association.inverse_class.tap do |klass|
    klass.re_define_method(ids_method) do
      send(association.name).pluck(:_id)
    end
  end
end

.define_ids_setter!(association) ⇒ Object

Defines the setter method that allows you to set documents in this association by their ids. The defined setter, finds documents with given ids and invokes regular association setter with found documents. Ids setters should be defined only for referenced many associations.

@param [ Mongoid::Association::Relatable ] association The association for the association.

@return [ Class ] The class being set up.

Examples:

Set up the id_setter for the association.

Person.define_ids_setter!(association)


376
377
378
379
380
381
382
383
384
# File 'lib/mongoid/association/accessors.rb', line 376

def self.define_ids_setter!(association)
  ids_method = "#{association.name.to_s.singularize}_ids="
  association.inverse_class.aliased_associations[ids_method.chop] = association.name.to_s
  association.inverse_class.tap do |klass|
    klass.re_define_method(ids_method) do |ids|
      send(association.setter, association.relation_class.find(ids.reject(&:blank?)))
    end
  end
end

.define_setter!(association) ⇒ Class

Defines the setter for the association. This does a few things based on some conditions. If there is an existing association, a target substitution will take place, otherwise a new association will be created with the supplied target.

Examples:

Set up the setter for the association.

Person.define_setter!(association)

Parameters:

Returns:

  • (Class)

    The class being set up.



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/mongoid/association/accessors.rb', line 342

def self.define_setter!(association)
  name = association.name
  association.inverse_class.tap do |klass|
    klass.re_define_method("#{name}=") do |object|
      without_autobuild do
        if association.fallback?
          klass = association.polymorphic? ? Mongoid::Document : association.relation_class
          object = nil unless object.is_a?(klass)
        end

        if value = get_relation(name, association, object)
          value = __build__(name, value, association) unless value.respond_to?(:substitute)

          set_relation(name, value.substitute(object.substitutable))
        else
          __build__(name, object.substitutable, association)
        end
      end
    end
  end
end

Instance Method Details

#__build__(name, object, association, selected_fields = nil) ⇒ Proxy

Builds the related document and creates the association unless the document is nil, then sets the association on this document.

Examples:

Build the association.

person.__build__(:addresses, { :_id => 1 }, association)

Parameters:

  • name (String | Symbol)

    The name of the association.

  • object (Hash | BSON::ObjectId)

    The id or attributes to use.

  • association (Mongoid::Association::Relatable)

    The association metadata.

  • selected_fields (Hash) (defaults to: nil)

    Fields which were retrieved via #only. If selected_fields is specified, fields not listed in it will not be accessible in the built document.

Returns:

  • (Proxy)

    The association.



25
26
27
28
# File 'lib/mongoid/association/accessors.rb', line 25

def __build__(name, object, association, selected_fields = nil)
  relation = create_relation(object, association, selected_fields)
  set_relation(name, relation)
end

#create_relation(object, association, selected_fields = nil) ⇒ Proxy

Create an association from an object and association metadata.

Examples:

Create the association.

person.create_relation(document, association)

Parameters:

  • object (Document | Array<Document>)

    The association target.

  • association (Mongoid::Association::Relatable)

    The association metadata.

  • selected_fields (Hash) (defaults to: nil)

    Fields which were retrieved via #only. If selected_fields is specified, fields not listed in it will not be accessible in the created association document.

Returns:

  • (Proxy)

    The association.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/mongoid/association/accessors.rb', line 42

def create_relation(object, association, selected_fields = nil)
  key = @attributes[association.inverse_type]
  type = key ? association.resolver.model_for(key) : nil
  target = if t = association.build(self, object, type, selected_fields)
             association.create_relation(self, t)
           end

  # Only need to do this on embedded associations. The pending callbacks
  # are only added when materializing the documents, which only happens
  # on embedded associations. There is no call to the database in the
  # construction of a referenced association.
  if association.embedded?
    Array(target).each do |doc|
      doc.try(:run_pending_callbacks)
    end
  end

  target
end

#reset_relation_criteria(name) ⇒ Object

Resets the criteria inside the association proxy. Used by many-to-many associations to keep the underlying ids array in sync.

Examples:

Reset the association criteria.

person.reset_relation_criteria(:preferences)

Parameters:

  • name (Symbol)

    The name of the association.



69
70
71
72
73
# File 'lib/mongoid/association/accessors.rb', line 69

def reset_relation_criteria(name)
  return unless instance_variable_defined?("@_#{name}")

  send(name).reset_unloaded
end

#set_relation(name, relation) ⇒ Proxy

Set the supplied association to an instance variable on the class with the provided name. Used as a helper just for code cleanliness.

Examples:

Set the proxy on the document.

person.set(:addresses, addresses)

Parameters:

  • name (String | Symbol)

    The name of the association.

  • relation (Proxy)

    The association to set.

Returns:

  • (Proxy)

    The association.



85
86
87
# File 'lib/mongoid/association/accessors.rb', line 85

def set_relation(name, relation)
  instance_variable_set("@_#{name}", relation)
end