mongoid 2.2.6 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/CHANGELOG.md +2 -858
  2. data/Rakefile +2 -5
  3. data/lib/mongoid.rb +1 -1
  4. data/lib/mongoid/attributes.rb +68 -18
  5. data/lib/mongoid/attributes/processing.rb +4 -3
  6. data/lib/mongoid/callbacks.rb +102 -0
  7. data/lib/mongoid/collection.rb +1 -1
  8. data/lib/mongoid/components.rb +2 -1
  9. data/lib/mongoid/contexts/enumerable.rb +0 -24
  10. data/lib/mongoid/contexts/mongo.rb +2 -2
  11. data/lib/mongoid/copyable.rb +3 -1
  12. data/lib/mongoid/criteria.rb +18 -10
  13. data/lib/mongoid/criterion/complex.rb +11 -0
  14. data/lib/mongoid/criterion/inclusion.rb +38 -7
  15. data/lib/mongoid/criterion/optional.rb +2 -7
  16. data/lib/mongoid/criterion/selector.rb +1 -0
  17. data/lib/mongoid/dirty.rb +19 -0
  18. data/lib/mongoid/document.rb +16 -12
  19. data/lib/mongoid/extensions/hash/criteria_helpers.rb +1 -1
  20. data/lib/mongoid/extensions/object/checks.rb +4 -1
  21. data/lib/mongoid/extensions/object_id/conversions.rb +4 -2
  22. data/lib/mongoid/extensions/string/inflections.rb +2 -2
  23. data/lib/mongoid/factory.rb +7 -2
  24. data/lib/mongoid/fields.rb +4 -10
  25. data/lib/mongoid/fields/serializable.rb +18 -2
  26. data/lib/mongoid/fields/serializable/integer.rb +17 -5
  27. data/lib/mongoid/fields/serializable/localized.rb +41 -0
  28. data/lib/mongoid/finders.rb +5 -4
  29. data/lib/mongoid/hierarchy.rb +87 -84
  30. data/lib/mongoid/identity.rb +4 -2
  31. data/lib/mongoid/keys.rb +2 -1
  32. data/lib/mongoid/logger.rb +1 -7
  33. data/lib/mongoid/matchers/and.rb +30 -0
  34. data/lib/mongoid/matchers/in.rb +1 -1
  35. data/lib/mongoid/matchers/nin.rb +1 -1
  36. data/lib/mongoid/matchers/strategies.rb +6 -4
  37. data/lib/mongoid/multi_parameter_attributes.rb +3 -2
  38. data/lib/mongoid/named_scope.rb +3 -13
  39. data/lib/mongoid/nested_attributes.rb +1 -1
  40. data/lib/mongoid/paranoia.rb +2 -3
  41. data/lib/mongoid/persistence.rb +9 -5
  42. data/lib/mongoid/persistence/atomic/operation.rb +1 -1
  43. data/lib/mongoid/persistence/deletion.rb +1 -1
  44. data/lib/mongoid/persistence/insertion.rb +1 -1
  45. data/lib/mongoid/persistence/modification.rb +1 -1
  46. data/lib/mongoid/railtie.rb +1 -1
  47. data/lib/mongoid/railties/database.rake +9 -1
  48. data/lib/mongoid/relations.rb +1 -0
  49. data/lib/mongoid/relations/accessors.rb +1 -1
  50. data/lib/mongoid/relations/builders.rb +6 -4
  51. data/lib/mongoid/relations/builders/referenced/many.rb +1 -23
  52. data/lib/mongoid/relations/builders/referenced/one.rb +1 -1
  53. data/lib/mongoid/relations/cascading.rb +5 -3
  54. data/lib/mongoid/relations/conversions.rb +35 -0
  55. data/lib/mongoid/relations/embedded/atomic.rb +3 -3
  56. data/lib/mongoid/relations/embedded/in.rb +1 -1
  57. data/lib/mongoid/relations/embedded/many.rb +16 -13
  58. data/lib/mongoid/relations/embedded/one.rb +3 -3
  59. data/lib/mongoid/relations/metadata.rb +19 -15
  60. data/lib/mongoid/relations/proxy.rb +4 -5
  61. data/lib/mongoid/relations/referenced/in.rb +1 -1
  62. data/lib/mongoid/relations/referenced/many.rb +12 -31
  63. data/lib/mongoid/relations/referenced/many_to_many.rb +4 -5
  64. data/lib/mongoid/relations/referenced/one.rb +6 -8
  65. data/lib/mongoid/relations/synchronization.rb +3 -5
  66. data/lib/mongoid/safety.rb +34 -4
  67. data/lib/mongoid/serialization.rb +20 -6
  68. data/lib/mongoid/threaded.rb +47 -0
  69. data/lib/mongoid/timestamps.rb +1 -0
  70. data/lib/mongoid/timestamps/created.rb +1 -8
  71. data/lib/mongoid/timestamps/timeless.rb +50 -0
  72. data/lib/mongoid/timestamps/updated.rb +2 -9
  73. data/lib/mongoid/validations.rb +0 -2
  74. data/lib/mongoid/validations/associated.rb +1 -2
  75. data/lib/mongoid/validations/uniqueness.rb +89 -36
  76. data/lib/mongoid/version.rb +1 -1
  77. data/lib/mongoid/versioning.rb +5 -6
  78. data/lib/rails/generators/mongoid_generator.rb +1 -1
  79. data/lib/rails/mongoid.rb +14 -5
  80. metadata +27 -23
