mongoid 3.0.5 → 3.0.6

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,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