mongoid 3.0.5 → 3.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,54 @@
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.5 (branch 3.0.0-stable)
6
+ ## 3.0.6 (branch 3.0.0-stable)
7
+
8
+ ### Resolved Issues
9
+
10
+ * \#2375 Uniqueness validation scoping now works with aliased fields.
11
+
12
+ * \#2372 Ensure that all atomic operations mongoize values before executing.
13
+
14
+ * \#2370 Paranoid documents now properly don't get deleted when using
15
+ `dependent: :restrict` and an exception is raised.
16
+
17
+ * \#2365 Don't do anything when trying to replace an embeds_one with the same
18
+ document.
19
+
20
+ * \#2362 Don't store inverse of field values in the database when they are not
21
+ needed. (When there is not more than one polymorphic parent defined on the
22
+ same class).
23
+
24
+ * \#2360 Cloning documents should ignore mass assignment protection rules.
25
+
26
+ * \#2356 When limiting fields returned in queries via `only` ensure that the
27
+ limitation is scoped to the model.
28
+
29
+ * \#2353 Allow `update_attribute` to properly handle aliased fields.
30
+
31
+ * \#2348 Conversion of strings to times should raise an arugment error if the
32
+ string is invalid. (Campbell Allen)
33
+
34
+ * \#2346 Ensure `belongs_to` relations are evolvable when passed the proxy and
35
+ not the document.
36
+
37
+ * \#2334 Fixed aggregation map/reduce when fields sometimes do not exist.
38
+ (James McKinney)
39
+
40
+ * \#2330 Fixed inconsistency of #size and #length on criteria when the documents
41
+ have been iterated over with a limit applied.
42
+
43
+ * \#2328 Ensure ordering is applied on all relation criteria if defined.
44
+
45
+ * \#2327 Don't execute callbacks from base document if the document cannot execute
46
+ them.
47
+
48
+ * \#2318 Ensure setting any numeric on a Float field actually sets it as a float,
49
+ even if the number provided is an integer.
50
+
51
+ ## 3.0.5
52
+
53
+ ### Resolved Issues
7
54
 
8
55
  * \#2313 Fixed deserialization of `nil` `TimeWithZone` fields. (nagachika)
9
56
 
@@ -32,8 +79,6 @@ For instructions on upgrading to newer versions, visit
32
79
 
33
80
  * \#1091 Allow presence validation to pass if the value is `false`.
34
81
 
35
- ### Resolved Issues
36
-
37
82
  ## 3.0.4
38
83
 
39
84
  ### Resolved Issues
@@ -33,6 +33,7 @@ require "active_model"
33
33
  require "origin"
34
34
  require "moped"
35
35
 
36
+ require "mongoid/evolvable"
36
37
  require "mongoid/extensions"
37
38
  require "mongoid/errors"
38
39
  require "mongoid/threaded"
@@ -45,7 +46,6 @@ require "mongoid/contextual"
45
46
  require "mongoid/copyable"
46
47
  require "mongoid/criteria"
47
48
  require "mongoid/dirty"
48
- require "mongoid/evolvable"
49
49
  require "mongoid/factory"
50
50
  require "mongoid/fields"
51
51
  require "mongoid/finders"
@@ -38,6 +38,20 @@ module Mongoid
38
38
  attr_accessor :before_callback_halted
39
39
  end
40
40
 
41
+ # Is the provided type of callback executable by this document?
42
+ #
43
+ # @example Is the callback executable?
44
+ # document.callback_executable?(:save)
45
+ #
46
+ # @param [ Symbol ] kin The type of callback.
47
+ #
48
+ # @return [ true, false ] If the callback can be executed.
49
+ #
50
+ # @since 3.0.6
51
+ def callback_executable?(kind)
52
+ respond_to?("_#{kind}_callbacks")
53
+ end
54
+
41
55
  # Run only the after callbacks for the specific event.
42
56
  #
43
57
  # @note ActiveSupport does not allow this type of behaviour by default, so
@@ -97,7 +111,7 @@ module Mongoid
97
111
  return false