data/Rakefile CHANGED
@@ -24,10 +24,6 @@ task :release => :build do
24
24
  system "gem push mongoid-#{Mongoid::VERSION}.gem"
25
25
  end
26
26
 
27
- RSpec::Core::RakeTask.new(:spec) do |spec|
28
- spec.pattern = "spec/**/*_spec.rb"
29
- end
30
-
31
27
  RSpec::Core::RakeTask.new("spec:unit") do |spec|
32
28
  spec.pattern = "spec/unit/**/*_spec.rb"
33
29
  end
@@ -49,4 +45,5 @@ RDoc::Task.new do |rdoc|
49
45
  rdoc.rdoc_files.include("lib/**/*.rb")
50
46
  end
51
47
 
52
- task :default => [ "spec:functional", "spec:unit" ]
48
+ task :spec => [ "spec:functional", "spec:unit" ]
49
+ task :default => :spec
@@ -94,7 +94,7 @@ I18n.load_path << File.join(File.dirname(__FILE__), "config", "locales", "en.yml
94
94
  module Mongoid #:nodoc
95
95
  extend self
96
96
 
97
- MONGODB_VERSION = "1.8.0"
97
+ MONGODB_VERSION = "2.0.0"
98
98
 
99
99
  # Sets the Mongoid configuration options. Best used by passing a block.
100
100
  #
@@ -6,6 +6,7 @@ module Mongoid #:nodoc:
6
6
  # This module contains the logic for handling the internal attributes hash,
7
7
  # and how to get and set values.
8
8
  module Attributes
9
+ extend ActiveSupport::Concern
9
10
  include Processing
10
11
 
11
12
  attr_reader :attributes
@@ -56,7 +57,7 @@ module Mongoid #:nodoc:
56
57
  #
57
58
  # @since 1.0.0
58
59
  def remove_attribute(name)
59
- assigning do
60
+ _assigning do
60
61
  access = name.to_s
61
62
  attribute_will_change!(access)
62
63
  attributes.delete(access)
@@ -95,18 +96,46 @@ module Mongoid #:nodoc:
95
96
  #
96
97
  # @since 1.0.0
97
98
  def write_attribute(name, value)
98
- assigning do
99
+ _assigning do
99
100
  access = name.to_s
101
+ localized = fields[access].try(:localized?)
100
102
  typed_value_for(access, value).tap do |value|
101
103
  unless attributes[access] == value || attribute_changed?(access)
102
104
  attribute_will_change!(access)
103
105
  end
104
- attributes[access] = value
106
+ if localized
107
+ (attributes[access] ||= {}).merge!(value)
108
+ else
109
+ attributes[access] = value
110
+ end
105
111
  end
106
112
  end
107
113
  end
108
114
  alias :[]= :write_attribute
109
115
 
116
+ # Allows you to set all the attributes for a particular mass-assignment security role
117
+ # by passing in a hash of attributes with keys matching the attribute names
118
+ # (which again matches the column names) and the role name using the :as option.
119
+ # To bypass mass-assignment security you can use the :without_protection => true option.
120
+ #
121
+ # @example Assign the attributes.
122
+ # person.assign_attributes(:title => "Mr.")
123
+ #
124
+ # @example Assign the attributes (with a role).
125
+ # person.assign_attributes({ :title => "Mr." }, :as => :admin)
126
+ #
127
+ # @param [ Hash ] attrs The new attributes to set.
128
+ # @param [ Hash ] options Supported options: :without_protection, :as
129
+ #
130
+ # @since 2.2.1
131
+ def assign_attributes(attrs = nil, options = {})
132
+ _assigning do
133
+ process(attrs, options[:as] || :default, !options[:without_protection]) do |document|
134
+ document.identify if new? && id.blank?
135
+ end
136
+ end
137
+ end
138
+
110
139
  # Writes the supplied attributes hash to the document. This will only
111
140
  # overwrite existing attributes if they are present in the new +Hash+, all
112
141
  # others will be preserved.
@@ -122,11 +151,7 @@ module Mongoid #:nodoc:
122
151
  #
123
152
  # @since 1.0.0
124
153
  def write_attributes(attrs = nil, guard_protected_attributes = true)
125
- assigning do
126
- process(attrs, guard_protected_attributes) do |document|
127
- document.identify if new? && id.blank?
128
- end
129
- end
154
+ assign_attributes(attrs, :without_protection => !guard_protected_attributes)
130
155
  end
131
156
  alias :attributes= :write_attributes
132
157
 
@@ -135,18 +160,16 @@ module Mongoid #:nodoc:
135
160
  # Set any missing default values in the attributes.
136
161
  #
137
162
  # @example Get the raw attributes after defaults have been applied.
138
- # person.apply_default_attributes
163
+ # person.apply_defaults
139
164
  #
140
165
  # @return [ Hash ] The raw attributes.
141
166
  #
142
167
  # @since 2.0.0.rc.8
143
- def apply_default_attributes
144
- (@attributes ||= {}).tap do |attrs|
145
- defaults.each do |name|
146
- unless attrs.has_key?(name)
147
- if field = fields[name]
148
- attrs[name] = field.eval_default(self)
149
- end
168
+ def apply_defaults
169
+ defaults.each do |name|
170
+ unless attributes.has_key?(name)
171
+ if field = fields[name]
172
+ attributes[name] = field.eval_default(self)
150
173
  end
151
174
  end
152
175
  end
@@ -157,14 +180,14 @@ module Mongoid #:nodoc:
157
180
  # be in a valid state.
158
181
  #
159
182
  # @example Execute the assignment.
160
- # assigning do
183
+ # _assigning do
161
184
  # person.attributes = { :addresses => [ address ] }
162
185
  # end
163
186
  #
164
187
  # @return [ Object ] The yielded value.
165
188
  #
166
189
  # @since 2.2.0
167
- def assigning
190
+ def _assigning
168
191
  begin
169
192
  Threaded.begin_assign
170
193
  yield
@@ -201,5 +224,32 @@ module Mongoid #:nodoc:
201
224
  def typed_value_for(key, value)
202
225
  fields.has_key?(key) ? fields[key].serialize(value) : value
203
226
  end
227
+
228
+ module ClassMethods #:nodoc:
229
+
230
+ # Alias the provided name to the original field. This will provide an
231
+ # aliased getter, setter, existance check, and all dirty attribute
232
+ # methods.
233
+ #
234
+ # @example Alias the attribute.
235
+ # class Product
236
+ # include Mongoid::Document
237
+ # field :price, :type => Float
238
+ # alias_attribute :cost, :price
239
+ # end
240
+ #
241
+ # @param [ Symbol ] name The new name.
242
+ # @param [ Symbol ] original The original name.
243
+ #
244
+ # @since 2.3.0
245
+ def alias_attribute(name, original)
246
+ class_eval <<-RUBY
247
+ alias :#{name} :#{original}
248
+ alias :#{name}= :#{original}=
249
+ alias :#{name}? :#{original}?
250
+ RUBY
251
+ super
252
+ end
253
+ end
204
254
  end
205
255
  end
@@ -14,12 +14,13 @@ module Mongoid #:nodoc:
14
14
  # person.process(:title => "sir", :age => 40)
15
15
  #
16
16
  # @param [ Hash ] attrs The attributes to set.
17
+ # @param [ Symbol ] role A role for scoped mass assignment.
17
18
  # @param [ Boolean ] guard_protected_attributes False to skip mass assignment protection.
18
19
  #
19
20
  # @since 2.0.0.rc.7
20
- def process(attrs = nil, guard_protected_attributes = true)
21
+ def process(attrs = nil, role = :default, guard_protected_attributes = true)
21
22
  attrs ||= {}
22
- attrs = sanitize_for_mass_assignment(attrs) if guard_protected_attributes
23
+ attrs = sanitize_for_mass_assignment(attrs, role) if guard_protected_attributes
23
24
  attrs.each_pair do |key, value|
24
25
  next if pending_attribute?(key, value)
25
26
  process_attribute(key, value)
@@ -37,7 +38,7 @@ module Mongoid #:nodoc:
37
38
  # @example Is the attribute pending?
38
39
  # document.pending_attribute?(:name, "Durran")
39
40
  #
40
- # @param [ Synbol ] key The name of the attribute.
41
+ # @param [ Symbol ] key The name of the attribute.
41
42
  # @param [ Object ] value The value of the attribute.
42
43
  #
43
44
  # @return [ true, false ] True if pending, false if not.
@@ -21,5 +21,107 @@ module Mongoid #:nodoc:
21
21
  define_model_callbacks :initialize, :only => :after
22
22
  define_model_callbacks :create, :destroy, :save, :update
23
23
  end
24
+
25
+ # Run the callbacks for the document. This overrides active support's
26
+ # functionality to cascade callbacks to embedded documents that have been
27
+ # flagged as such.
28
+ #
29
+ # @example Run the callbacks.
30
+ # run_callbacks :save do
31
+ # save!
32
+ # end
33
+ #
34
+ # @param [ Symbol ] kind The type of callback to execute.
35
+ # @param [ Array ] *args Any options.
36
+ #
37
+ # @return [ Document ] The document
38
+ #
39
+ # @since 2.3.0
40
+ def run_callbacks(kind, *args, &block)
41
+ run_cascading_callbacks(cascadable_children(kind), kind, *args) do
42
+ super(kind, *args, &block)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # Execute the callbacks, including all children that have cascade callbacks
49
+ # set to true.
50
+ #
51
+ # @example Run the cascading callbacks.
52
+ # document.run_cascading_callbacks([], :update)
53
+ #
54
+ # @param [ Array<Document> ] children The cascading children.
55
+ # @param [ Symbol ] kind The callback type.
56
+ # @param [ Array ] args The options.
57
+ #
58
+ # @since 2.3.0
59
+ def run_cascading_callbacks(children, kind, *args, &block)
60
+ if child = children.pop
61
+ run_cascading_callbacks(children, kind, *args) do
62
+ child.run_callbacks(child_callback_type(kind, child), *args) do
63
+ block.call
64
+ end
65
+ end
66
+ else
67
+ block.call
68
+ end
69
+ end
70
+
71
+ # Get all the child embedded documents that are flagged as cascadable.
72
+ #
73
+ # @example Get all the cascading children.
74
+ # document.cascadable_children(:update)
75
+ #
76
+ # @param [ Symbol ] kind The type of callback.
77
+ #
78
+ # @return [ Array<Document> ] The children.
79
+ #
80
+ # @since 2.3.0
81
+ def cascadable_children(kind)
82
+ [].tap do |children|
83
+ relations.each_pair do |name, metadata|
84
+ next unless metadata.cascading_callbacks?
85
+ child = send(name)
86
+ Array.wrap(child).each do |doc|
87
+ children.push(doc) if cascadable_child?(kind, doc)
88
+ children.concat(doc.send(:cascadable_children, kind))
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ # Determine if the child should fire the callback.
95
+ #
96
+ # @example Should the child fire the callback?
97
+ # document.cascadable_child?(:update, doc)
98
+ #
99
+ # @param [ Symbol ] kind The type of callback.
100
+ # @param [ Document ] child The child document.
101
+ #
102
+ # @return [ true, false ] If the child should fire the callback.
103
+ #
104
+ # @since 2.3.0
105
+ def cascadable_child?(kind, child)
106
+ [ :create, :destroy ].include?(kind) || child.changed? || child.new_record?
107
+ end
108
+
109
+ # Get the name of the callback that the child should fire. This changes
110
+ # depending on whether or not the child is new. A persisted parent with a
111
+ # new child would fire :update from the parent, but needs to fire :create
112
+ # on the child.
113
+ #
114
+ # @example Get the callback type.
115
+ # document.child_callback_type(:update, doc)
116
+ #
117
+ # @param [ Symbol ] kind The type of callback.
118
+ # @param [ Document ] child The child document
119
+ #
120
+ # @return [ Symbol ] The name of the callback.
121
+ #
122
+ # @since 2.3.0
123
+ def child_callback_type(kind, child)
124
+ kind == :update && child.new_record? ? :create : kind
125
+ end
24
126
  end
25
127
  end
@@ -142,7 +142,7 @@ module Mongoid #:nodoc
142
142
  #
143
143
  # @since 2.0.0
144
144
  def update(selector, document, options = {})
145
- updater = Threaded.update_consumer(name)
145
+ updater = Threaded.update_consumer(klass)
146
146
  if updater
147
147
  updater.consume(selector, document, options)
148
148
  else
@@ -17,11 +17,11 @@ module Mongoid #:nodoc
17
17
  include ActiveModel::Serializers::JSON
18
18
  include ActiveModel::Serializers::Xml
19
19
  include Mongoid::Atomic
20
+ include Mongoid::Dirty
20
21
  include Mongoid::Attributes
21
22
  include Mongoid::Collections
22
23
  include Mongoid::Copyable
23
24
  include Mongoid::DefaultScope
24
- include Mongoid::Dirty
25
25
  include Mongoid::Extras
26
26
  include Mongoid::Fields
27
27
  include Mongoid::Hierarchy
@@ -38,6 +38,7 @@ module Mongoid #:nodoc
38
38
  include Mongoid::Serialization
39
39
  include Mongoid::Sharding
40
40
  include Mongoid::State
41
+ include Mongoid::Timestamps::Timeless
41
42
  include Mongoid::Validations
42
43
  include Mongoid::Callbacks
43
44
  include Mongoid::MultiDatabase
@@ -210,18 +210,6 @@ module Mongoid #:nodoc:
210
210
 
211
211
  protected
212
212
 
213
- # Get the root class collection name.
214
- #
215
- # @example Get the root class collection name.
216
- # context.collection_name
217
- #
218
- # @return [ String ] The name of the collection.
219
- #
220
- # @since 2.4.3
221
- def collection_name
222
- root ? root.collection_name : nil
223
- end
224
-
225
213
  # Filters the documents against the criteria's selector
226
214
  #
227
215
  # @example Filter the documents.
@@ -263,22 +251,10 @@ module Mongoid #:nodoc:
263
251
  documents
264
252
  end
265
253
 
266
- # Get the root document for the enumerable.
267
- #
268
- # @example Get the root document.
269
- # context.root
270
- #
271
- # @return [ Document ] The root.
272
254
  def root
273
255
  @root ||= documents.first.try(:_root)
274
256
  end
275
257
 
276
- # Get the root class for the enumerable.
277
- #
278
- # @example Get the root class.
279
- # context.root_class
280
- #
281
- # @return [ Class ] The root class.
282
258
  def root_class
283
259
  @root_class ||= root ? root.class : nil
284
260
  end
@@ -330,7 +330,7 @@ module Mongoid #:nodoc:
330
330
  { "$set" => attributes },
331
331
  Safety.merge_safety_options(:multi => true)
332
332
  ).tap do
