custom_fields 1.0.0.beta.25 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,16 +1,59 @@
1
1
  h1. CustomFields
2
2
 
3
3
  Manage custom fields to a mongoid document or a collection. This module is one of the core features we implemented in our custom cms named Locomotive.
4
+ Basically, its aim is to provide to editors a way to manage extra fields to a Mongoid document through for instance a web UI.
5
+
6
+ The main goals:
7
+
8
+ * offering a very secure way to add / edit / delete extra fields to a Mongoid document
9
+ * scoping the modifications added to a Mongoid document so that other documents of the same class won't be updated.
4
10
 
5
11
  h2. Requirements
6
12
 
7
- ActiveSupport 3.0.7, MongoDB 1.6 and Mongoid 2.0.2
13
+ ActiveSupport 3.1.1, MongoDB 2.0 and Mongoid 2.3.2
14
+
15
+ h2. Examples
16
+
17
+ h3. On a has_many relationship
18
+
19
+ bc.. class Company
20
+ embeds_many :employees
21
+
22
+ custom_fields_for :employees
23
+ end
24
+
25
+ class Employee
26
+ field :name, String
27
+
28
+ embedded_in :company, :inverse_of => :employees
29
+ end
30
+
31
+ company = Company.new
32
+ company.employees_custom_fields.build :label => 'His/her position', :_alias => 'position', :kind => 'string', :required => true
33
+
34
+ company.save
35
+
36
+ company.employees.build :name => 'Michael Scott', :position => 'Regional manager'
37
+
38
+ another_company = Company.new
39
+ employee = company.employees.build
40
+ employee.position # returns a "not defined method" error
41
+
42
+ h3. On the class itself
43
+
44
+ bc.. class Company
45
+ custom_fields_for_itself
46
+ end
8
47
 
48
+ company = Company.new
49
+ company.self_metadata_custom_fields.build :label => 'Shipping Address', :_alias => 'address', :kind => 'text'
9
50
 
10
- h2. Example
51
+ company.save
11
52
 
12
- Coming soon but take a look of the tests to see what CustomFields is capable of.
53
+ company.self_metadata.address = '700 S Laflin, 60607 Chicago'
13
54
 
55
+ another_company = Company.new
56
+ other_company.self_metadata.address # returns a "not defined method" error
14
57
 
15
58
  h2. Contact
16
59
 
@@ -12,175 +12,329 @@ module CustomFields
12
12
 
13
13
  module InstanceMethods
14
14
 
15
- def custom_fields_for?(collection_name)
16
- self.class.custom_fields_for?(collection_name)
15
+ # Determines if the relation is enhanced by the custom fields
16
+ #
17
+ # @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
18
+ # person.custom_fields_for?(:addresses)
19
+ #
20
+ # @param [ String, Symbol ] name The name of the relation.
21
+ #
22
+ # @return [ true, false ] True if enhanced, false if not.
23
+ #
24
+ def custom_fields_for?(name)
25
+ self.class.custom_fields_for?(name)
17
26
  end
18
27
 
