mongoid 3.0.6 → 3.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: