lib/mongoid/association/builders.rb
# frozen_string_literal: true
# rubocop:todo all
module Mongoid
module Association
# This module is responsible for defining the build and create methods used
# in one to one associations.
#
# @example Methods that get created.
#
# class Person
# include Mongoid::Document
# embeds_one :name
# end
#
# # The following methods get created:
# person.build_name({ :first_name => "Durran" })
# person.create_name({ :first_name => "Durran" })
module Builders
extend ActiveSupport::Concern
private
# Parse out the attributes and the options from the args passed to a
# build_ or create_ methods.
#
# @example Parse the args.
# doc.parse_args(:name => "Joe")
#
# @param [ Hash... ] *args The arguments.
#
# @return [ Array<Hash> ] The attributes and options.
def parse_args(*args)
[ args.first || {}, args.size > 1 ? args[1] : {} ]
end
# Defines a builder method. This is defined as #build_name.
#
# @example
# Person.define_builder!(association)
#
# @param [ Mongoid::Association::Relatable ] association The association metadata.
#
# @return [ Class ] The class being set up.
def self.define_builder!(association)
association.inverse_class.tap do |klass|
klass.re_define_method("build_#{association.name}") do |*args|
attributes, _options = parse_args(*args)
document = Factory.execute_build(association.relation_class, attributes, execute_callbacks: false)
_building do
child = send("#{association.name}=", document)
child.run_pending_callbacks
child.run_callbacks(:build)
child
end
end
end
end
# Defines a creator method. This is defined as #create_name.
# After the object is built it will immediately save.
#
# @example
# Person.define_creator!(association)
#
# @param [ Mongoid::Association::Relatable ] association The association metadata.
#
# @return [ Class ] The class being set up.
def self.define_creator!(association)
association.inverse_class.tap do |klass|
klass.re_define_method("create_#{association.name}") do |*args|
attributes, _options = parse_args(*args)
document = Factory.execute_build(association.relation_class, attributes, execute_callbacks: false)
doc = _assigning do
send("#{association.name}=", document)
end
doc.run_pending_callbacks
doc.save
save if new_record? && association.stores_foreign_key?
doc
end
end
end
end
end
end