333
- Threaded.clear_safety_options!
333
+ Threaded.clear_options!
334
334
  end
335
335
  end
336
336
  alias :update :update_all
@@ -371,7 +371,7 @@ module Mongoid #:nodoc:
371
371
  :reduce => reduce.gsub("[field]", field)
372
372
  )
373
373
  value = collection.empty? ? nil : collection.first[start.to_s]
374
- value ? (value.nan? ? nil : value) : value
374
+ value && value.try(:nan?) ? nil : value
375
375
  end
376
376
 
377
377
  # Filters the field list. If no fields have been supplied, then it will be
@@ -38,7 +38,9 @@ module Mongoid #:nodoc:
38
38
  instance_variable_set(name, value ? value.dup : nil)
39
39
  end
40
40
  attributes.delete("_id")
41
- attributes.delete("versions")
41
+ if attributes.delete("versions")
42
+ attributes["version"] = 1
43
+ end
42
44
  @new_record = true
43
45
  identify
44
46
  end
@@ -153,6 +153,19 @@ module Mongoid #:nodoc:
153
153
  context.count > 0
154
154
  end
155
155
 
156
+ # Extract a single id from the provided criteria. Could be in an $and
157
+ # query or a straight _id query.
158
+ #
159
+ # @example Extract the id.
160
+ # criteria.extract_id
161
+ #
162
+ # @return [ Object ] The id.
163
+ #
164
+ # @since 2.3.0
165
+ def extract_id
166
+ selector[:_id]
167
+ end
168
+
156
169
  # When freezing a criteria we need to initialize the context first
157
170
  # otherwise the setting of the context on attempted iteration will raise a
158
171
  # runtime error.
@@ -383,19 +396,14 @@ module Mongoid #:nodoc:
383
396
  clone.tap do |crit|
384
397
  converted = BSON::ObjectId.convert(klass, attributes || {})
385
398
  converted.each_pair do |key, value|
386
- existing = crit.selector[key]
387
- unless existing
399
+ unless crit.selector[key]
388
400
  crit.selector[key] = { operator => value }
389
401
  else
390
- if existing.respond_to?(:merge)
391
- if existing.has_key?(operator)
392
- new_value = existing.values.first.send(combine, value)
393
- crit.selector[key] = { operator => new_value }
394
- else
395
- crit.selector[key][operator] = value
396
- end
402
+ if crit.selector[key].has_key?(operator)
403
+ new_value = crit.selector[key].values.first.send(combine, value)
404
+ crit.selector[key] = { operator => new_value }
397
405
  else
398
- crit.selector[key] = { operator => value }
406
+ crit.selector[key][operator] = value
399
407
  end
400
408
  end
401
409
  end