28
+ # Returns the class enhanced by the custom fields defined in the parent class.
29
+ #
30
+ # @param [ String, Symbol ] name The name of the relation.
31
+ #
32
+ # @return [ Class ] The modified class.
33
+ #
34
+ def klass_with_custom_fields(name)
35
+ self.class.klass_with_custom_fields(name, self)
36
+ end
37
+
38
+ # Returns the ordered list of custom fields for a relation
39
+ #
40
+ # @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
41
+ # person.ordered_custom_fields(:addresses)
42
+ #
43
+ # @param [ String, Symbol ] name The name of the relation.
44
+ #
45
+ # @return [ Collection ] The ordered list.
46
+ #
47
+ def ordered_custom_fields(name)
48
+ self.send(:"#{name}_custom_fields").sort { |a, b| (a.position || 0) <=> (b.position || 0) }
49
+ end
50
+
51
+ # Marks all the custom fields as persisted. Actually, this is a patch
52
+ # for mongoid since for the update, it runs the reset_persisted_children method after
53
+ # the callbacks unlike for the create.
54
+ # We assume that all the fields have been validated in a previous step
55
+ #
56
+ # @param [ String, Symbol ] name The name of the relation.
57
+ #
58
+ def mark_custom_fields_as_persisted(name)
59
+ self.send(:"#{name}_custom_fields").each do |field|
60
+ field.instance_variable_set(:@new_record, false) unless field.persisted?
61
+ end
62
+ end
63
+
64
+ # Builds the class enhanced by the custom fields defined in the parent class.
65
+ # The new class inherits from the original one.
66
+ #
67
+ # @param [ String, Symbol ] name The name of the relation.
68
+ # @param [ Metadata ] metadata The relation's metadata.
69
+ #
70
+ # @return [ Class ] The modified class.
71
+ #
72
+ def build_klass_with_custom_fields(name, metadata)
73
+ custom_fields = self.ordered_custom_fields(name)
74
+
75
+ metadata.klass.to_klass_with_custom_fields(name, self, custom_fields)
76
+ end
77
+
78
+ # Marks the class enhanced by the custom fields as invalidated
79
+ #
80
+ # @param [ String, Symbol ] name The name of the relation.
81
+ #
82
+ def mark_klass_with_custom_fields_as_invalidated(name)
83
+ self.send(:"invalidate_#{name}_klass_flag=", true)
84
+ end
85
+
86
+ # Reset the flag telling if the class enhanced by the custom fields is invalidated
87
+ #
88
+ # @param [ String, Symbol ] name The name of the relation.
89
+ #
90
+ def reset_klass_with_custom_fields_invalidated_flag(name)
91
+ # puts "* reset_klass_with_custom_fields_invalidated_flag #{name}"
92
+ self.send(:"invalidate_#{name}_klass_flag=", false)
93
+ end
94
+
95
+ # Determines if the enhanced class has to be invalidated.
96
+ #
97
+ # @param [ String, Symbol ] name The name of the relation.
98
+ #
99
+ # @return [ true, false ] True if enhanced, false if not.
100
+ #
101
+ def invalidate_klass_with_custom_fields?(name)
102
+ !!self.send(:"invalidate_#{name}_klass_flag")
103
+ end
104
+
105
+ # Destroy the class enhanced by the custom fields so that next time we need it,
106
+ # we have a fresh new one.
107
+ #
108
+ # @param [ String, Symbol ] name The name of the relation.
109
+ #
110
+ def invalidate_klass_with_custom_fields(name)
111
+ self.class.invalidate_klass_with_custom_fields(name, self)
112
+ end
113
+
114
+ # Duplicates a metadata and assigns the enhanced class to it.
115
+ #
116
+ # @param [ Metadata ] metadata The relation's old metadata.
117
+ #
118
+ # @return [ Metadata ] The relation's new metadata
119
+ #
19
120
  def clone_metadata_for_custom_fields(metadata)
20
- singular_name = metadata.name.to_s.singularize.gsub(/^_/, '')
121
+ # puts "-> clone_metadata_for_custom_fields #{metadata.name}"
21
122
 
22
- klass = self.send(:"fetch_#{singular_name}_klass")
123
+ klass = self.build_klass_with_custom_fields(metadata.name, metadata)
23
124
 
24
- # safer to do that because we are going to modify the metadata klass for next operations
125
+ # we do not want that other instances of the parent class have the same metadata
25
126
  metadata.clone.tap do |metadata|
26
127
  metadata.instance_variable_set(:@klass, klass)
27
128
  end
28
129
  end
29
130
 
30
- end
131
+ # When the fields have been modified and before the object is saved,
132
+ # we bump the version.
133
+ #
134
+ # @param [ String, Symbol ] name The name of the relation.
135
+ #
136
+ def bump_custom_fields_version(name)
137
+ if self.invalidate_klass_with_custom_fields?(name)
138
+ # puts "%%% bump_custom_fields_version #{name} #{self.send(:"#{name}_custom_fields_version").inspect}"
139
+ version = self.send(:"#{name}_custom_fields_version") || 0
140
+ self.send(:"#{name}_custom_fields_version=", version + 1)
141
+ end
142
+ end
31
143
 
