mongoid 3.0.23 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/CHANGELOG.md +253 -9
  2. data/LICENSE +1 -1
  3. data/README.md +4 -1
  4. data/lib/config/locales/en.yml +7 -6
  5. data/lib/mongoid.rb +18 -1
  6. data/lib/mongoid/atomic.rb +22 -20
  7. data/lib/mongoid/atomic/paths/embedded.rb +19 -5
  8. data/lib/mongoid/atomic/paths/root.rb +1 -1
  9. data/lib/mongoid/atomic/positionable.rb +73 -0
  10. data/lib/mongoid/attributes.rb +63 -1
  11. data/lib/mongoid/callbacks.rb +58 -4
  12. data/lib/mongoid/components.rb +8 -3
  13. data/lib/mongoid/config.rb +71 -23
  14. data/lib/mongoid/contextual.rb +2 -1
  15. data/lib/mongoid/contextual/aggregable/mongo.rb +27 -63
  16. data/lib/mongoid/contextual/atomic.rb +4 -3
  17. data/lib/mongoid/contextual/find_and_modify.rb +1 -1
  18. data/lib/mongoid/contextual/geo_near.rb +238 -0
  19. data/lib/mongoid/contextual/map_reduce.rb +12 -1
  20. data/lib/mongoid/contextual/memory.rb +36 -31
  21. data/lib/mongoid/contextual/mongo.rb +147 -91
  22. data/lib/mongoid/contextual/queryable.rb +25 -0
  23. data/lib/mongoid/copyable.rb +4 -1
  24. data/lib/mongoid/criteria.rb +23 -275
  25. data/lib/mongoid/criterion/findable.rb +179 -0
  26. data/lib/mongoid/criterion/modifiable.rb +191 -0
  27. data/lib/mongoid/criterion/scoping.rb +11 -6
  28. data/lib/mongoid/document.rb +7 -56
  29. data/lib/mongoid/equality.rb +66 -0
  30. data/lib/mongoid/errors/mongoid_error.rb +7 -3
  31. data/lib/mongoid/extensions/array.rb +13 -1
  32. data/lib/mongoid/extensions/date.rb +9 -2
  33. data/lib/mongoid/extensions/hash.rb +38 -2
  34. data/lib/mongoid/extensions/nil_class.rb +12 -0
  35. data/lib/mongoid/extensions/object.rb +24 -0
  36. data/lib/mongoid/extensions/string.rb +14 -2
  37. data/lib/mongoid/extensions/time.rb +4 -1
  38. data/lib/mongoid/fields.rb +49 -5
  39. data/lib/mongoid/fields/foreign_key.rb +12 -0
  40. data/lib/mongoid/fields/standard.rb +12 -0
  41. data/lib/mongoid/finders.rb +8 -0
  42. data/lib/mongoid/hierarchy.rb +19 -1
  43. data/lib/mongoid/indexes.rb +30 -4
  44. data/lib/mongoid/indexes/validators/options.rb +12 -2
  45. data/lib/mongoid/inspection.rb +2 -1
  46. data/lib/mongoid/matchers/strategies.rb +5 -5
  47. data/lib/mongoid/observer.rb +27 -36
  48. data/lib/mongoid/persistence.rb +42 -17
  49. data/lib/mongoid/persistence/atomic.rb +10 -5
  50. data/lib/mongoid/persistence/atomic/operation.rb +26 -9
  51. data/lib/mongoid/persistence/atomic/unset.rb +1 -1
  52. data/lib/mongoid/persistence/operations/embedded/insert.rb +5 -2
  53. data/lib/mongoid/persistence/operations/embedded/remove.rb +5 -2
  54. data/lib/mongoid/persistence/operations/update.rb +7 -3
  55. data/lib/mongoid/railties/database.rake +12 -19
  56. data/lib/mongoid/relations.rb +2 -0
  57. data/lib/mongoid/relations/accessors.rb +30 -8
  58. data/lib/mongoid/relations/binding.rb +5 -1
  59. data/lib/mongoid/relations/bindings/referenced/in.rb +1 -1
  60. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +3 -3
  61. data/lib/mongoid/relations/counter_cache.rb +107 -0
  62. data/lib/mongoid/relations/embedded/batchable.rb +13 -4
  63. data/lib/mongoid/relations/embedded/many.rb +30 -1
  64. data/lib/mongoid/relations/macros.rb +2 -0
  65. data/lib/mongoid/relations/marshalable.rb +0 -1
  66. data/lib/mongoid/relations/metadata.rb +63 -11
  67. data/lib/mongoid/relations/options.rb +1 -0
  68. data/lib/mongoid/relations/proxy.rb +45 -2
  69. data/lib/mongoid/relations/referenced/in.rb +11 -2
  70. data/lib/mongoid/relations/referenced/many.rb +31 -3
  71. data/lib/mongoid/relations/referenced/many_to_many.rb +31 -3
  72. data/lib/mongoid/relations/referenced/one.rb +1 -1
  73. data/lib/mongoid/relations/targets/enumerable.rb +5 -1
  74. data/lib/mongoid/relations/touchable.rb +35 -6
  75. data/lib/mongoid/reloading.rb +5 -3
  76. data/lib/mongoid/scoping.rb +2 -2
  77. data/lib/mongoid/sessions.rb +57 -7
  78. data/lib/mongoid/sessions/factory.rb +22 -1
  79. data/lib/mongoid/threaded.rb +4 -30
  80. data/lib/mongoid/threaded/lifecycle.rb +12 -12
  81. data/lib/mongoid/timestamps.rb +1 -0
  82. data/lib/mongoid/timestamps/created.rb +2 -0
  83. data/lib/mongoid/timestamps/created/short.rb +19 -0
  84. data/lib/mongoid/timestamps/short.rb +10 -0
  85. data/lib/mongoid/timestamps/updated.rb +2 -0
  86. data/lib/mongoid/timestamps/updated/short.rb +19 -0
  87. data/lib/mongoid/validations.rb +2 -0
  88. data/lib/mongoid/validations/queryable.rb +2 -2
  89. data/lib/mongoid/validations/uniqueness.rb +1 -18
  90. data/lib/mongoid/version.rb +1 -1
  91. data/lib/rails/generators/mongoid/model/model_generator.rb +1 -0
  92. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +3 -0
  93. data/lib/rails/mongoid.rb +53 -29
  94. data/lib/support/ruby_version.rb +26 -0
  95. metadata +18 -7
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+
4
+ # This module contains the behaviour of Mongoid's clone/dup of documents.
5
+ module Equality
6
+
7
+ # Default comparison is via the string version of the id.
8
+ #
9
+ # @example Compare two documents.
10
+ # person <=> other_person
11
+ #
12
+ # @param [ Document ] other The document to compare with.
13
+ #
14
+ # @return [ Integer ] -1, 0, 1.
15
+ #
16
+ # @since 1.0.0
17
+ def <=>(other)
18
+ attributes["_id"].to_s <=> other.attributes["_id"].to_s
19
+ end
20
+
21
+ # Performs equality checking on the document ids. For more robust
22
+ # equality checking please override this method.
23
+ #
24
+ # @example Compare for equality.
25
+ # document == other
26
+ #
27
+ # @param [ Document, Object ] other The other object to compare with.
28
+ #
29
+ # @return [ true, false ] True if the ids are equal, false if not.
30
+ #
31
+ # @since 1.0.0
32
+ def ==(other)
33
+ self.class == other.class &&
34
+ attributes["_id"] == other.attributes["_id"]
35
+ end
36
+
37
+ # Performs class equality checking.
38
+ #
39
+ # @example Compare the classes.
40
+ # document === other
41
+ #
42
+ # @param [ Document, Object ] other The other object to compare with.
43
+ #
44
+ # @return [ true, false ] True if the classes are equal, false if not.
45
+ #
46
+ # @since 1.0.0
47
+ def ===(other)
48
+ other.class == Class ? self.class === other : self == other
49
+ end
50
+
51
+ # Delegates to ==. Used when needing checks in hashes.
52
+ #
53
+ # @example Perform equality checking.
54
+ # document.eql?(other)
55
+ #
56
+ # @param [ Document, Object ] other The object to check against.
57
+ #
58
+ # @return [ true, false ] True if equal, false if not.
59
+ #
60
+ # @since 1.0.0
61
+ def eql?(other)
62
+ self == (other)
63
+ end
64
+ end
65
+ end
66
+
@@ -18,9 +18,13 @@ module Mongoid
18
18
  #