98
112
  end
99
113
  end
100
- super(kind, *args, &block)
114
+ callback_executable?(kind) ? super(kind, *args, &block) : true
101
115
  end
102
116
 
103
117
  private
@@ -158,7 +172,7 @@ module Mongoid
158
172
  #
159
173
  # @since 2.3.0
160
174
  def cascadable_child?(kind, child)
161
- return false if kind == :initialize || !child.respond_to?("_#{kind}_callbacks")
175
+ return false if kind == :initialize || !child.callback_executable?(kind)
162
176
  [ :create, :destroy ].include?(kind) || child.changed? || child.new_record?
163
177
  end
164
178
 
@@ -36,11 +36,12 @@ module Mongoid
36
36
  # @param [ String ] name The database name.
37
37
  #
38
38
  # @since 3.0.0
39
- def connect_to(name)
39
+ def connect_to(name, options = { consistency: :eventual })
40
40
  self.sessions = {
41
41
  default: {
42
42
  database: name,
43
- hosts: [ "localhost:27017" ]
43
+ hosts: [ "localhost:27017" ],
44
+ options: options
44
45
  }
45
46
  }
46
47
  end
@@ -167,10 +167,12 @@ module Mongoid
167
167
  function(key, values) {
168
168
  var agg = { count: 0, max: null, min: null, sum: 0 };
169
169
  values.forEach(function(val) {
170
- if (agg.max == null || val.max > agg.max) agg.max = val.max;
171
- if (agg.min == null || val.max < agg.min) agg.min = val.max;
170
+ if (val.max !== null) {
171
+ if (agg.max == null || val.max > agg.max) agg.max = val.max;
172
+ if (agg.min == null || val.max < agg.min) agg.min = val.max;
173
+ agg.sum += val.sum;
174
+ }
172
175
  agg.count += 1;
173
- agg.sum += val.sum;
174
176
  });
175
177
  return agg;
176
178
  }}
@@ -27,7 +27,7 @@ module Mongoid
27
27
  # @since 3.0.0
28
28
  def initialize(collection, criteria, update, options = {})
29
29
  @collection, @criteria, @options, @update =
30
- collection, criteria, options, update
30
+ collection, criteria, options, update.mongoize
31
31
  @query = collection.find(criteria.selector)
32
32
  apply_criteria_options
33
33
  end
@@ -129,10 +129,9 @@ module Mongoid
129
129
  # @since 3.0.0
130
130
  def each(&block)
131
131
  if block_given?
132
- reset_length
133
132
  selecting do
134
133
  documents_for_iteration.each do |doc|
135
- yield_and_increment(doc, &block)
134
+ yield_document(doc, &block)
136
135
  end
137
136
  @cache_loaded = true
138
137
  eager_loadable? ? docs : self
@@ -572,34 +571,6 @@ module Mongoid
572
571
  !eager_loaded && !criteria.inclusions.empty?
573
572
  end
574
573
 
575
- # Increment the length of the results.
576
- #
577
- # @api private
578
- #
579
- # @example Increment the length.
580
- # context.increment_length
581
- #
582
- # @return [ Integer ] The new length
583
- #
584
- # @since 3.0.0
585
- def increment_length
586
- @length += 1
587
- end
588
-
589
- # Reset the length to zero. This happens once before iteration.
590
- #
591
- # @api private
592
- #
593
- # @example Reset the length.
594
- # context.reset_length
595
- #
596
- # @return [ Integer ] zero.
597
- #
598
- # @since 3.0.0
599
- def reset_length
600
- @length = 0
601
- end
602
-
603
574
  # Apply all the optional criterion.
604
575
  #
605
576
  # @example Apply the options.
@@ -627,12 +598,11 @@ module Mongoid
627
598
  # @since 2.4.4
628
599
  def selecting
629
600
  begin
630
- unless criteria.options[:fields].blank?
631
- Threaded.selection = criteria.options[:fields]
632
- end
601
+ fields = criteria.options[:fields]
602
+ Threaded.set_selection(criteria.object_id, fields) unless fields.blank?
633
603
  yield
