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.



387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/mongoid/association/accessors.rb', line 387

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.



412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/mongoid/association/accessors.rb', line 412

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
# 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)
      value = send("build_#{name}") if value.nil? && association.autobuilding? && !without_autobuild?
      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.



319
320
321
322
323
324
325
326
# File 'lib/mongoid/association/accessors.rb', line 319

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)


368
369
370
371
372
373
374
375
376
# File 'lib/mongoid/association/accessors.rb', line 368

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.



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/mongoid/association/accessors.rb', line 339

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