19
19
  # @since 3.0.0
20
20
  def compose_message(key, attributes)
21
- "\nProblem:\n #{problem(key, attributes)}"+
22
- "\nSummary:\n #{summary(key, attributes)}"+
23
- "\nResolution:\n #{resolution(key, attributes)}"
21
+ @problem = problem(key, attributes)
22
+ @summary = summary(key, attributes)
23
+ @resolution = resolution(key, attributes)
24
+
25
+ "\nProblem:\n #{@problem}"+
26
+ "\nSummary:\n #{@summary}"+
27
+ "\nResolution:\n #{@resolution}"
24
28
  end
25
29
 
26
30
  private
@@ -53,6 +53,18 @@ module Mongoid
53
53
  ::Time.configured.local(*self)
54
54
  end
55
55
 
56
+ # Check if the array is part of a blank relation criteria.
57
+ #
58
+ # @example Is the array blank criteria?
59
+ # [].blank_criteria?
60
+ #
61
+ # @return [ true, false ] If the array is blank criteria.
62
+ #
63
+ # @since 3.1.0
64
+ def blank_criteria?
65
+ any?(&:blank_criteria?)
66
+ end
67
+
56
68
  # Is the array a set of multiple arguments in a method?
57
69
  #
58
70
  # @example Is this multi args?
