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.



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

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.



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

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.



280
281
282
283
284
285
286
287
288
289
290
# File 'lib/mongoid/association/accessors.rb', line 280

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.



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

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



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

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)


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

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.



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

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)
          if !value.respond_to?(:substitute)
            value = __build__(name, value, association) 
          end

          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.



27
28
29
30
# File 'lib/mongoid/association/accessors.rb', line 27

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.



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

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



73
74
75
76
77
# File 'lib/mongoid/association/accessors.rb', line 73

def reset_relation_criteria(name)
  if instance_variable_defined?("@_#{name}")
    send(name).reset_unloaded
  end
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.



89
90
91
# File 'lib/mongoid/association/accessors.rb', line 89

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