32
- # Enhance an embedded collection OR the instance itself (by passing self) by providing methods to manage custom fields.
33
- #
34
- # class Company
35
- #
36
- # custom_fields_for :self
37
- #
38
- # embeds_many :employees
39
- # custom_fields_for :employees
40
- # end
41
- #
42
- # class Employee
43
- # embedded_in :company, :inverse_of => :employees
44
- # field :name, String
45
- # end
46
- #
47
- # company.employee_custom_fields.build :label => 'His/her position', :_alias => 'position', :kind => 'string'
48
- #
49
- # company.employees.build :name => 'Michael Scott', :position => 'Regional manager'
50
- #
51
- #
52
- # company.self_custom_fields.build :label => 'Shipping Address', :_alias => 'address', :kind => 'text'
53
- #
54
- # company.metadata.address = '700 S Laflin, 60607 Chicago'
55
- #
56
- # other_company.metadata.address # returns a "not defined method" error
57
- #
58
- module ClassMethods
144
+ # Increments by 1 the counter couting the number of added custom fields
145
+ # for a relation
146
+ #
147
+ # @param [ String, Symbol ] name The name of the relation.
148
+ #
149
+ # @return [ Integer ] The new value of the counter
150
+ #
151
+ def bump_custom_fields_counter(name)
152
+ counter = self.send(:"#{name}_custom_fields_counter") || 0
153
+ self.send(:"#{name}_custom_fields_counter=", counter + 1)
154
+ end
155
+
156
+ # Builds a new relation so that the builder takes the last version of
157
+ # the enhanced class when creating new instances
158
+ #
159
+ # @param [ String, Symbol ] name The name of the relation.
160
+ #
161
+ def rebuild_custom_fields_relation(name)
162
+ # metadata = self.clone_metadata_for_custom_fields(self.relations[name.to_s])
163
+
164
+ # puts "rebuild_custom_fields_relation #{name}"
59
165
 
60
- def custom_fields_for?(collection_name)
61
- self._custom_fields_for.include?(collection_name.to_s)
166
+ metadata = self.relations[name.to_s]
167
+ self.build(name, nil, metadata)
62
168
  end
63
169
 
64
- def custom_fields_for(collection_name)
65
- singular_name = collection_name.to_s.singularize
66
- dynamic_custom_field_class_name = "#{self.name}#{singular_name.camelize}Field"
170
+ end
67
171
 
68
- self.declare_embedded_in_definition_in_custom_field(collection_name)
172
+ module ClassMethods
69
173
 
70
- # enhance the class itself
71
- if (itself = %w(itself self).include?(collection_name.to_s))
72
- collection_name, singular_name = '_metadata', 'metadata'
174
+ # Determines if the relation is enhanced by the custom fields
175
+ #
176
+ # @example the Person class has somewhere in its code this: "custom_fields_for :addresses"
177
+ # Person.custom_fields_for?(:addresses)
178
+ #
179
+ # @param [ String, Symbol ] name The name of the relation.
180
+ #
181
+ # @return [ true, false ] True if enhanced, false if not.
182
+ #
183
+ def custom_fields_for?(name)
184
+ self._custom_fields_for.include?(name.to_s)
185
+ end
73
186
 
74
- class_eval <<-EOV
75
- embeds_one :#{collection_name}, :class_name => '::CustomFields::Metadata'
187
+ # Enhance an embedded collection OR the instance itself (by passing self) by providing methods to manage custom fields.
188
+ #
189
+ # @param [ String, Symbol ] name The name of the relation.
190
+ #
191
+ # @example
192
+ # class Company
193
+ # embeds_many :employees
194
+ # custom_fields_for :employees
195
+ # end
196
+ #
197
+ # class Employee
198
+ # embedded_in :company, :inverse_of => :employees
199
+ # field :name, String
200
+ # end
201
+ #
202
+ # company.employees_custom_fields.build :label => 'His/her position', :_alias => 'position', :kind => 'string'
203
+ # company.employees.build :name => 'Michael Scott', :position => 'Regional manager'
204
+ #
205
+ def custom_fields_for(name)
206
+ self.declare_embedded_in_definition_in_custom_field(name)
207
+
208
+ # stores the relation name
209
+ self._custom_fields_for << name.to_s
210
+
211
+ self.extend_for_custom_fields(name)
212
+ end
76
213
 
77
- def safe_#{singular_name}
78
- self.#{collection_name} || self.build_#{collection_name}
79
- end
80
- EOV
214
+ # Enhances the class itself
215
+ #
216
+ # @example
217
+ # class Company
218
+ # custom_fields_for_itself
219
+ # end
220
+ #
221
+ # company.self_metadata_custom_fields.build :label => 'Shipping Address', :_alias => 'address', :kind => 'text'
222
+ # company.self_metadata.address = '700 S Laflin, 60607 Chicago'
223
+ # other_company.self_metadata.address # returns a "not defined method" error
224
+ #
225
+ def custom_fields_for_itself
226
+ self.embeds_one :self_metadata, :class_name => '::CustomFields::SelfMetadata'
227
+
228
+ class_eval do
229
+ def self_metadata_with_automatic_build
230
+ object = self_metadata_without_automatic_build
231
+ object || self.build_self_metadata
232
+ end
233
+ alias_method_chain :self_metadata, :automatic_build
81
234
  end