@@ -62,7 +74,7 @@ module Mongoid
62
74
  #
63
75
  # @since 3.0.0
64
76
  def multi_arged?
65
- first.resizable? || size > 1
77
+ !first.is_a?(Hash) && first.resizable? || size > 1
66
78
  end
67
79
 
68
80
  # Turn the object from the ruby type we deal with to a Mongo friendly
@@ -3,6 +3,9 @@ module Mongoid
3
3
  module Extensions
4
4
  module Date
5
5
 
6
+ # Constant for epoch - used when passing invalid times.
7
+ EPOCH = ::Date.new(1970, 1, 1)
8
+
6
9
  # Convert the date into a time.
7
10
  #
8
11
  # @example Convert the date to a time.
@@ -57,8 +60,12 @@ module Mongoid
57
60
  # @since 3.0.0
58
61
  def mongoize(object)
59
62
  unless object.blank?
60
- time = object.__mongoize_time__
61
- ::Time.utc(time.year, time.month, time.day)
63
+ begin
64
+ time = object.__mongoize_time__
65
+ ::Time.utc(time.year, time.month, time.day)
66
+ rescue ArgumentError
67
+ EPOCH
68
+ end
62
69
  end
63
70
  end
64
71
  end
@@ -35,18 +35,33 @@ module Mongoid
35
35
  # @return [ Hash ] A new consolidated hash.
36
36
  #
37
37
  # @since 3.0.0
38
- def __consolidate__
38
+ def __consolidate__(klass)
39
39
  consolidated = {}
40
40
  each_pair do |key, value|
41
41
  if key =~ /\$/
42
+ value.each_pair do |_key, _value|
43
+ value[_key] = mongoize_for(klass, _key, _value)
44
+ end
42
45
  (consolidated[key] ||= {}).merge!(value)
43
46
  else
44
- (consolidated["$set"] ||= {}).merge!(key => value)
47
+ (consolidated["$set"] ||= {}).merge!(key => mongoize_for(klass, key, value))
45
48
  end
46
49
  end
47
50
  consolidated
48
51
  end
49
52
 
53
+ # Check if the hash is part of a blank relation criteria.
54
+ #
55
+ # @example Is the hash blank criteria?
56
+ # {}.blank_criteria?
57
+ #
58
+ # @return [ true, false ] If the hash is blank criteria.
59
+ #
60
+ # @since 3.1.0
61
+ def blank_criteria?
62
+ self == { "_id" => { "$in" => [] }}
63
+ end
64
+
50
65
  # Deletes an id value from the hash.
51
66
  #
52
67
  # @example Delete an id value.
@@ -135,6 +150,27 @@ module Mongoid
135
150
  criteria
136
151
  end
137
152
 
153
+ private
154
+
155
+ # Mongoize for the klass, key and value.
156
+ #
157
+ # @api private
158
+ #
159
+ # @example Mongoize for the klass, field and value.
160
+ # {}.mongoize_for(Band, "name", "test")
161
+ #
162
+ # @param [ Class ] klass The model class.
163
+ # @param [ String, Symbol ] The field key.
164
+ # @param [ Object ] value The value to mongoize.
165
+ #
166
+ # @return [ Object ] The mongoized value.
167
+ #
168
+ # @since 3.1.0
169
+ def mongoize_for(klass, key, value)
170
+ field = klass.fields[key.to_s]
171
+ field ? field.mongoize(value) : value
172
+ end
173
+
138
174
  module ClassMethods