634
604
  ensure
635
- Threaded.selection = nil
605
+ Threaded.set_selection(criteria.object_id, nil)
636
606
  end
637
607
  end
638
608
 
@@ -650,28 +620,28 @@ module Mongoid
650
620
  def with_eager_loading(document)
651
621
  selecting do
652
622
  return nil unless document
653
- doc = Factory.from_db(klass, document)
623
+ doc = Factory.from_db(klass, document, criteria.object_id)
654
624
  eager_load([ doc ]) if eager_loadable?
655
625
  doc
656
626
  end
657
627
  end
658
628
 
659
- # Yield to the document and increment the length.
629
+ # Yield to the document.
660
630
  #
661
631
  # @api private
662
632
  #
663
- # @example Yield and increment.
664
- # context.yield_and_increment(doc) do |doc|
633
+ # @example Yield the document.
634
+ # context.yield_document(doc) do |doc|
665
635
  # ...
666
636
  # end
667
637
  #
668
638
  # @param [ Document ] document The document to yield to.
669
639
  #
670
640
  # @since 3.0.0
671
- def yield_and_increment(document, &block)
672
- doc = document.respond_to?(:_id) ? document : Factory.from_db(klass, document)
641
+ def yield_document(document, &block)
642
+ doc = document.respond_to?(:_id) ?
643
+ document : Factory.from_db(klass, document, criteria.object_id)
673
644
  yield(doc)
674
- increment_length
675
645
  documents.push(doc) if cacheable?
676
646
  end
677
647
  end
@@ -22,7 +22,7 @@ module Mongoid
22
22
  if attrs.delete("versions")
23
23
  attrs["version"] = 1
24
24
  end
25
- self.class.new(attrs)
25
+ self.class.new(attrs, without_protection: true)
26
26
  end
27
27
  alias :dup :clone
28
28
  end
@@ -7,6 +7,7 @@ module Mongoid
7
7
  extend ActiveSupport::Concern
8
8
  include Mongoid::Components
9
9
 
10
+ attr_accessor :criteria_instance_id
10
11
  attr_reader :new_record
11
12
 
12
13
  # Default comparison is via the string version of the id.
@@ -301,13 +302,16 @@ module Mongoid
301
302
  # Person.instantiate(:title => "Sir", :age => 30)
302
303
  #
303
304
  # @param [ Hash ] attrs The hash of attributes to instantiate with.
305
+ # @param [ Integer ] criteria_instance_id The criteria id that
306
+ # instantiated the document.
304
307
  #
305
308
  # @return [ Document ] A new document.
306
309
  #
307
310
  # @since 1.0.0
308
- def instantiate(attrs = nil)
311
+ def instantiate(attrs = nil, criteria_instance_id = nil)
309
312
  attributes = attrs || {}
310
313
  doc = allocate
314
+ doc.criteria_instance_id = criteria_instance_id
311
315
  doc.instance_variable_set(:@attributes, attributes)
312
316
  doc.apply_defaults
313
317
  IdentityMap.set(doc) unless _loading_revision?
@@ -42,7 +42,7 @@ module Mongoid
42
42
  # @since 3.0.0
43
43
  def mongoize(object)
44
44
  unless object.blank?
45
- __numeric__(object) rescue 0.0
45
+ __numeric__(object).to_f rescue 0.0
46
46
  else
47
47
  nil
48
48
  end
@@ -35,10 +35,16 @@ module Mongoid
35
35
  # @example Mongoize the string.
36
36
  # "2012-01-01".__mongoize_time__
37
37
  #
38
+ # @note The extra parse from Time is because ActiveSupport::TimeZone
39
+ # either returns nil or Time.now if the string is empty or invalid,
40
+ # which is a regression from pre-3.0 and also does not agree with
41
+ # the core Time API.
42
+ #
38
43
  # @return [ Time ] The time.
39
44
  #
40
45
  # @since 3.0.0
41
46
  def __mongoize_time__
