mongoid 3.0.1 → 3.0.2

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.
@@ -3,6 +3,53 @@
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.2 (branch: 3.0.0-stable)
7
+
8
+ ### Resolved Issues
9
+
10
+ * \#2244 Get rid of id mass assignment warnings in nested attributes.
11
+
12
+ * \#2242 Fix eager loading not to load all documents when calling first or
13
+ last.
14
+
15
+ * \#2241 Map/reduce operations now always use strong consistency since they
16
+ have the potential to write to collections, most of the time.
17
+
18
+ * \#2238 Ensure n-n foreign key fields are flagged as resizable to prevent
19
+ `nil` -> `[]` changes when using `#only` and updating.
20
+
21
+ * \#2236 Keep the instance of the document in the validations exception
22
+ accessible via `document` or `record`.
23
+
24
+ * \#2234 Ensure validations when document is getting persisted with custom
25
+ options work properly with custom options, and do not clear them out if
26
+ validation passes.
27
+
28
+ * \#2224 `Model#inc` now accepts `BigDecimal` values.
29
+
30
+ * \#2216 Fixed assignment of metadata on embeds one relations when setting
31
+ multiple times in a row.
32
+
33
+ * \#2212 Ensure errors are cleared after a save with `validate: false` in all
34
+ situations.
35
+
36
+ * \#2207 When eager loading ids the query must be duped to avoid multiple
37
+ iteration problems not getting the required fields.
38
+
39
+ * \#2204 Raise an `InvalidIncludes` error when passing arguments to
40
+ `Criteria.includes` that are invalid (not relations, or more than 1 level.)
41
+
42
+ * \#2203 Map/Reduce now works properly in conjunction with `Model#with`.
43
+
44
+ Band.
45
+ with(session: "secondary").
46
+ where(:likes.gt => 100).
47
+ map_reduce(map, reduce).
48
+ out(inline: 1)
49
+
50
+ * \#2199 Autosave false is now respected when automatically adding
51
+ presence validation. (John Nishinaga)
52
+
6
53
  ## 3.0.1
7
54
 
8
55
  ### Resolved Issues
@@ -132,6 +132,15 @@ en:
132
132
  \_\_\_\_include Mongoid::Document\n
133
133
  \_\_\_\_field :%{name}, %{option}: true\n
134
134
  \_\_end\n\n"