139
175
 
140
176
  # Turn the object from the ruby type we deal with to a Mongo friendly
@@ -3,6 +3,18 @@ module Mongoid
3
3
  module Extensions
4
4
  module NilClass
5
5
 
6
+ # Try to form a setter from this object.
7
+ #
8
+ # @example Try to form a setter.
9
+ # object.__setter__
10
+ #
11
+ # @return [ nil ] Always nil.
12
+ #
13
+ # @since 3.1.0
14
+ def __setter__
15
+ self
16
+ end
17
+
6
18
  # Get the name of a nil collection.
7
19
  #
8
20
  # @example Get the nil name.
@@ -40,6 +40,18 @@ module Mongoid
40
40
  self
41
41
  end
42
42
 
43
+ # Try to form a setter from this object.
44
+ #
45
+ # @example Try to form a setter.
46
+ # object.__setter__
47
+ #
48
+ # @return [ String ] The object as a string plus =.
49
+ #
50
+ # @since 3.1.0
51
+ def __setter__
52
+ "#{self}="
53
+ end
54
+
43
55
  # Get the value of the object as a mongo friendy sort value.
44
56
  #
45
57
  # @example Get the object as sort criteria.
@@ -64,6 +76,18 @@ module Mongoid
64
76
  self
65
77
  end
66
78
 
79
+ # Check if the object is part of a blank relation criteria.
80
+ #
81
+ # @example Is the object blank criteria?
82
+ # "".blank_criteria?
83
+ #
84
+ # @return [ true, false ] If the object is blank criteria.
85
+ #
86
+ # @since 3.1.0
87
+ def blank_criteria?
88
+ false
89
+ end
90
+
67
91
  # Do or do not, there is no try. -- Yoda.
68
92
  #
69
93
  # @example Do or do not.
@@ -81,7 +81,7 @@ module Mongoid
81
81
  #
82
82
  # @since 3.0.0
83
83
  def numeric?
84
- true if Float(self) rescue (self == "NaN")
84
+ true if Float(self) rescue false
85
85
  end
86
86
 
87
87
  # Get the string as a getter string.
@@ -93,7 +93,7 @@ module Mongoid
93
93
  #
94
94
  # @since 1.0.0
95
95
  def reader
96
- delete("=")
96
+ delete("=").sub(/\_before\_type\_cast$/, '')
97
97
  end
98
98
 
99
99
  # Convert the string to an array with the string in it.