47
+ ::Time.parse(self)
42
48
  ::Time.configured.parse(self)
43
49
  end
44
50
 
@@ -34,12 +34,12 @@ module Mongoid
34
34
  # @param [ Hash ] attributes The document attributes.
35
35
  #
36
36
  # @return [ Document ] The instantiated document.
37
- def from_db(klass, attributes = nil)
37
+ def from_db(klass, attributes = nil, criteria_instance_id = nil)
38
38
  type = (attributes || {})["_type"]
39
39
  if type.blank?
40
- klass.instantiate(attributes)
40
+ klass.instantiate(attributes, criteria_instance_id)
41
41
  else
42
- type.camelize.constantize.instantiate(attributes)
42
+ type.camelize.constantize.instantiate(attributes, criteria_instance_id)
43
43
  end
44
44
  end
45
45
  end
@@ -50,7 +50,7 @@ module Mongoid
50
50
  #
51
51
  # @since 2.1.8
52
52
  def eval_default(doc)
53
- if fields = Threaded.selection
53
+ if fields = Threaded.selection(doc.criteria_instance_id)
54
54
  evaluated_default(doc) if included?(fields)
55
55
  else
56
56
  evaluated_default(doc)
@@ -58,10 +58,10 @@ module Mongoid
58
58
  #
59
59
  # @since 1.0.0
60
60
  def remove(options = {})
61
+ cascade!
61
62
  time = self.deleted_at = Time.now
62
63
  paranoid_collection.find(atomic_selector).
63
64
  update({ "$set" => { paranoid_field => time }})
64
- cascade!
65
65
  @destroyed = true
66
66
  IdentityMap.remove(self)
67
67
  Threaded.clear_options!
@@ -155,10 +155,11 @@ module Mongoid
155
155
  #
156
156
  # @since 2.0.0.rc.6
157
157
  def update_attribute(name, value)
158
- unless attribute_writable?(name.to_s)
159
- raise Errors::ReadonlyAttribute.new(name, value)
158
+ normalized = name.to_s
159
+ unless attribute_writable?(normalized)
160
+ raise Errors::ReadonlyAttribute.new(normalized, value)
160
161
  end
161
- write_attribute(name, value)
162
+ write_attribute(aliased_fields[normalized] || normalized, value)
162
163
  save(validate: false)
163
164
  end
164
165
 
@@ -93,7 +93,7 @@ module Mongoid
93
93
  #
94
94
  # @since 3.0.3
95
95
  def cast_value
96
- value
96
+ value.mongoize
97
97
  end
98
98
 
99
99
  # Executes the common functionality between operations.
@@ -169,9 +169,11 @@ module Mongoid
169
169
  #
170
170
  # @since 3.0.0
171
171
  def bind_inverse_of_field(doc, name)
172
- if inverse_metadata = metadata.inverse_metadata(doc)
173
- if setter = inverse_metadata.inverse_of_field_setter
174
- doc.you_must(setter, name)
172
+ if metadata.inverse_field_bindable?
173
+ if inverse_metadata = metadata.inverse_metadata(doc)
174
+ if setter = inverse_metadata.inverse_of_field_setter
175
+ doc.you_must(setter, name)
176
+ end
175
177
  end
176
178
  end
177
179
  end
@@ -97,7 +97,7 @@ module Mongoid
97
97
  doc = existing.find(converted)
98
98
  if destroyable?(attrs)
99
99
  existing.delete(doc)
100
- doc.destroy unless doc.embedded?
100
+ doc.destroy unless doc.embedded? || doc.destroyed?
101
101
  else
102
102
  attrs.delete_id
103
103
  metadata.embedded? ? doc.attributes = attrs : doc.update_attributes(attrs)
@@ -28,7 +28,9 @@ module Mongoid
28
28
  # @example Perform the cascading delete.
29
29
  # strategy.cascade
30
30
  def cascade
31
- raise Errors::DeleteRestriction.new(document, metadata.name) unless relation.blank?
31
+ unless relation.blank?
32
+ raise Errors::DeleteRestriction.new(document, metadata.name)
33
+ end
32
34
  end
33
35
  end
34
36
  end
@@ -337,7 +337,7 @@ module Mongoid
337
337
  criterion = klass.scoped
338
338
  criterion.embedded = true
339
339
  criterion.documents = target
340
- criterion
340
+ Many.apply_ordering(criterion, metadata)
341
341
  end
342
342
 
343
343
  # Deletes one document from the target and unscoped.
@@ -36,18 +36,20 @@ module Mongoid
36
36
  #
37
37
  # @since 2.0.0.rc.1
38
38
  def substitute(replacement)
39
- if _assigning?
40
- base.add_atomic_unset(target)
41
- else
42
- target.destroy if persistable?
39
+ if replacement != self
40
+ if _assigning?
41
+ base.add_atomic_unset(target)
42
+ else
43
+ target.destroy if persistable?
44
+ end
45
+ unbind_one
46
+ return nil unless replacement
47
+ replacement = Factory.build(klass, replacement) if replacement.is_a?(::Hash)
48
+ self.target = replacement
49
+ bind_one
50
+ characterize_one(target)
51
+ target.save if persistable? && !_assigning?
43
52
  end
44
- unbind_one
45
- return nil unless replacement
46
- replacement = Factory.build(klass, replacement) if replacement.is_a?(::Hash)
47
- self.target = replacement
48
- bind_one
49
- characterize_one(target)
50
- target.save if persistable? && !_assigning?
51
53
  self
52
54
  end
53
55
 
@@ -150,8 +150,7 @@ module Mongoid
150
150
  #
151
151
  # @since 2.1.0
152
152
  def criteria(object, type)
153
- query = relation.criteria(self, object, type)
154
- order ? query.order_by(order) : query
153
+ relation.criteria(self, object, type)
155
154
  end
156
155
 
157
156
  # Returns the cyclic option of the relation.
@@ -436,6 +435,21 @@ module Mongoid
436
435
  !!inverse_class_name
437
436
  end
438
437
 
438
+ # Is the inverse field bindable? Ie, do we have more than one definition
439
+ # on the parent class with the same polymorphic name (as).
440
+ #
441
+ # @example Is the inverse of bindable?
442
+ # metadata.inverse_of_bindable?
443
+ #
444
+ # @return [ true, false ] If the relation needs the inverse of field set.
445
+ #
446
+ # @since 3.0.6
447
+ def inverse_field_bindable?
448
+ @inverse_field_bindable ||= (inverse_klass.relations.values.count do |meta|
449
+ meta.as == as
450
+ end > 1)
451
+ end
452
+
439
453
  # Used for relational many to many only. This determines the name of the
440
454
  # foreign key field on the inverse side of the relation, since in this
441
455
  # case there are keys on both sides.
@@ -147,6 +147,21 @@ module Mongoid
147
147
 
148
148
  class << self
149
149
 
150
+ # Apply ordering to the criteria if it was defined on the relation.
151
+ #
152
+ # @example Apply the ordering.
153
+ # Proxy.apply_ordering(criteria, metadata)
154
+ #
155
+ # @param [ Criteria ] criteria The criteria to modify.
156
+ # @param [ Metadata ] metadata The relation metadata.
157
+ #
158
+ # @return [ Criteria ] The ordered criteria.
159
+ #
160
+ # @since 3.0.6
161
+ def apply_ordering(criteria, metadata)
162
+ metadata.order ? criteria.order_by(metadata.order) : criteria
163
+ end
164
+
150
165
  # Get the criteria that is used to eager load a relation of this
151
166
  # type.
152
167
  #
@@ -8,6 +8,7 @@ module Mongoid
8
8
  # of the relation and the reference is to document(s) in another
9
9
  # collection.
10
10
  class In < Relations::One
11
+ include Evolvable
11
12
 
12
13
  # Instantiate a new referenced_in relation.
13
14
  #
@@ -537,13 +537,15 @@ module Mongoid
537
537
  #
538
538
  # @since 2.1.0
