/
one.rb
129 lines (118 loc) · 3.91 KB
/
one.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# frozen_string_literal: true
# encoding: utf-8
module Mongoid
module Association
module Nested
class One
include Buildable
attr_accessor :destroy
# Builds the association depending on the attributes and the options
# passed to the macro.
#
# @example Build a 1-1 nested document.
# one.build(person, as: :admin)
#
# @note This attempts to perform 3 operations, either one of an update of
# the existing association, a replacement of the association with a new
# document, or a removal of the association.
#
# @param [ Document ] parent The parent document.
#
# @return [ Document ] The built document.
#
# @since 2.0.0
def build(parent)
return if reject?(parent, attributes)
@existing = parent.send(association.name)
if update?
attributes.delete_id
existing.assign_attributes(attributes)
elsif replace?
parent.send(association.setter, Factory.build(@class_name, attributes))
elsif delete?
parent.send(association.setter, nil)
end
end
# Create the new builder for nested attributes on one-to-one
# associations.
#
# @example Instantiate the builder.
# One.new(association, attributes)
#
# @param [ Association ] association The association metadata.
# @param [ Hash ] attributes The attributes hash to attempt to set.
# @param [ Hash ] options The options defined.
#
# @since 2.0.0
def initialize(association, attributes, options)
@attributes = attributes.with_indifferent_access
@association = association
@options = options
@class_name = options[:class_name] ? options[:class_name].constantize : association.klass
@destroy = @attributes.delete(:_destroy)
end
private
# Is the id in the attribtues acceptable for allowing an update to
# the existing association?
#
# @api private
#
# @example Is the id acceptable?
# one.acceptable_id?
#
# @return [ true, false ] If the id part of the logic will allow an update.
#
# @since 2.0.0
def acceptable_id?
id = convert_id(existing.class, attributes[:id])
existing._id == id || id.nil? || (existing._id != id && update_only?)
end
# Can the existing association be deleted?
#
# @example Can the existing object be deleted?
# one.delete?
#
# @return [ true, false ] If the association should be deleted.
#
# @since 2.0.0
def delete?
destroyable? && !attributes[:id].nil?
end
# Can the existing association potentially be destroyed?
#
# @example Is the object destroyable?
# one.destroyable?({ :_destroy => "1" })
#
# @return [ true, false ] If the association can potentially be
# destroyed.
#
# @since 2.0.0
def destroyable?
Nested::DESTROY_FLAGS.include?(destroy) && allow_destroy?
end
# Is the document to be replaced?
#
# @example Is the document to be replaced?
# one.replace?
#
# @return [ true, false ] If the document should be replaced.
#
# @since 2.0.0
def replace?
!existing && !destroyable? && !attributes.blank?
end
# Should the document be updated?
#
# @example Should the document be updated?
# one.update?
#
# @return [ true, false ] If the object should have its attributes updated.
#
# @since 2.0.0
def update?
existing && !destroyable? && acceptable_id?
end
end
end
end
end