82
235
 
83
- # record the collection_name
84
- self._custom_fields_for << collection_name.to_s
236
+ self.custom_fields_for('self_metadata')
237
+ end
85
238
 
86
- # common part
87
- class_eval <<-EOV
88
- field :#{singular_name}_custom_fields_counter, :type => Integer, :default => 0
89
- field :#{singular_name}_custom_fields_version, :type => Integer, :default => 0
90
-
91
- embeds_many :#{singular_name}_custom_fields, :class_name => '#{dynamic_custom_field_class_name}' do
92
- def build(attributes = {}, type = nil, &block)
93
- instantiated(type).tap do |doc|
94
- append(doc, default_options(:binding => true))
95
- doc.write_attributes(attributes)
96
- doc.identify
97
- block.call(doc) if block
98
- end
99
- end
100
- alias :new :build
101
- end
239
+ protected
102
240
 
103
- attr_accessor :invalidate_#{singular_name}_klass_flag
241
+ # Extends / Decorates the current class in order to be fully custom_fields compliant.
242
+ # it declares news fields, adds new callbacks, ...etc
243
+ #
244
+ # @param [ String, Symbol ] name The name of the relation.
245
+ #
246
+ def extend_for_custom_fields(name)
247
+ class_eval do
248
+ field :"#{name}_custom_fields_counter", :type => Integer, :default => 0
249
+ field :"#{name}_custom_fields_version", :type => Integer, :default => 0
104
250
 
105
- before_save do |record|
106
- if record.invalidate_#{singular_name}_klass?
107
- record.#{singular_name}_custom_fields_version ||= 0
108
- record.#{singular_name}_custom_fields_version += 1
109
- # puts "[parent/before_save] set #{singular_name}_custom_fields_version " + record.#{singular_name}_custom_fields_version.to_s # debug purpose
110
- end
111
- end
251
+ embeds_many :"#{name}_custom_fields", :class_name => self.dynamic_custom_field_class_name(name), :cascade_callbacks => true
112
252
 
113
- after_destroy :invalidate_#{singular_name}_klass
253
+ attr_accessor :"invalidate_#{name}_klass_flag" # flag for invalidating the custom class
114
254
 
115
- accepts_nested_attributes_for :#{singular_name}_custom_fields, :allow_destroy => true
255
+ accepts_nested_attributes_for :"#{name}_custom_fields", :allow_destroy => true
256
+ end
116
257
 
117
- def ordered_#{singular_name}_custom_fields
118
- self.#{singular_name}_custom_fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
119
- end
258
+ class_eval <<-EOV
120
259
 
121
- def fetch_#{singular_name}_klass
122
- metadata = self.relations['#{collection_name.to_s}']
123
- metadata.klass.to_klass_with_custom_fields(self.ordered_#{singular_name}_custom_fields, self, metadata.name)
124
- end
260
+ before_save :bump_#{name}_custom_fields_version
261
+ after_save :mark_#{name}_custom_fields_as_persisted
262
+ after_save :rebuild_#{name}_relation
263
+ after_save :reset_#{name}_klass_invalidated_flag
264
+ after_destroy :invalidate_#{name}_klass
125
265
 
126
- def #{singular_name}_klass
127
- metadata = self.relations['#{collection_name.to_s}']
128
- metadata.klass.current_klass_with_custom_fields(self, metadata.name)
266
+ def #{name}_klass
267
+ self.klass_with_custom_fields('#{name}')
129
268
  end
130
269
 
131
- def #{singular_name}_klass_out_of_date?
132
- self.#{singular_name}_klass.nil? || self.#{singular_name}_klass.version != self.#{singular_name}_custom_fields_version
270
+ def #{name}_klass_name
271
+ self.class.klass_name_with_custom_fields('#{name}', self)
133
272
  end
134
273
 
135
- def invalidate_#{singular_name}_klass
136
- metadata = self.relations['#{collection_name.to_s}']
137
- metadata.klass.invalidate_proxy_class_with_custom_fields(self, metadata.name)
274
+ def #{name}_klass_out_of_date?
275
+ self.#{name}_klass.nil? || self.#{name}_klass.version != self.#{name}_custom_fields_version
138
276
  end
139
277
 
140
- def invalidate_#{singular_name}_klass?
141
- self.invalidate_#{singular_name}_klass_flag == true
278
+ def invalidate_#{name}_klass
279
+ self.invalidate_klass_with_custom_fields('#{name}')
142
280
  end
143
- EOV
144
281
 
145
- # mongoid tiny patch: for performance optimization (ie: we do want to invalidate klass with custom fields every time we save a field)
146
- unless instance_methods.collect(&:to_s).include?('write_attributes_with_custom_fields')
282
+ def invalidate_#{name}_klass?
283
+ self.invalidate_klass_with_custom_fields?('#{name}')
284
+ end
147
285
 
148
- class_eval do
149
- def write_attributes_with_custom_fields(attrs = nil, guard_protected_attributes = true)
150
- self.instance_variable_set(:@_writing_attributes_with_custom_fields, true)
151
- self.write_attributes_without_custom_fields(attrs, guard_protected_attributes)
286
+ def rebuild_#{name}_relation
287
+ # puts "--> AFTER SAVE (#{name})"
288
+ if self.#{name}_klass_out_of_date?
289
+ # puts 'rebuild relation for #{name} after save'
290
+ self.rebuild_custom_fields_relation('#{name}')
152
291
  end
292
+ end
293
+
294
+ protected
153
295
 
154
- alias_method_chain :write_attributes, :custom_fields
296
+ def bump_#{name}_custom_fields_version
297
+ # puts "--> BEFORE SAVE (#{name})"
298
+ self.bump_custom_fields_version('#{name}')
155
299
  end
156
300
 
157
- end
301
+ def mark_#{name}_custom_fields_as_persisted
302
+ self.mark_custom_fields_as_persisted('#{name}')
303
+ end
158
304
 
159
- if itself
160
- class_eval <<-EOV
161
- alias :self_custom_fields :#{singular_name}_custom_fields
162
- EOV
163
- end
305
+ def reset_#{name}_klass_invalidated_flag
306
+ self.reset_klass_with_custom_fields_invalidated_flag('#{name}')
307
+ end
164
308
 
309
+ EOV
165
310
  end
166
311
 
167
- protected
168
-
169
- def dynamic_custom_field_class_name(collection_name)
170
- "#{self.name}#{collection_name.to_s.singularize.camelize}Field"
312
+ # Returns the class name of the custom field which is based both on the parent class name
313
+ # and the name of the relation in order to avoid name conflicts (with other classes)
314
+ #
315
+ # @param [ Metadata ] metadata The relation's old metadata.
316
+ #
317
+ # @return [ String ] The class name
318
+ #
319
+ def dynamic_custom_field_class_name(name)
320
+ "#{self.name}#{name.to_s.singularize.camelize}Field"
171
321
  end
172
322
 
173
323
  # An embedded relationship has to be defined on both side in order for it
174
324
  # to work properly. But because custom_field can be embedded in different
175
325
  # models that it's not aware of, we have to declare manually the definition
176
326
  # once we know the target class.
177
- def declare_embedded_in_definition_in_custom_field(target_collection_name)
178
- singular_name = target_collection_name.to_s.singularize
179
- klass_name = self.dynamic_custom_field_class_name(target_collection_name)
327
+ #
328
+ # @param [ String, Symbol ] name The name of the relation.
329
+ #
330
+ # @return [ Field ] The new field class.
331
+ #
332
+ def declare_embedded_in_definition_in_custom_field(name)
333
+ klass_name = self.dynamic_custom_field_class_name(name)
180
334
 
181
335
  unless Object.const_defined?(klass_name)
182
336
  (klass = Class.new(::CustomFields::Field)).class_eval <<-EOF
183
- embedded_in :#{self.name.underscore}, :inverse_of => :#{singular_name}_custom_fields
337
+ embedded_in :#{self.name.underscore}, :inverse_of => :#{name}_custom_fields
184
338
  EOF
185
339
 
186
340
  Object.const_set(klass_name, klass)
@@ -191,4 +345,6 @@ module CustomFields
191
345
 
192
346
  end
193
347
 
194
- end
348
+ end
349
+
350
+
@@ -0,0 +1,12 @@
1
+ unless ActiveSupport::Callbacks::ClassMethods.method_defined?(:without_callback)
2
+
3
+ module ActiveSupport::Callbacks::ClassMethods
4
+ def without_callback(*args, &block)
5
+ skip_callback(*args)
6
+ yield.tap do |result|
7
+ set_callback(*args)
8
+ end
9
+ end
10
+ end
11
+
12
+ end
@@ -1,9 +1,37 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
 
4
+ # # This is the base module for all domain objects that need to be persisted to
5
+ # # the database as documents.
6
+ # module Document
7
+ #
8
+ # # Reloads the +Document+ attributes from the database. If the document has
9
+ # # not been saved then an error will get raised if the configuration option
10
+ # # was set.
11
+ # #
12
+ # # @example Reload the document.
13
+ # # person.reload
14
+ # #
15
+ # # @raise [ Errors::DocumentNotFound ] If the document was deleted.
16
+ # #
17
+ # # @return [ Document ] The document, reloaded.
18
+ # def reload_with_custom_fields
19
+ # reload_without_custom_fields.tap do
20
+ # instance_variable_names.each do |name|
21
+ # if name =~ /_proxy_class$/
22
+ # remove_instance_variable("#{name}")
23
+ # end
24
+ # end
25
+ # end
26
+ # end
27
+ #
28
+ # alias_method_chain :reload, :custom_fields
29
+ #
30
+ # end
31
+
4
32
  # This is the base module for all domain objects that need to be persisted to
5
33
  # the database as documents.
6
- module Document
34
+ module Reloading
7
35
 
8
36
  # Reloads the +Document+ attributes from the database. If the document has
9
37
  # not been saved then an error will get raised if the configuration option
@@ -4,26 +4,27 @@ module Mongoid # :nodoc:
4
4
 
5
5
  module Accessors
6
6
 
7
- # Create a relation from an object and metadata.
7
+ # Builds the related document and creates the relation based on the class
8
+ # enhanced by the custom_fields functionnality (if set up for the relation).
8
9
  #
9
- # @example Create the relation.
10
- # person.create_relation(document, metadata)
11
- #
12
- # @param [ Document, Array<Document ] object The relation target.
13
- # @param [ Metadata ] metadata The relation metadata.
10
+ # @param [ String, Symbol ] name The name of the relation.
11
+ # @param [ Hash, BSON::ObjectId ] object The id or attributes to use.
12
+ # @param [ Metadata ] metadata The relation's metadata.
13
+ # @param [ true, false ] building If we are in a build operation.
14
14
  #
15
15
  # @return [ Proxy ] The relation.
16
16
  #
17
17
  # @since 2.0.0.rc.1
18
- def create_relation_with_custom_fields(object, metadata)
18
+ def build_with_custom_fields(name, object, metadata)
19
19
  if self.respond_to?(:custom_fields_for?) && self.custom_fields_for?(metadata.name)
20
+ # puts "[Accessors] build_with_custom_fields #{name}"
20
21
  metadata = self.clone_metadata_for_custom_fields(metadata)
21
22
  end
22
23
 
23
- create_relation_without_custom_fields(object, metadata)
24
+ build_without_custom_fields(name, object, metadata)
24
25
  end
25
26
 
26
- alias_method_chain :create_relation, :custom_fields
27
+ alias_method_chain :build, :custom_fields
27
28
  end
28
29
 
29
30
  end
@@ -2,7 +2,6 @@ module Mongoid # :nodoc:
2
2
  module Relations #:nodoc:
3
3
 
4
4
  module Builders
5
- extend ActiveSupport::Concern
6
5
 
7
6
  module ClassMethods #:nodoc:
8
7
 
@@ -13,8 +12,12 @@ module Mongoid # :nodoc:
13
12
  metadata = self.clone_metadata_for_custom_fields(metadata)
14
13
  end
15
14
 
16
- document = Factory.build(metadata.klass, args.first || {})
17
- send("#{name}=", document, :binding => true)
15
+ attributes = args.first || {}
16
+ options = args.size > 1 ? args[1] : {}
17
+ document = Factory.build(metadata.klass, attributes, options)
18
+ _building do
19
+ send("#{name}=", document)
20
+ end
18
21
  end
19
22
  end
20
23
  end