539
539
  def criteria(metadata, object, type = nil)
540
- with_inverse_field_criterion(
541
- with_polymorphic_criterion(
542
- metadata.klass.where(metadata.foreign_key => object),
543
- metadata,
544
- type
545
- ),
546
- metadata
540
+ apply_ordering(
541
+ with_inverse_field_criterion(
542
+ with_polymorphic_criterion(
543
+ metadata.klass.where(metadata.foreign_key => object),
544
+ metadata,
545
+ type
546
+ ),
547
+ metadata
548
+ ), metadata
547
549
  )
548
550
  end
549
551
 
@@ -278,7 +278,7 @@ module Mongoid
278
278
  #
279
279
  # @since 2.1.0
280
280
  def criteria(metadata, object, type = nil)
281
- metadata.klass.all_of(_id: { "$in" => object })
281
+ apply_ordering(metadata.klass.all_of(_id: { "$in" => object }), metadata)
282
282
  end
283
283
 
284
284
  # Get the criteria that is used to eager load a relation of this
@@ -301,25 +301,28 @@ module Mongoid
301
301
  # @example Get the field selection options.
302
302
  # Threaded.selection
303
303
  #
304
+ # @param [ Class ] klass The model class.
305
+ #
304
306
  # @return [ Hash ] The field selection.
305
307
  #
306
308
  # @since 2.4.4
307
- def selection
308
- Thread.current["[mongoid]:selection"]
309
+ def selection(klass)
310
+ Thread.current["[mongoid][#{klass}]:selection"]
309
311
  end
310
312
 
311
313
  # Set the field selection on the current thread.
312
314
  #
313
315
  # @example Set the field selection.
314
- # Threaded.selection = { field: 1 }
316
+ # Threaded.set_selection(Person, { field: 1 })
315
317
  #
318
+ # @param [ Integer ] criteria_instance_id The criteria instance id.
316
319
  # @param [ Hash ] value The current field selection.
317
320
  #
318
321
  # @return [ Hash ] The field selection.
319
322
  #
320
323
  # @since 2.4.4
321
- def selection=(value)
322
- Thread.current["[mongoid]:selection"] = value
324
+ def set_selection(criteria_instance_id, value)
325
+ Thread.current["[mongoid][#{criteria_instance_id}]:selection"] = value
323
326
  end
324
327
 
325
328
  # Get the global session override.
@@ -39,7 +39,7 @@ module Mongoid
39
39
  ensure
40
40
  document.exit_validate
41
41
  end
42
- document.errors.add(attribute, :invalid) unless valid
42
+ document.errors.add(attribute, :invalid, options) unless valid
43
43
  end
44
44
  end
45
45
  end
@@ -167,7 +167,9 @@ 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
- criteria = criteria.where(item => document.attributes[item.to_s])
170
+ normalized = item.to_s
171
+ name = document.aliased_fields[normalized] || normalized
172
+ criteria = criteria.where(item => document.attributes[name])
171
173
  end
172
174
  criteria = criteria.where(deleted_at: nil) if document.paranoid?
173
175
  criteria
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "3.0.5"
3
+ VERSION = "3.0.6"
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.5
4
+ version: 3.0.6
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-08-25 00:00:00.000000000 Z
12
+ date: 2012-09-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -307,7 +307,6 @@ files:
307
307
  - lib/mongoid/sharding.rb
308
308
  - lib/mongoid/state.rb
309
309
  - lib/mongoid/threaded/lifecycle.rb
310
- - lib/mongoid/threaded/sessions.rb
311
310
  - lib/mongoid/threaded.rb
312
311
  - lib/mongoid/timestamps/created.rb
313
312
  - lib/mongoid/timestamps/timeless.rb
@@ -353,7 +352,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
353
352
  version: '0'
354
353
  segments:
355
354
  - 0
356
- hash: -1770994134498643287
355
+ hash: -3437651563723863969
357
356
  required_rubygems_version: !ruby/object:Gem::Requirement
358
357
  none: false
359
358
  requirements:
File without changes