@@ -132,6 +132,18 @@ module Mongoid
132
132
  /[@$"]/ !~ to_sym.inspect
133
133
  end
134
134
 
135
+ # Does the string end with _before_type_cast?
136
+ #
137
+ # @example Is the string a setter method?
138
+ # "price_before_type_cast".before_type_cast?
139
+ #
140
+ # @return [ true, false ] If the string ends with "_before_type_cast"
141
+ #
142
+ # @since 3.1.0
143
+ def before_type_cast?
144
+ ends_with?("_before_type_cast")
145
+ end
146
+
135
147
  # Is the object not to be converted to bson on criteria creation?
136
148
  #
137
149
  # @example Is the object unconvertable?
@@ -3,6 +3,9 @@ module Mongoid
3
3
  module Extensions
4
4
  module Time
5
5
 
6
+ # Constant for epoch - used when passing invalid times.
7
+ EPOCH = ::Time.utc(1970, 1, 1, 0, 0, 0)
8
+
6
9
  # Turn the object from the ruby type we deal with to a Mongo friendly
7
10
  # type.
8
11
  #
@@ -73,7 +76,7 @@ module Mongoid
73
76
  ::Time.at(time.to_i, time.usec).utc
74
77
  end
75
78
  rescue ArgumentError
76
- raise Errors::InvalidTime.new(object)
79
+ EPOCH
77
80
  end
78
81
  end
79
82
  end
@@ -23,7 +23,6 @@ module Mongoid
23
23
  self.pre_processed_defaults = []
24
24
  self.post_processed_defaults = []
25
25
 
26
- field(:_type, default: ->{ self.class.name if hereditary? }, type: String)
27
26
  field(
28
27
  :_id,
29
28
  default: ->{ Moped::BSON::ObjectId.new },
@@ -77,7 +76,7 @@ module Mongoid
77
76
  unless attributes.has_key?(name)
78
77
  if field = fields[name]
79
78
  default = field.eval_default(self)
80
- unless default.nil?
79
+ unless default.nil? || field.lazy?
81
80
  attribute_will_change!(name)
82
81
  attributes[name] = default
83
82
  end
@@ -126,6 +125,21 @@ module Mongoid
126
125
  self.class.database_field_name(name)
127
126
  end
128
127
 
128
+ # Is the provided field a lazy evaluation?
129
+ #
130
+ # @example If the field is lazy settable.
131
+ # doc.lazy_settable?(field, nil)
132
+ #
133
+ # @param [ Field ] field The field.
134
+ # @param [ Object ] value The current value.
135
+ #
136
+ # @return [ true, false ] If we set the field lazily.
137
+ #
138
+ # @since 3.1.0
139
+ def lazy_settable?(field, value)
140
+ !frozen? && value.nil? && field.lazy?
141
+ end
142
+
129
143
  # Is the document using object ids?
130
144
  #
131
145
  # @note Refactored from using delegate for class load performance.
@@ -336,6 +350,7 @@ module Mongoid
336
350
  # person.name #=> returns the field
337
351
  # person.name = "" #=> sets the field
338
352
  # person.name? #=> Is the field present?
353
+ # person.name_before_type_cast #=> returns the field before type cast
339
354
  #
340
355
  # @param [ Symbol ] name The name of the field.
341
356
  # @param [ Symbol ] meth The name of the accessor.
@@ -346,6 +361,7 @@ module Mongoid
346
361
  field = fields[name]
347
362
 
348
363
  create_field_getter(name, meth, field)
364
+ create_field_getter_before_type_cast(name, meth)
349
365
  create_field_setter(name, meth, field)
350
366
  create_field_check(name, meth)
351
367
 
@@ -369,9 +385,37 @@ module Mongoid
369
385
  def create_field_getter(name, meth, field)
370
386
  generated_methods.module_eval do
371
387
  re_define_method(meth) do
372
- value = fields[name].demongoize(read_attribute(name))
373
- attribute_will_change!(name) if value.resizable?
374
- value
388
+ raw = read_attribute(name)
389
+ if lazy_settable?(field, raw)
390
+ write_attribute(name, field.eval_default(self))
391
+ else
392
+ value = field.demongoize(raw)
393
+ attribute_will_change!(name) if value.resizable?
394
+ value
395
+ end
396
+ end
397
+ end
398
+ end
399
+
400
+ # Create the getter_before_type_cast method for the provided field. If
401
+ # the attribute has been assigned, return the attribute before it was
402
+ # type cast. Otherwise, delegate to the getter.
403
+ #
404
+ # @example Create the getter_before_type_cast.
405
+ # Model.create_field_getter_before_type_cast("name", "name")
406
+ #
407
+ # @param [ String ] name The name of the attribute.
408
+ # @param [ String ] meth The name of the method.
409
+ #
410
+ # @since 3.1.0
411
+ def create_field_getter_before_type_cast(name, meth)
412
+ generated_methods.module_eval do
413
+ re_define_method("#{meth}_before_type_cast") do
414
+ if has_attribute_before_type_cast?(name)
415
+ read_attribute_before_type_cast(name)
416
+ else
417
+ send meth
418
+ end
375
419
  end
376
420
  end
377
421
  end
@@ -69,6 +69,18 @@ module Mongoid
69
69
  end
70
70
  end
71
71
 
72
+ # Does this field do lazy default evaluation?
73
+ #
74
+ # @example Is the field lazy?
75
+ # field.lazy?
76
+ #
77
+ # @return [ true, false ] If the field is lazy.
78
+ #
79
+ # @since 3.1.0
80
+ def lazy?
81
+ type.resizable?
82
+ end
83
+
72
84
  # Mongoize the object into the Mongo friendly value.
73
85
  #
74
86
  # @example Mongoize the object.
@@ -95,6 +95,18 @@ module Mongoid
95
95
  end
96
96
  end
97
97
 
98
+ # Does this field do lazy default evaluation?
99
+ #
100
+ # @example Is the field lazy?
101
+ # field.lazy?
102
+ #
103
+ # @return [ true, false ] If the field is lazy.
104
+ #
105
+ # @since 3.1.0
106
+ def lazy?
107
+ false
108
+ end
109
+
98
110
  # Is the field localized or not?
99
111
  #
100
112
  # @example Is the field localized?