135
+ invalid_includes:
136
+ message: "Invalid includes directive: %{klass}.includes(%{args})"
137
+ summary: "Eager loading in Mongoid only supports providing arguments
138
+ to %{klass}.includes that are the names of relations on the %{klass}
139
+ model, and only supports one level of eager loading. (ie, eager loading
140
+ associations not on the %{klass} but one step away via another relation
141
+ is not allowed."
142
+ resolution: "Ensure that each parameter passed to %{klass}.includes is
143
+ a valid name of a relation on the %{klass} model. These are: %{relations}."
135
144
  invalid_index:
136
145
  message: "Invalid index specification on %{klass}: %{spec}, %{options}"
137
146
  summary: "Indexes in Mongoid are defined as a hash of field name and
@@ -3,6 +3,10 @@ module Mongoid
3
3
  module Contextual
4
4
  module Command
5
5
 
6
+ # @attribute [r] collection The collection to query against.
7
+ # @attribute [r] criteria The criteria for the context.
8
+ attr_reader :collection, :criteria
9
+
6
10
  # The database command that is being built to send to the db.
7
11
  #
8
12
  # @example Get the command.
@@ -15,18 +19,6 @@ module Mongoid
15
19
  @command ||= {}
16
20
  end
17
21
 
18
- # Get the criteria for the command.
19
- #
20
- # @example Get the criteria.
21
- # command.criteria
22
- #
23
- # @return [ Criteria ] The criteria.
24
- #
25
- # @since 3.0.0
26
- def criteria
27
- @criteria
28
- end
29
-
30
22
  # Get the database session.
31
23
  #
32
24
  # @example Get the session.
@@ -36,7 +28,7 @@ module Mongoid
36
28
  #
37
29
  # @since 3.0.0
38
30
  def session
39
- criteria.klass.mongo_session
31
+ collection.database.session
40
32
  end
41
33
  end
42
34
  end
@@ -25,9 +25,10 @@ module Mongoid
25
25
  # @option options [ true, false ] :upsert Create the document if it doesn't exist.
26
26
  #
27
27
  # @since 3.0.0
28
- def initialize(criteria, update, options = {})
29
- @criteria, @options, @update = criteria, options, update
30
- @query = criteria.klass.collection.find(criteria.selector)
28
+ def initialize(collection, criteria, update, options = {})
29
+ @collection, @criteria, @options, @update =
30
+ collection, criteria, options, update
31
+ @query = collection.find(criteria.selector)
31
32
  apply_criteria_options
32
33
  end
33
34
 
@@ -78,9 +78,9 @@ module Mongoid
78
78
  # @param [ String ] reduce The reduce js function.
79
79
  #
80
80
  # @since 3.0.0
81
- def initialize(criteria, map, reduce)
82
- @criteria = criteria
83
- command[:mapreduce] = criteria.klass.collection_name.to_s
81
+ def initialize(collection, criteria, map, reduce)
82
+ @collection, @criteria = collection, criteria
83
+ command[:mapreduce] = collection.name.to_s
84
84
  command[:map], command[:reduce] = map, reduce
85
85
  apply_criteria_options
86
86
  end
@@ -266,7 +266,7 @@ module Mongoid
266
266
  # @since 3.0.0
267
267
  def results
268
268
  raise Errors::NoMapReduceOutput.new(command) unless command[:out]
269
- @results ||= session.command(command)
269
+ @results ||= session.with(consistency: :strong).command(command)
270
270
  end
271
271
  end
272
272
  end
@@ -12,10 +12,11 @@ module Mongoid
12
12
  include Aggregable::Mongo
13
13
  include Atomic
14
14
 
15
+ # @attribute [r] collection The collection to query against.
15
16
  # @attribute [r] criteria The criteria for the context.
16
17
  # @attribute [r] klass The klass for the criteria.
17
18
  # @attribute [r] query The Moped query.
18
- attr_reader :criteria, :klass, :query
19
+ attr_reader :collection, :criteria, :klass, :query
19
20
 
20
21
  # @attribute [rw] eager_loaded Has the context been eager loaded?
21
22
  attr_accessor :eager_loaded
@@ -66,7 +67,7 @@ module Mongoid
66
67
  def count(document = nil, &block)
67
68
  return super(&block) if block_given?
68
69
  return query.count unless document
69
- klass.collection.find(criteria.and(_id: document.id).selector).count
70
+ collection.find(criteria.and(_id: document.id).selector).count
70
71
  end
71
72
 
72
73
  # Delete all documents in the database that match the selector.
@@ -182,7 +183,7 @@ module Mongoid
182
183
  #
183
184
  # @since 3.0.0
184
185
  def find_and_modify(update, options = {})
185
- if doc = FindAndModify.new(criteria, update, options).result
186
+ if doc = FindAndModify.new(collection, criteria, update, options).result
186
187
  Factory.from_db(klass, doc)
187
188
  end
188
189
  end
@@ -212,8 +213,9 @@ module Mongoid
212
213
  # @since 3.0.0
213
214
  def initialize(criteria)
214
215
  @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
216
+ @collection = klass.collection
215
217
  add_type_selection
216
- @query = klass.collection.find(criteria.selector)
218
+ @query = collection.find(criteria.selector)
217
219
  apply_options
218
220
  end
219
221
 
@@ -269,7 +271,7 @@ module Mongoid
269
271
  #
270
272
  # @since 3.0.0
271
273
  def map_reduce(map, reduce)
272
- MapReduce.new(criteria, map, reduce)
274
+ MapReduce.new(collection, criteria, map, reduce)
273
275
  end
274
276
 
275
277
  # Skips the provided number of documents.
@@ -536,7 +538,7 @@ module Mongoid
536
538
  # @since 3.0.0
537
539
  def eager_loaded_ids(docs, metadata)
538
540
  if metadata.stores_foreign_key?
539
- load_ids(metadata.foreign_key).flatten
541
+ docs.flat_map{ |doc| doc.send(metadata.foreign_key) }
540
542
  else
541
543
  docs.map(&:id)
542
544
  end
@@ -582,23 +584,6 @@ module Mongoid
582
584
  @length = 0
583
585
  end
584
586
 
585
- # Loads an array of ids only for the current criteria. Used by eager
586
- # loading to determine the documents to load.
587
- #
588
- # @example Load the related ids.
589
- # criteria.load_ids("person_id")
590
- #
591
- # @param [ String ] key The id or foriegn key string.
592
- #
593
- # @return [ Array<String, Moped::BSON::ObjectId> ] The ids to load.
594
- #
595
- # @since 3.0.0
596
- def load_ids(key)
597
- query.select(key => 1).map do |doc|
598
- doc[key]
599
- end
600
- end
601
-
602
587
  # Apply all the optional criterion.
603
588
  #
604
589
  # @example Apply the options.
@@ -360,8 +360,9 @@ module Mongoid
360
360
  #
361
361
  # @since 2.2.0
362
362
  def includes(*relations)
363
- relations.each do |name|
363
+ relations.flatten.each do |name|
364
364
  metadata = klass.reflect_on_association(name)
365
+ raise Errors::InvalidIncludes.new(klass, relations) unless metadata
365
366
  inclusions.push(metadata) unless inclusions.include?(metadata)
366
367
  end
367
368
  clone
@@ -10,6 +10,7 @@ require "mongoid/errors/invalid_database"
10
10
  require "mongoid/errors/invalid_field"
11
11
  require "mongoid/errors/invalid_field_option"
12
12
  require "mongoid/errors/invalid_find"
13
+ require "mongoid/errors/invalid_includes"
13
14
  require "mongoid/errors/invalid_index"
14
15
  require "mongoid/errors/invalid_options"
15
16
  require "mongoid/errors/invalid_scope"
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Errors
4
+
5
+ # This error is raised when an invalid value is passed to an eager
6
+ # loading query.
7
+ class InvalidIncludes < MongoidError
8
+
9
+ # Initialize the error.
10
+ #
11
+ # @example Initialize the error.
12
+ # InvalidIncludes.new(Band, [ :members ])
13
+ #
14
+ # @param [ Class ] klass The model class.
15
+ # @param [ Array<Object> ] args The arguments passed to the includes.
16
+ #
17
+ # @since 3.0.2
18
+ def initialize(klass, args)
19
+ super(
20
+ compose_message(
21
+ "invalid_includes",
22
+ {
23
+ klass: klass.name,
24
+ args: args.map(&:inspect).join(", "),
25
+ relations: klass.relations.keys.map(&:inspect).join(", ")
26
+ }
27
+ )
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
@@ -8,7 +8,12 @@ module Mongoid
8
8
  # @example Create the error.
9
9
  # Validations.new(person.errors)
10
10
  class Validations < MongoidError
11
+ attr_reader :document
12
+ alias :record :document
13
+
11
14
  def initialize(document)
15
+ @document = document
16
+
12
17
  super(
13
18
  compose_message(
14
19
  "validations",
@@ -47,6 +47,18 @@ module Mongoid
47
47
  consolidated
48
48
  end
49
49
 
50
+ # Deletes an id value from the hash.
51
+ #
52
+ # @example Delete an id value.
53
+ # {}.delete_id
54
+ #
55
+ # @return [ Object ] The deleted value, or nil.
56
+ #
57
+ # @since 3.0.2
58
+ def delete_id
59
+ delete("_id") || delete("id") || delete(:id) || delete(:_id)
60
+ end
61
+
50
62
  # Get the id attribute from this hash, whether it's prefixed with an
51
63
  # underscore or is a symbol.
52
64
  #
@@ -100,6 +100,18 @@ module Mongoid
100
100
  metadata.polymorphic? ? true : metadata.klass.using_object_ids?
101
101
  end
102
102
 
103
+ # Returns true if an array, false if not.
104
+ #
105
+ # @example Is the field resizable?
106
+ # field.resizable?
107
+ #
108
+ # @return [ true, false ] If the field is resizable.
109
+ #
110
+ # @since 3.0.2
111
+ def resizable?
112
+ type.resizable?
113
+ end
114
+
103
115
  private
104
116
 
105
117
  # Evaluate the default proc. In some cases we need to instance exec,
@@ -72,7 +72,7 @@ module Mongoid
72
72
  # person.inc(:score, 2)
73
73
  #
74
74
  # @param [ Symbol ] field The name of the field.
75
- # @param [ Integer ] value The value to increment.
75
+ # @param [ Numeric ] value The value to increment.
76
76
  # @param [ Hash ] options The mongo persistence options.
77
77
  #
78
78
  # @return [ Array<Object> ] The new value of the field.
@@ -12,11 +12,12 @@ module Mongoid
12
12
  # @example Persist the new values.
13
13
  # inc.persist
14
14
  #
15
- # @return [ Object ] The new integer value.
15
+ # @return [ Object ] The new numeric value.
16
16
  #
17
17
  # @since 2.0.0
18
18
  def persist
19
19
  prepare do
20
+ self.value = value.to_f
20
21
  current = document[field] || 0
21
22
  document[field] = current + value
22
23
  execute("$inc")
@@ -29,6 +29,7 @@ module Mongoid
29
29
  end
30
30
  document.post_persist unless result == false
31
31
  end
32
+ document.errors.clear unless validating?
32
33
  document
33
34
  end
34
35
  end
@@ -26,6 +26,7 @@ module Mongoid
26
26
  end
27
27
  end
28
28
  document.post_persist unless result == false
29
+ document.errors.clear unless validating?
29
30
  result
30
31
  end
31
32
  end
@@ -99,6 +99,7 @@ module Mongoid
99
99
  existing.delete(doc)
100
100
  doc.destroy unless doc.embedded?
101
101
  else
102
+ attrs.delete_id
102
103
  metadata.embedded? ? doc.attributes = attrs : doc.update_attributes(attrs)
103
104
  end
104
105
  else
@@ -25,6 +25,7 @@ module Mongoid
25
25
  return if reject?(parent, attributes)
26
26
  @existing = parent.send(metadata.name)
27
27
  if update?
28
+ attributes.delete_id
28
29
  existing.attributes = attributes
29
30
  elsif replace?
30
31
  parent.send(metadata.setter, Factory.build(metadata.klass, attributes))
@@ -19,6 +19,7 @@ module Mongoid
19
19
  init(base, target, metadata) do
20
20
  characterize_one(target)
21
21
  bind_one
22
+ characterize_one(target)
22
23
  target.save if persistable?
23
24
  end
24
25
  end
@@ -38,12 +39,14 @@ module Mongoid
38
39
  if _assigning?
39
40
  base.add_atomic_unset(target)
40
41
  else
41
- destroy if persistable?
42
+ target.destroy if persistable?
42
43
  end
43
44
  unbind_one
44
45
  return nil unless replacement
45
46
  self.target = replacement
46
47
  bind_one
48
+ characterize_one(target)
49
+ target.save if persistable? && !_assigning?
47
50
  self
48
51
  end
49
52
 
@@ -151,7 +151,7 @@ module Mongoid
151
151
  def collection
152
152
  if opts = persistence_options
153
153
  coll = mongo_session.with(opts)[opts[:collection] || collection_name]
154
- clear_persistence_options
154
+ clear_persistence_options unless validating_with_query?
155
155
  coll
156
156
  else
157
157
  mongo_session[collection_name]
@@ -3,6 +3,7 @@ require "mongoid/validations/localizable"
3
3
  require "mongoid/validations/associated"
4
4
  require "mongoid/validations/format"
5
5
  require "mongoid/validations/length"
6
+ require "mongoid/validations/queryable"
6
7
  require "mongoid/validations/presence"
7
8
  require "mongoid/validations/uniqueness"
8
9
 
@@ -89,6 +90,18 @@ module Mongoid
89
90
  Threaded.validated?(self)
90
91
  end
91
92
 
93
+ # Are we currently performing a validation that has a query?
94
+ #
95
+ # @example Are we validating with a query?
96
+ # document.validating_with_query?
97
+ #
98
+ # @return [ true, false ] If we are validating with a query.
99
+ #
100
+ # @since 3.0.2
101
+ def validating_with_query?
102
+ self.class.validating_with_query?
103
+ end
104
+
92
105
  module ClassMethods
93
106
 
94
107
  # Validates whether or not an association is valid or not. Will correctly
@@ -194,13 +207,15 @@ module Mongoid
194
207
  if args.first == PresenceValidator
195
208
  args.last[:attributes].each do |name|
196
209
  metadata = relations[name.to_s]
197
- autosave(metadata.merge!(autosave: true)) if metadata
210
+ if metadata && metadata[:autosave] != false
211
+ autosave(metadata.merge!(autosave: true))
212
+ end
198
213
  end
199
214
  end
200
215
  super
201
216
  end
202
217
 
203
- protected
218
+ private
204
219
 
205
220
  # Adds an associated validator for the relation if the validate option
206
221
  # was not provided or set to true.
@@ -216,6 +231,18 @@ module Mongoid
216
231
  validates_associated(metadata.name)
217
232
  end
218
233
  end
234
+
235
+ # Are we currently performing a validation that has a query?
236
+ #
237
+ # @example Are we validating with a query?
238
+ # Model.validating_with_query?
239
+ #
240
+ # @return [ true, false ] If we are validating with a query.
241
+ #
242
+ # @since 3.0.2
243
+ def validating_with_query?
244
+ Threaded.executing?("#{name}-validate-with-query")
245
+ end
219
246
  end
220
247
  end
221
248
  end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Validations
4
+ module Queryable
5
+
6
+ # Wrap the validation inside the an execution block that alert's the
7
+ # session not to clear it's persistence options.
8
+ #
9
+ # @example Execute the validation with a query.
10
+ # with_query(document) do
11
+ # #...
12
+ # end
13
+ #
14
+ # @param [ Document ] document The document being validated.
15
+ #
16
+ # @return [ Object ] The result of the yield.
17
+ #
18
+ # @since 3.0.2
19
+ def with_query(document)
20
+ begin
21
+ Threaded.begin("#{klass.name}-validate-with-query")
22
+ yield
23
+ ensure
24
+ klass.clear_persistence_options unless document.errors.empty?
25
+ Threaded.exit("#{klass.name}-validate-with-query")
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -14,6 +14,8 @@ module Mongoid
14
14
  # validates_uniqueness_of :title
15
15
  # end
16
16
  class UniquenessValidator < ActiveModel::EachValidator
17
+ include Queryable
18
+
17
19
  attr_reader :klass
18
20
 
19
21
  # Unfortunately, we have to tie Uniqueness validators to a class.
@@ -41,12 +43,14 @@ module Mongoid
41
43
  #
42
44
  # @since 1.0.0
43
45
  def validate_each(document, attribute, value)
44
- attrib, val = to_validate(document, attribute, value)
45
- return unless validation_required?(document, attrib)
46
- if document.embedded?
47
- validate_embedded(document, attrib, val)
48
- else
49
- validate_root(document, attrib, val)
46
+ with_query(document) do
47
+ attrib, val = to_validate(document, attribute, value)
48
+ return unless validation_required?(document, attrib)
49
+ if document.embedded?
50
+ validate_embedded(document, attrib, val)
51
+ else
52
+ validate_root(document, attrib, val)
53
+ end
50
54
  end
51
55
  end
52
56
 
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "3.0.1"
3
+ VERSION = "3.0.2"
4
4
  end
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.1
4
+ version: 3.0.2
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-07-12 00:00:00.000000000 Z
12
+ date: 2012-07-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -50,7 +50,7 @@ dependencies:
50
50
  requirements:
51
51
  - - ~>
52
52
  - !ruby/object:Gem::Version
53
- version: 1.1.1
53
+ version: 1.1.3
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,7 +58,7 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 1.1.1
61
+ version: 1.1.3
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: origin
64
64
  requirement: !ruby/object:Gem::Requirement
@@ -129,6 +129,7 @@ files:
129
129
  - lib/mongoid/errors/invalid_field.rb
130
130
  - lib/mongoid/errors/invalid_field_option.rb
131
131
  - lib/mongoid/errors/invalid_find.rb
132
+ - lib/mongoid/errors/invalid_includes.rb
132
133
  - lib/mongoid/errors/invalid_index.rb
133
134
  - lib/mongoid/errors/invalid_options.rb
134
135
  - lib/mongoid/errors/invalid_scope.rb
@@ -319,6 +320,7 @@ files:
319
320
  - lib/mongoid/validations/length.rb
320
321
  - lib/mongoid/validations/localizable.rb
321
322
  - lib/mongoid/validations/presence.rb
323
+ - lib/mongoid/validations/queryable.rb
322
324
  - lib/mongoid/validations/uniqueness.rb
323
325
  - lib/mongoid/validations.rb
324
326
  - lib/mongoid/version.rb
@@ -352,7 +354,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
352
354
  version: '0'
353
355
  segments:
354
356
  - 0
355
- hash: 842230721201432915
357
+ hash: -2401816609188543871
356
358
  required_rubygems_version: !ruby/object:Gem::Requirement
357
359
  none: false
358
360
  requirements: