mongoid 3.0.6 → 3.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/CHANGELOG.md +57 -1
  2. data/lib/config/locales/en.yml +6 -2
  3. data/lib/mongoid/attributes.rb +1 -1
  4. data/lib/mongoid/attributes/processing.rb +58 -11
  5. data/lib/mongoid/config.rb +13 -0
  6. data/lib/mongoid/contextual/map_reduce.rb +1 -1
  7. data/lib/mongoid/contextual/memory.rb +5 -4
  8. data/lib/mongoid/contextual/mongo.rb +2 -2
  9. data/lib/mongoid/criteria.rb +10 -1
  10. data/lib/mongoid/dirty.rb +0 -1
  11. data/lib/mongoid/document.rb +2 -1
  12. data/lib/mongoid/extensions/array.rb +1 -1
  13. data/lib/mongoid/extensions/big_decimal.rb +15 -0
  14. data/lib/mongoid/extensions/hash.rb +18 -0
  15. data/lib/mongoid/factory.rb +1 -1
  16. data/lib/mongoid/fields.rb +31 -0
  17. data/lib/mongoid/hierarchy.rb +15 -0
  18. data/lib/mongoid/indexes.rb +22 -2
  19. data/lib/mongoid/indexes/validators/options.rb +2 -2
  20. data/lib/mongoid/multi_parameter_attributes.rb +1 -1
  21. data/lib/mongoid/nested_attributes.rb +1 -1
  22. data/lib/mongoid/persistence.rb +1 -1
  23. data/lib/mongoid/persistence/insertion.rb +1 -0
  24. data/lib/mongoid/railtie.rb +1 -15
  25. data/lib/mongoid/relations/accessors.rb +1 -1
  26. data/lib/mongoid/relations/builders/nested_attributes/many.rb +17 -6
  27. data/lib/mongoid/relations/builders/nested_attributes/one.rb +41 -50
  28. data/lib/mongoid/relations/embedded/batchable.rb +1 -1
  29. data/lib/mongoid/relations/metadata.rb +1 -0
  30. data/lib/mongoid/relations/referenced/many.rb +5 -1
  31. data/lib/mongoid/relations/targets/enumerable.rb +10 -0
  32. data/lib/mongoid/threaded.rb +22 -4
  33. data/lib/mongoid/validations/uniqueness.rb +1 -2
  34. data/lib/mongoid/version.rb +1 -1
  35. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +13 -1
  36. metadata +3 -3
@@ -3,7 +3,63 @@
3
3
  For instructions on upgrading to newer versions, visit
4
4
  [mongoid.org](http://mongoid.org/docs/upgrading.html).
5
5
 
6
- ## 3.0.6 (branch 3.0.0-stable)
6
+ ## 3.0.9
7
+
8
+ ### Resolved Issues
9
+
10
+ * \#2463 Fixed the broken `rails g mongoid:config` from a fresh repo.
11
+
12
+ * \#2456 The descendants cache is now reset when the document is inherited
13
+ again. (Kostyantyn Stepanyuk)
14
+
15
+ * \#2453 `Model#write_attribute` now properly works with aliased fields.
16
+ (Campbell Allen)
17
+
18
+ * \#2444 Removed extra dirty methods creation call. (Kostyantyn Stepanyuk)
19
+
20
+ * \#2440/\#2435 Pass mass assignment options down to children when setting via
21
+ nested attributes or embedded documents.
22
+
23
+ * \#2439 Fixed memory leak in threaded selection of returned fields.
24
+ (Tim Olsen)
25
+
26
+ * mongoid/moped\#82 Aliased fields now work with `Criteria#distinct`.
27
+
28
+ * \#2423 Fixed embedded document's `update_all` to perform the correct $set
29
+ when using off a criteria.
30
+
31
+ * \#2414 Index definitions now respect aliased fields.
32
+
33
+ * \#2413 Enumerable targets now properly return enumerators when no blocks
34
+ are provided. (Andrew Smith)
35
+
36
+ * \#2411 BigDecimal fields are properly stored as strings when mongoizing
37
+ integers and floats.
38
+
39
+ * \#2409 Don't warn about missing mongoid.yml if configured programatically.
40
+
41
+ * \#2403 Return false on `update_all` of an embeds many with no documents.
42
+
43
+ * \#2401 Bring back the ability to merge a criteria with a hash.
44
+
45
+ * \#2399 Reject blank id values on has_many `Model#object_ids=`.
46
+ (Tiago Rafael Godinho)
47
+
48
+ * \#2393 Ensure `inverse_of` is respected when using polymorphic relations.
49
+
50
+ * \#2388 Map/reduce properly uses `sort` instead of `orderby` in the execution
51
+ of the command. (Alex Tsibulya)
52
+
53
+ * \#2386 Allow geo haystack and bits parameters in indexes. (Bradley Rees)
54
+
55
+ * \#2380 `Model#becomes` now properly copies over dirty attributes.
56
+
57
+ * \#2331 Don't double push child documents when extra saves are called in an
58
+ after_create callback.
59
+
60
+ ## 3.0.8 (Yanked)
61
+
62
+ ## 3.0.6
7
63
 
8
64
  ### Resolved Issues
9
65
 
@@ -152,8 +152,12 @@ en:
152
152
  \_\_drop_dups: true|false\n
153
153
  \_\_name: 'index_name'\n
154
154
  \_\_sparse: true|false\n
155
- \_\_unique: true|false\n\n
156
- Valid types are: 1, -1, '2d'\n\n
155
+ \_\_unique: true|false\n
156
+ \_\_min: 1\n
157
+ \_\_max: 1\n
158
+ \_\_bits: 26\n
159
+ \_\_bucket_size : 1\n
160
+ Valid types are: 1, -1, '2d', 'geoHaystack'\n\n
157
161
  Example:\n
158
162
  \_\_class Band\n
159
163
  \_\_\_\_include Mongoid::Document\n
@@ -119,7 +119,7 @@ module Mongoid
119
119
  #
120
120
  # @since 1.0.0
121
121
  def write_attribute(name, value)
122
- access = name.to_s
122
+ access = database_field_name(name.to_s)
123
123
  if attribute_writable?(access)
124
124
  _assigning do
125
125
  localized = fields[access].try(:localized?)
@@ -19,19 +19,41 @@ module Mongoid
19
19
  #
20
20
  # @since 2.0.0.rc.7
21
21
  def process_attributes(attrs = nil, role = :default, guard_protected_attributes = true)
22
- attrs ||= {}
23
- if !attrs.empty?
24
- attrs = sanitize_for_mass_assignment(attrs, role) if guard_protected_attributes
25
- attrs.each_pair do |key, value|
26
- next if pending_attribute?(key, value)
27
- process_attribute(key, value)
22
+ with_mass_assignment(role, guard_protected_attributes) do
23
+ attrs ||= {}
24
+ if !attrs.empty?
25
+ attrs = sanitize_for_mass_assignment(attrs, role) if guard_protected_attributes
26
+ attrs.each_pair do |key, value|
27
+ next if pending_attribute?(key, value)
28
+ process_attribute(key, value)
29
+ end
28
30
  end
31
+ yield self if block_given?
32
+ process_pending
29
33
  end
30
- yield self if block_given?
31
- process_pending
32
34
  end
33
35
 
34
- protected
36
+ private
37
+
38
+ # Get the current mass assignment options for this model.
39
+ #
40
+ # @api private
41
+ #
42
+ # @return [ Hash ] The mass assignment options.
43
+ #
44
+ # @since 3.0.7
45
+ def mass_assignment_options
46
+ @mass_assignment_options ||= {}
47
+ end
48
+
49
+ # Set the mass assignment options for the current model.
50
+ #
51
+ # @api private
52
+ #
53
+ # @return [ Hash ] The mass assignment options.
54
+ #
55
+ # @since 3.0.7
56
+ attr_writer :mass_assignment_options
35
57
 
36
58
  # If the key provided is the name of a relation or a nested attribute, we
37
59
  # need to wait until all other attributes are set before processing
@@ -121,6 +143,8 @@ module Mongoid
121
143
  # @example Process the pending items.
122
144
  # document.process_pending
123
145
  #
146
+ # @param [ Hash ] options The mass assignment options.
147
+ #
124
148
  # @since 2.0.0.rc.7
125
149
  def process_pending
126
150
  process_nested and process_relations
@@ -133,18 +157,41 @@ module Mongoid
133
157
  # @example Process the relations.
134
158
  # document.process_relations
135
159
  #
160
+ # @param [ Hash ] options The mass assignment options.
161
+ #
136
162
  # @since 2.0.0.rc.7
137
163
  def process_relations
138
164
  pending_relations.each_pair do |name, value|
139
165
  metadata = relations[name]
140
166
  if value.is_a?(Hash)
141
- metadata.nested_builder(value, {}).build(self)
167
+ metadata.nested_builder(value, {}).build(self, mass_assignment_options)
142
168
  else
143
169
  send("#{name}=", value)
144
170
  end
145
171
  end
146
172
  end
173
+
174
+ # Execute the block with the provided mass assignment options set.
175
+ #
176
+ # @api private
177
+ #
178
+ # @example Execute with mass assignment.
179
+ # model.with_mass_assignment(:default, true)
180
+ #
181
+ # @param [ Symbol ] role The role.
182
+ # @param [ true, false ] guard_protected_attributes To enable mass
183
+ # assignment.
184
+ #
185
+ # @since 3.0.7
186
+ def with_mass_assignment(role, guard_protected_attributes)
187
+ begin
188
+ self.mass_assignment_options =
189
+ { as: role, without_protection: !guard_protected_attributes }
190
+ yield
191
+ ensure
192
+ self.mass_assignment_options = nil
193
+ end
194
+ end
147
195
  end
148
196
  end
149
197
  end
150
-
@@ -26,6 +26,19 @@ module Mongoid
26
26
  option :use_activesupport_time_zone, default: true
27
27
  option :use_utc, default: false
28
28
 
29
+ # Has Mongoid been configured? This is checking that at least a valid
30
+ # session config exists.
31
+ #
32
+ # @example Is Mongoid configured?
33
+ # config.configured?
34
+ #
35
+ # @return [ true, false ] If Mongoid is configured.
36
+ #
37
+ # @since 3.0.9
38
+ def configured?
39
+ sessions.has_key?(:default)
40
+ end
41
+
29
42
  # Connect to the provided database name on the default session.
30
43
  #
31
44
  # @note Use only in development or test environments for convenience.
@@ -216,7 +216,7 @@ module Mongoid
216
216
  def apply_criteria_options
217
217
  command[:query] = criteria.selector
218
218
  if sort = criteria.options[:sort]
219
- command[:orderby] = sort
219
+ command[:sort] = sort
220
220
  end
221
221
  if limit = criteria.options[:limit]
222
222
  command[:limit] = limit
@@ -281,14 +281,15 @@ module Mongoid
281
281
  #
282
282
  # @since 3.0.4
283
283
  def update_documents(attributes, docs)
284
- return false unless attributes
285
- updates = {}
284
+ return false if !attributes || docs.empty?
285
+ updates = { "$set" => {}}
286
286
  docs.each do |doc|
287
287
  @selector ||= root.atomic_selector
288
288
  doc.write_attributes(attributes)
289
- updates.merge!(doc.atomic_position => attributes)
289
+ updates["$set"].merge!(doc.atomic_updates["$set"])
290
+ doc.move_changes
290
291
  end
291
- collection.find(selector).update("$set" => updates)
292
+ collection.find(selector).update(updates)
292
293
  end
293
294
 
294
295
  # Get the limiting value.
@@ -113,7 +113,7 @@ module Mongoid
113
113
  #
114
114
  # @since 3.0.0
115
115
  def distinct(field)
116
- query.distinct(field)
116
+ query.distinct(klass.database_field_name(field))
117
117
  end
118
118
 
119
119
  # Iterate over the context. If provided a block, yield to a Mongoid
@@ -602,7 +602,7 @@ module Mongoid
602
602
  Threaded.set_selection(criteria.object_id, fields) unless fields.blank?
603
603
  yield
604
604
  ensure
605
- Threaded.set_selection(criteria.object_id, nil)
605
+ Threaded.delete_selection(criteria.object_id)
606
606
  end
607
607
  end
608
608
 
@@ -320,7 +320,7 @@ module Mongoid
320
320
  # @since 1.0.0
321
321
  def initialize(klass)
322
322
  @klass = klass
323
- super(klass.aliased_fields, klass.fields)
323
+ klass ? super(klass.aliased_fields, klass.fields) : super({}, {})
324
324
  end
325
325
 
326
326
  # Eager loads all the provided relations. Will load all the documents
@@ -390,6 +390,15 @@ module Mongoid
390
390
  # @example Merge the criteria with another criteria.
391
391
  # criteri.merge(other_criteria)
392
392
  #
393
+ # @example Merge the criteria with a hash. The hash must contain a klass
394
+ # key and the key/value pairs correspond to method names/args.
395
+
396
+ # criteria.merge({
397
+ # klass: Band,
398
+ # where: { name: "Depeche Mode" },
399
+ # order_by: { name: 1 }
400
+ # })
401
+ #
393
402
  # @param [ Criteria ] other The other criterion to merge with.
394
403
  #
395
404
  # @return [ Criteria ] A cloned self.
@@ -264,7 +264,6 @@ module Mongoid
264
264
  create_dirty_default_change_check(name, meth)
265
265
  create_dirty_previous_value_accessor(name, meth)
266
266
  create_dirty_reset(name, meth)
267
- create_dirty_reset(name, meth)
268
267
  end
269
268
 
270
269
  # Creates the dirty change accessor.
@@ -215,7 +215,8 @@ module Mongoid
215
215
  unless klass.include?(Mongoid::Document)
216
216
  raise ArgumentError, "A class which includes Mongoid::Document is expected"
217
217
  end
218
- became = klass.instantiate(as_document.__deep_copy__)
218
+ became = klass.new(as_document.__deep_copy__)
219
+ became.instance_variable_set(:@changed_attributes, changed_attributes)
219
220
  became.instance_variable_set(:@errors, errors)
220
221
  became.instance_variable_set(:@new_record, new_record?)
221
222
  became.instance_variable_set(:@destroyed, destroyed?)
@@ -25,7 +25,7 @@ module Mongoid
25
25
  #
26
26
  # @since 3.0.0
27
27
  def __find_args__
28
- flat_map{ |a| a.__find_args__ }.uniq_by{ |a| a.to_s }
28
+ flat_map{ |a| a.__find_args__ }.uniq{ |a| a.to_s }
29
29
  end
30
30
 
31
31
  # Mongoize the array into an array of object ids.
@@ -45,6 +45,21 @@ module Mongoid
45
45
  object.numeric? ? ::BigDecimal.new(object.to_s) : object
46
46
  end
47
47
  end
48
+
49
+ # Mongoize an object of any type to how it's stored in the db as a big
50
+ # decimal.
51
+ #
52
+ # @example Mongoize the object.
53
+ # BigDecimal.mongoize(123)
54
+ #
55
+ # @param [ Object ] object The object to Mongoize
56
+ #
57
+ # @return [ String ] The mongoized object.
58
+ #
59
+ # @since 3.0.7
60
+ def mongoize(object)
61
+ object ? object.to_s : object
62
+ end
48
63
  end
49
64
  end
50
65
  end
@@ -97,6 +97,24 @@ module Mongoid
97
97
  true
98
98
  end
99
99
 
100
+ # Convert this hash to a criteria. Will iterate over each keys in the
101
+ # hash which must correspond to method on a criteria object. The hash
102
+ # must also include a "klass" key.
103
+ #
104
+ # @example Convert the hash to a criteria.
105
+ # { klass: Band, where: { name: "Depeche Mode" }.to_criteria
106
+ #
107
+ # @return [ Criteria ] The criteria.
108
+ #
109
+ # @since 3.0.7
110
+ def to_criteria
111
+ criteria = Criteria.new(delete(:klass) || delete("klass"))
112
+ each_pair do |method, args|
113
+ criteria = criteria.__send__(method, args)
114
+ end
115
+ criteria
116
+ end
117
+
100
118
  module ClassMethods
101
119
 
102
120
  # Turn the object from the ruby type we deal with to a Mongo friendly
@@ -12,7 +12,7 @@ module Mongoid
12
12
  #
13
13
  # @param [ Class ] klass The class to instantiate from if _type is not present.
14
14
  # @param [ Hash ] attributes The document attributes.
15
- # @param [ Hash ] optiosn The mass assignment scoping options.
15
+ # @param [ Hash ] options The mass assignment scoping options.
16
16
  #
17
17
  # @return [ Document ] The instantiated document.
18
18
  def build(klass, attributes = nil, options = {})
@@ -109,6 +109,21 @@ module Mongoid
109
109
  self.class.attribute_names
110
110
  end
111
111
 
112
+ # Get the name of the provided field as it is stored in the database.
113
+ # Used in determining if the field is aliased or not.
114
+ #
115
+ # @example Get the database field name.
116
+ # model.database_field_name(:authorization)
117
+ #
118
+ # @param [ String, Symbol ] name The name to get.
119
+ #
120
+ # @return [ String ] The name of the field as it's stored in the db.
121
+ #
122
+ # @since 3.0.7
123
+ def database_field_name(name)
124
+ self.class.database_field_name(name)
125
+ end
126
+
112
127
  # Is the document using object ids?
113
128
  #
114
129
  # @note Refactored from using delegate for class load performance.
@@ -175,6 +190,22 @@ module Mongoid
175
190
  fields.keys
176
191
  end
177
192
 
193
+ # Get the name of the provided field as it is stored in the database.
194
+ # Used in determining if the field is aliased or not.
195
+ #
196
+ # @example Get the database field name.
197
+ # Model.database_field_name(:authorization)
198
+ #
199
+ # @param [ String, Symbol ] name The name to get.
200
+ #
201
+ # @return [ String ] The name of the field as it's stored in the db.
202
+ #
203
+ # @since 3.0.7
204
+ def database_field_name(name)
205
+ normalized = name.to_s
206
+ aliased_fields[normalized] || normalized
207
+ end
208
+
178
209
  # Defines all the fields that are accessible on the Document
179
210
  # For each field that is defined, a getter and setter will be
180
211
  # added as an instance method to the Document.
@@ -44,6 +44,20 @@ module Mongoid
44
44
  children
45
45
  end
46
46
 
47
+ # Marks all children as being persisted.
48
+ #
49
+ # @example Flag all the children.
50
+ # document.flag_children_persisted
51
+ #
52
+ # @return [ Array<Document> ] The flagged children.
53
+ #
54
+ # @since 3.0.7
55
+ def flag_children_persisted
56
+ _children.each do |child|
57
+ child.new_record = false
58
+ end
59
+ end
60
+
47
61
  # Determines if the document is a subclass of another document.
48
62
  #
49
63
  # @example Check if the document is a subclass
@@ -141,6 +155,7 @@ module Mongoid
141
155
  # @since 2.0.0.rc.6
142
156
  def inherited(subclass)
143
157
  super
158
+ @_type = nil
144
159
  subclass.fields = fields.dup
145
160
  subclass.pre_processed_defaults = pre_processed_defaults.dup
146
161
  subclass.post_processed_defaults = post_processed_defaults.dup
@@ -78,7 +78,7 @@ module Mongoid
78
78
  # @since 1.0.0
79
79
  def index(spec, options = nil)
80
80
  Validators::Options.validate(self, spec, options || {})
81
- index_options[spec] = normalize_index_options(options)
81
+ index_options[normalize_spec(spec)] = normalize_index_options(options)
82
82
  end
83
83
 
84
84
  private
@@ -88,7 +88,7 @@ module Mongoid
88
88
  # @api private
89
89
  #
90
90
  # @example Normalize the index options.
91
- # Model.normalize_index_options(drop_dups: true)
91
+ # Model.normalize_index_options(drop_dups: true)
92
92
  #
93
93
  # @param [ Hash ] options The index options.
94
94
  #
@@ -98,8 +98,28 @@ module Mongoid
98
98
  def normalize_index_options(options)
99
99
  opts = options || {}
100
100
  opts[:dropDups] = opts.delete(:drop_dups) if opts.has_key?(:drop_dups)
101
+ opts[:bucketSize] = opts.delete(:bucket_size) if opts.has_key?(:bucket_size)
101
102
  opts
102
103
  end
104
+
105
+ # Normalize the spec, in case aliased fields are provided.
106
+ #
107
+ # @api private
108
+ #
109
+ # @example Normalize the spec.
110
+ # Model.normalize_spec(name: 1)
111
+ #
112
+ # @param [ Hash ] spec The index specification.
113
+ #
114
+ # @return [ Hash ] The normalized specification.
115
+ #
116
+ # @since 3.0.7
117
+ def normalize_spec(spec)
118
+ spec.inject({}) do |normalized, (name, direction)|
119
+ normalized[database_field_name(name).to_sym] = direction
120
+ normalized
121
+ end
122
+ end
103
123
  end
104
124
  end
105
125
  end
@@ -7,8 +7,8 @@ module Mongoid
7
7
  module Options
8
8
  extend self
9
9
 
10
- VALID_OPTIONS = [ :background, :drop_dups, :name, :sparse, :unique, :max, :min ]
11
- VALID_TYPES = [ 1, -1, "2d" ]
10
+ VALID_OPTIONS = [ :background, :drop_dups, :name, :sparse, :unique, :max, :min, :bits, :bucket_size ]
11
+ VALID_TYPES = [ 1, -1, "2d", "geoHaystack" ]
12
12
 
13
13
  # Validate the index specification.
14
14
  #
@@ -66,7 +66,7 @@ module Mongoid
66
66
  multi_parameter_attributes.each_pair do |key, values|
67
67
  begin
68
68
  values = (values.keys.min..values.keys.max).map { |i| values[i] }
69
- field = self.class.fields[aliased_fields[key] || key]
69
+ field = self.class.fields[database_field_name(key)]
70
70
  attributes[key] = instantiate_object(field, values)
71
71
  rescue => e
72
72
  errors << Errors::AttributeAssignmentError.new(
@@ -52,7 +52,7 @@ module Mongoid
52
52
  autosave(metadata.merge!(autosave: true))
53
53
  re_define_method(meth) do |attrs|
54
54
  _assigning do
55
- metadata.nested_builder(attrs, options).build(self)
55
+ metadata.nested_builder(attrs, options).build(self, mass_assignment_options)
56
56
  end
57
57
  end
58
58
  end
@@ -159,7 +159,7 @@ module Mongoid
159
159
  unless attribute_writable?(normalized)
160
160
  raise Errors::ReadonlyAttribute.new(normalized, value)
161
161
  end
162
- write_attribute(aliased_fields[normalized] || normalized, value)
162
+ write_attribute(database_field_name(normalized), value)
163
163
  save(validate: false)
164
164
  end
165
165
 
@@ -24,6 +24,7 @@ module Mongoid
24
24
  document.run_callbacks(:create) do
25
25
  yield(document)
26
26
  document.new_record = false
27
+ document.flag_children_persisted
27
28
  true
28
29
  end
29
30
  end
@@ -60,20 +60,6 @@ module Rails
60
60
 
61
61
  # Initialize Mongoid. This will look for a mongoid.yml in the config
62
62
  # directory and configure mongoid appropriately.
63
- #
64
- # @example mongoid.yml
65
- #
66
- # development:
67
- # host: localhost
68
- # database: mongoid
69
- # slaves:
70
- # # - host: localhost
71
- # # port: 27018
72
- # # - host: localhost
73
- # # port: 27019
74
- # allow_dynamic_fields: false
75
- # persist_in_safe_mode: false
76
- #
77
63
  initializer "setup database" do
78
64
  config_file = Rails.root.join("config", "mongoid.yml")
79
65
  if config_file.file?
@@ -95,7 +81,7 @@ module Rails
95
81
  # alert to create one.
96
82
  initializer "warn when configuration is missing" do
97
83
  config.after_initialize do
98
- unless Rails.root.join("config", "mongoid.yml").file?
84
+ unless Rails.root.join("config", "mongoid.yml").file? || ::Mongoid.configured?
99
85
  puts "\nMongoid config not found. Create a config file at: config/mongoid.yml"
100
86
  puts "to generate one run: rails generate mongoid:config\n\n"
101
87
  end
@@ -228,7 +228,7 @@ module Mongoid
228
228
  def ids_setter(name, metadata)
229
229
  ids_method = "#{name.to_s.singularize}_ids="
230
230
  re_define_method(ids_method) do |ids|
231
- send(metadata.setter, metadata.klass.find(ids))
231
+ send(metadata.setter, metadata.klass.find(ids.reject(&:blank?)))
232
232
  end
233
233
  self
234
234
  end
@@ -16,18 +16,19 @@ module Mongoid
16
16
  # many.build(person)
17
17
  #
18
18
  # @param [ Document ] parent The parent document of the relation.
19
+ # @param [ Hash ] options The mass assignment options.
19
20
  #
20
21
  # @return [ Array ] The attributes.
21
- def build(parent)
22
+ def build(parent, options = {})
22
23
  @existing = parent.send(metadata.name)
23
24
  if over_limit?(attributes)
24
25
  raise Errors::TooManyNestedAttributeRecords.new(existing, options[:limit])
25
26
  end
26
27
  attributes.each do |attrs|
27
28
  if attrs.respond_to?(:with_indifferent_access)
28
- process_attributes(parent, attrs)
29
+ process_attributes(parent, attrs, options)
29
30
  else
30
- process_attributes(parent, attrs[1])
31
+ process_attributes(parent, attrs[1], options)
31
32
  end
32
33
  end
33
34
  end
@@ -85,11 +86,17 @@ module Mongoid
85
86
  # Process each set of attributes one at a time for each potential
86
87
  # new, existing, or ignored document.
87
88
  #
89
+ # @api private
90
+ #
88
91
  # @example Process the attributes
89
92
  # builder.process_attributes({ "id" => 1, "street" => "Bond" })
90
93
  #
94
+ # @param [ Document ] parent The parent document.
91
95
  # @param [ Hash ] attrs The single document attributes to process.
92
- def process_attributes(parent, attrs)
96
+ # @param [ Hash ] options the mass assignment options.
97
+ #
98
+ # @since 2.0.0
99
+ def process_attributes(parent, attrs, options)
93
100
  return if reject?(parent, attrs)
94
101
  if id = attrs.extract_id
95
102
  first = existing.first
@@ -100,10 +107,14 @@ module Mongoid
100
107
  doc.destroy unless doc.embedded? || doc.destroyed?
101
108
  else
102
109
  attrs.delete_id
103
- metadata.embedded? ? doc.attributes = attrs : doc.update_attributes(attrs)
110
+ if metadata.embedded?
111
+ doc.assign_attributes(attrs, options)
112
+ else
113
+ doc.update_attributes(attrs, options)
114
+ end
104
115
  end
105
116
  else
106
- existing.push(Factory.build(metadata.klass, attrs)) unless destroyable?(attrs)
117
+ existing.push(Factory.build(metadata.klass, attrs, options)) unless destroyable?(attrs)
107
118
  end
108
119
  end
109
120
  end
@@ -10,25 +10,27 @@ module Mongoid
10
10
  # Builds the relation depending on the attributes and the options
11
11
  # passed to the macro.
12
12
  #
13
- # This attempts to perform 3 operations, either one of an update of
14
- # the existing relation, a replacement of the relation with a new
15
- # document, or a removal of the relation.
13
+ # @example Build a 1-1 nested document.
14
+ # one.build(person, as: :admin)
16
15
  #
17
- # Example:
16
+ # @note This attempts to perform 3 operations, either one of an update of
17
+ # the existing relation, a replacement of the relation with a new
18
+ # document, or a removal of the relation.
18
19
  #
19
- # <tt>one.build(person)</tt>
20
+ # @param [ Document ] parent The parent document.
21
+ # @param [ Hash ] options The mass assignment options.
20
22
  #
21
- # Options:
23
+ # @return [ Document ] The built document.
22
24
  #
23
- # parent: The parent document of the relation.
24
- def build(parent)
25
+ # @since 2.0.0
26
+ def build(parent, options = {})
25
27
  return if reject?(parent, attributes)
26
28
  @existing = parent.send(metadata.name)
27
29
  if update?
28
30
  attributes.delete_id
29
- existing.attributes = attributes
31
+ existing.assign_attributes(attributes, options)
30
32
  elsif replace?
31
- parent.send(metadata.setter, Factory.build(metadata.klass, attributes))
33
+ parent.send(metadata.setter, Factory.build(metadata.klass, attributes, options))
32
34
  elsif delete?
33
35
  parent.send(metadata.setter, nil)
34
36
  end
@@ -37,19 +39,14 @@ module Mongoid
37
39
  # Create the new builder for nested attributes on one-to-one
38
40
  # relations.
39
41
  #
40
- # Example:
42
+ # @example Instantiate the builder.
43
+ # One.new(metadata, attributes, options)
41
44
  #
42
- # <tt>One.new(metadata, attributes, options)</tt>
45
+ # @param [ Metadata ] metadata The relation metadata.
46
+ # @param [ Hash ] attributes The attributes hash to attempt to set.
47
+ # @param [ Hash ] options The options defined.
43
48
  #
44
- # Options:
45
- #
46
- # metadata: The relation metadata
47
- # attributes: The attributes hash to attempt to set.
48
- # options: The options defined.
49
- #
50
- # Returns:
51
- #
52
- # A new builder.
49
+ # @since 2.0.0
53
50
  def initialize(metadata, attributes, options)
54
51
  @attributes = attributes.with_indifferent_access
55
52
  @metadata = metadata
@@ -62,13 +59,14 @@ module Mongoid
62
59
  # Is the id in the attribtues acceptable for allowing an update to
63
60
  # the existing relation?
64
61
  #
65
- # Example:
62
+ # @api private
66
63
  #
67
- # <tt>acceptable_id?</tt>
64
+ # @example Is the id acceptable?
65
+ # one.acceptable_id?
68
66
  #
69
- # Returns:
67
+ # @return [ true, false ] If the id part of the logic will allow an update.
70
68
  #
71
- # True if the id part of the logic will allow an update.
69
+ # @since 2.0.0
72
70
  def acceptable_id?
73
71
  id = convert_id(existing.class, attributes[:id])
74
72
  existing.id == id || id.nil? || (existing.id != id && update_only?)
@@ -76,56 +74,49 @@ module Mongoid
76
74
 
77
75
  # Can the existing relation be deleted?
78
76
  #
79
- # Example:
77
+ # @example Can the existing object be deleted?
78
+ # one.delete?
80
79
  #
81
- # <tt>delete?</tt>
80
+ # @return [ true, false ] If the relation should be deleted.
82
81
  #
83
- # Returns:
84
- #
85
- # True if the relation should be deleted.
82
+ # @since 2.0.0
86
83
  def delete?
87
84
  destroyable? && !attributes[:id].nil?
88
85
  end
89
86
 
90
- # Can the existing relation potentially be deleted?
91
- #
92
- # Example:
87
+ # Can the existing relation potentially be destroyed?
93
88
  #
94
- # <tt>destroyable?({ :_destroy => "1" })</tt>
89
+ # @example Is the object destroyable?
90
+ # one.destroyable?({ :_destroy => "1" })
95
91
  #
96
- # Options:
92
+ # @return [ true, false ] If the relation can potentially be
93
+ # destroyed.
97
94
  #
98
- # attributes: The attributes to pull the flag from.
99
- #
100
- # Returns:
101
- #
102
- # True if the relation can potentially be deleted.
95
+ # @since 2.0.0
103
96
  def destroyable?
104
97
  [ 1, "1", true, "true" ].include?(destroy) && allow_destroy?
105
98
  end
106
99
 
107
100
  # Is the document to be replaced?
108
101
  #
109
- # Example:
102
+ # @example Is the document to be replaced?
103
+ # one.replace?
110
104
  #
111
- # <tt>replace?</tt>
105
+ # @return [ true, false ] If the document should be replaced.
112
106
  #
113
- # Returns:
114
- #
115
- # True if the document should be replaced.
107
+ # @since 2.0.0
116
108
  def replace?
117
109
  !existing && !destroyable? && !attributes.blank?
118
110
  end
119
111
 
120
112
  # Should the document be updated?
121
113
  #
122
- # Example:
123
- #
124
- # <tt>update?</tt>
114
+ # @example Should the document be updated?
115
+ # one.update?
125
116
  #
126
- # Returns:
117
+ # @return [ true, false ] If the object should have its attributes updated.
127
118
  #
128
- # True if the object should have its attributes updated.
119
+ # @since 2.0.0
129
120
  def update?
130
121
  existing && !destroyable? && acceptable_id?
131
122
  end
@@ -194,7 +194,7 @@ module Mongoid
194
194
  docs.map do |doc|
195
195
  attributes = { metadata: metadata, _parent: base }
196
196
  attributes.merge!(doc)
197
- Factory.build(klass, attributes)
197
+ Factory.build(klass, attributes, base.send(:mass_assignment_options))
198
198
  end
199
199
  else
200
200
  docs
@@ -1131,6 +1131,7 @@ module Mongoid
1131
1131
  #
1132
1132
  # @return [ Array<String> ] The inverse names.
1133
1133
  def lookup_inverses(other)
1134
+ return [ inverse_of ] if inverse_of
1134
1135
  if other
1135
1136
  matches = []
1136
1137
  other.class.relations.values.each do |meta|
@@ -163,7 +163,11 @@ module Mongoid
163
163
  #
164
164
  # @since 2.1.0
165
165
  def each
166
- target.each { |doc| yield(doc) if block_given? }
166
+ if block_given?
167
+ target.each { |doc| yield(doc) }
168
+ else
169
+ to_enum
170
+ end
167
171
  end
168
172
 
169
173
  # Determine if any documents in this relation exist in the database.
@@ -139,15 +139,25 @@ module Mongoid
139
139
  # the cursor while loading the documents and then iterates over the
140
140
  # _added docs.
141
141
  #
142
+ # If no block is passed then it returns an enumerator containing all
143
+ # docs.
144
+ #
142
145
  # @example Iterate over the enumerable.
143
146
  # enumerable.each do |doc|
144
147
  # puts doc
145
148
  # end
146
149
  #
150
+ # @example return an enumerator containing all the docs
151
+ #
152
+ # a = enumerable.each
153
+ #
147
154
  # @return [ true ] That the enumerable is now _loaded.
148
155
  #
149
156
  # @since 2.1.0
150
157
  def each
158
+ unless block_given?
159
+ return to_enum
160
+ end
151
161
  if _loaded?
152
162
  _loaded.each_pair do |id, doc|
153
163
  yield(doc)
@@ -301,13 +301,14 @@ module Mongoid
301
301
  # @example Get the field selection options.
302
302
  # Threaded.selection
303
303
  #
304
- # @param [ Class ] klass The model class.
304
+ # @param [ Integer ] criteria_instance_id The criteria instance id.
305
305
  #
306
306
  # @return [ Hash ] The field selection.
307
307
  #
308
308
  # @since 2.4.4
309
- def selection(klass)
310
- Thread.current["[mongoid][#{klass}]:selection"]
309
+ def selection(criteria_instance_id)
310
+ selections = Thread.current["[mongoid][selections]"]
311
+ selections[criteria_instance_id] if selections
311
312
  end
312
313
 
313
314
  # Set the field selection on the current thread.
@@ -322,7 +323,24 @@ module Mongoid
322
323
  #
323
324
  # @since 2.4.4
324
325
  def set_selection(criteria_instance_id, value)
325
- Thread.current["[mongoid][#{criteria_instance_id}]:selection"] = value
326
+ Thread.current["[mongoid][selections]"] ||= {}
327
+ Thread.current["[mongoid][selections]"][criteria_instance_id] = value
328
+ end
329
+
330
+ # Delete the field selection on the current thread.
331
+ #
332
+ # @example Delete the field selection.
333
+ # Threaded.delete_selection(Person)
334
+ #
335
+ # @param [ Integer ] criteria_instance_id The criteria instance id.
336
+ #
337
+ # @return [ Boolean ] Whether there was a field selection.
338
+ #
339
+ # @since 3.0.7
340
+ def delete_selection(criteria_instance_id)
341
+ selections = Thread.current["[mongoid][selections]"]
342
+ return false unless selections
343
+ !!selections.delete(criteria_instance_id)
326
344
  end
327
345
 
328
346
  # Get the global session override.
@@ -167,8 +167,7 @@ module Mongoid
167
167
  # @since 2.3.0
168
168
  def scope(criteria, document, attribute)
169
169
  Array.wrap(options[:scope]).each do |item|
170
- normalized = item.to_s
171
- name = document.aliased_fields[normalized] || normalized
170
+ name = document.database_field_name(item)
172
171
  criteria = criteria.where(item => document.attributes[name])
173
172
  end
174
173
  criteria = criteria.where(deleted_at: nil) if document.paranoid?
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "3.0.6"
3
+ VERSION = "3.0.9"
4
4
  end
@@ -18,7 +18,15 @@ development:
18
18
  # Change the default consistency model to :eventual or :strong.
19
19
  # :eventual will send reads to secondaries, :strong sends everything
20
20
  # to master. (default: :eventual)
21
- consistency: :strong
21
+ # consistency: :eventual
22
+
23
+ # How many times Moped should attempt to retry an operation after
24
+ # failure. (default: 30)
25
+ # max_retries: 30
26
+
27
+ # The time in seconds that Moped should wait before retrying an
28
+ # operation on failure. (default: 1)
29
+ # retry_interval: 1
22
30
  # Configure Mongoid specific options. (optional)
23
31
  options:
24
32
  # Configuration for whether or not to allow access to fields that do
@@ -66,3 +74,7 @@ test:
66
74
  - localhost:27017
67
75
  options:
68
76
  consistency: :strong
77
+ # In the test environment we lower the retries and retry interval to
78
+ # low amounts for fast failures.
79
+ max_retries: 1
80
+ retry_interval: 0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.6
4
+ version: 3.0.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-15 00:00:00.000000000 Z
12
+ date: 2012-10-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -352,7 +352,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
352
352
  version: '0'
353
353
  segments:
354
354
  - 0
355
- hash: -3437651563723863969
355
+ hash: 3436548235215749672
356
356
  required_rubygems_version: !ruby/object:Gem::Requirement
357
357
  none: false
358
358
  requirements: