mongoid 2.2.6 → 2.3.0

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.
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
@@ -24,7 +24,7 @@ module Mongoid #:nodoc:
24
24
  document.freeze
25
25
  document.destroyed = true
26
26
  IdentityMap.remove(document)
27
- Threaded.clear_safety_options!
27
+ Threaded.clear_options!
28
28
  true
29
29
  end
30
30
  end
@@ -31,7 +31,7 @@ module Mongoid #:nodoc:
31
31
 
32
32
  unless result == false
33
33
  doc.move_changes
34
- Threaded.clear_safety_options!
34
+ Threaded.clear_options!
35
35
  end
36
36
  end
37
37
  end
@@ -28,7 +28,7 @@ module Mongoid #:nodoc:
28
28
  unless result == false
29
29
  document.reset_persisted_children
30
30
  document.move_changes
31
- Threaded.clear_safety_options!
31
+ Threaded.clear_options!
32
32
  end
33
33
  end
34
34
  end
@@ -95,7 +95,7 @@ module Rails #:nodoc:
95
95
  # environments.
96
96
  initializer "preload all application models" do |app|
97
97
  config.to_prepare do
98
- ::Rails::Mongoid.load_models(app) unless $rails_rake_task
98
+ ::Rails::Mongoid.preload_models(app) unless $rails_rake_task
99
99
  end
100
100
  end
101
101
 
@@ -79,7 +79,15 @@ namespace :db do
79
79
 
80
80
  desc 'Create the indexes defined on your mongoid models'
81
81
  task :create_indexes => :environment do
82
- ::Rails::Mongoid.create_indexes("app/models/**/*.rb")
82
+ engines_models_paths = Rails.application.railties.engines.map do |engine|
83
+ engine.paths["app/models"].paths
84
+ end
85
+ root_models_paths = Rails.application.paths["app/models"]
86
+ models_paths = engines_models_paths.push(root_models_paths).flatten
87
+
88
+ models_paths.each do |path|
89
+ ::Rails::Mongoid.create_indexes("#{path}/**/*.rb")
90
+ end
83
91
  end
84
92
 
85
93
  def convert_ids(obj)
@@ -3,6 +3,7 @@ require "mongoid/relations/accessors"
3
3
  require "mongoid/relations/auto_save"
4
4
  require "mongoid/relations/cascading"
5
5
  require "mongoid/relations/constraint"
6
+ require "mongoid/relations/conversions"
6
7
  require "mongoid/relations/cyclic"
7
8
  require "mongoid/relations/proxy"
8
9
  require "mongoid/relations/bindings"
@@ -96,7 +96,7 @@ module Mongoid # :nodoc:
96
96
  if instance_variable_defined?(variable) && !reload
97
97
  instance_variable_get(variable)
98
98
  else
99
- building do
99
+ _building do
100
100
  build(name, attributes[metadata.key], metadata, :loading => true)
101
101
  end
102
102
  end
@@ -35,14 +35,14 @@ module Mongoid # :nodoc:
35
35
  # Execute a block in building mode.
36
36
  #
37
37
  # @example Execute in building mode.
38
- # building do
38
+ # _building do
39
39
  # relation.push(doc)
40
40
  # end
41
41
  #
42
42
  # @return [ Object ] The return value of the block.
43
43
  #
44
44
  # @since 2.1.0
45
- def building
45
+ def _building
46
46
  Threaded.begin_build
47
47
  yield
48
48
  ensure
@@ -65,8 +65,10 @@ module Mongoid # :nodoc:
65
65
  def builder(name, metadata)
66
66
  tap do
67
67
  define_method("build_#{name}") do |*args|
68
- document = Factory.build(metadata.klass, args.first || {})
69
- building do
68
+ attributes = args.first || {}
69
+ options = args.size > 1 ? args[1] : {}
70
+ document = Factory.build(metadata.klass, attributes, options)
71
+ _building do
70
72
  send("#{name}=", document)
71
73
  end
72
74
  end
@@ -17,31 +17,9 @@ module Mongoid # :nodoc:
17
17
  def build(type = nil)
18
18
  return object unless query?
19
19
  return [] if object.is_a?(Array)
20
- crit = metadata.criteria(convertable(metadata, object))
20
+ crit = metadata.criteria(Conversions.flag(object, metadata))
21
21
  IdentityMap.get(crit.klass, crit.selector) || crit
22
22
  end
23
-
24
- private
25
-
26
- # Get the value for the foreign key in convertable or unconvertable
27
- # form.
28
- #
29
- # @example Get the value.
30
- # builder.convertable
31
- #
32
- # @return [ String, BSON::ObjectId ] The string or object id.
33
- #
34
- # @since 2.0.2
35
- def convertable(metadata, object)
36
- inverse = metadata.inverse_klass
37
- if inverse.using_object_ids? || object.is_a?(BSON::ObjectId)
38
- object
39
- else
40
- object.tap do |obj|
41
- obj.unconvertable_to_bson = true if obj.is_a?(String)
42
- end
43
- end
44
- end
45
23
  end
46
24
  end
47
25
  end
@@ -16,7 +16,7 @@ module Mongoid # :nodoc:
16
16
  # @return [ Document ] A single document.
17
17
  def build(type = nil)
18
18
  return object unless query?
19
- criteria = metadata.criteria(object)
19
+ criteria = metadata.criteria(Conversions.flag(object, metadata))
20
20
  IdentityMap.get(criteria.klass, criteria.selector) || criteria.first
21
21
  end
22
22
  end
@@ -26,9 +26,11 @@ module Mongoid # :nodoc:
26
26
  # @since 2.0.0.rc.1
27
27
  def cascade!
28
28
  cascades.each do |name|
29
- metadata = relations[name]
30
- strategy = metadata.cascade_strategy
31
- strategy.new(self, metadata).cascade
29
+ if !metadata || !metadata.versioned?
30
+ meta = relations[name]
31
+ strategy = meta.cascade_strategy
32
+ strategy.new(self, meta).cascade
33
+ end
32
34
  end
33
35
  end
34
36
 
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # Contains utility methods for object id conversion.
6
+ module Conversions
7
+ extend self
8
+
9
+ # Mark the provided object as unconvertable to bson or not, and always
10
+ # return the provided object.
11
+ #
12
+ # @example Flag the object.
13
+ # Conversions.flag(metadata, 15)
14
+ #
15
+ # @param [ Object ] object The object to flag.
16
+ # @param [ Metadata ] The relation metadata.
17
+ #
18
+ # @return [ Object ] The provided object.
19
+ #
20
+ # @since 2.3.0
21
+ def flag(object, metadata)
22
+ inverse = metadata.inverse_klass
23
+ if inverse.using_object_ids? || object.is_a?(BSON::ObjectId)
24
+ object
25
+ else
26
+ object.tap do |obj|
27
+ if obj.is_a?(String)
28
+ obj.unconvertable_to_bson = true
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -55,13 +55,13 @@ module Mongoid #:nodoc:
55
55
  #
56
56
  # @since 2.0.0
57
57
  def atomically(modifier, &block)
58
- updater = Threaded.update_consumer(collection_name) ||
59
- Threaded.set_update_consumer(collection_name, MODIFIERS[modifier].new)
58
+ updater = Threaded.update_consumer(root_class) ||
59
+ Threaded.set_update_consumer(root_class, MODIFIERS[modifier].new)
60
60
  count_executions do
61
61
  block.call if block
62
62
  end.tap do
63
63
  if @executions.zero?
64
- Threaded.set_update_consumer(collection_name, nil)
64
+ Threaded.set_update_consumer(root_class, nil)
65
65
  updater.execute(collection)
66
66
  end
67
67
  end
@@ -89,7 +89,7 @@ module Mongoid # :nodoc:
89
89
  #
90
90
  # @since 2.1.0
91
91
  def persistable?
92
- target.persisted? && !binding? && !building?
92
+ target.persisted? && !binding? && !_building?
93
93
  end
94
94
 
95
95
  class << self
@@ -26,7 +26,7 @@ module Mongoid # :nodoc:
26
26
  args.flatten.each do |doc|
27
27
  next unless doc
28
28
  append(doc)
29
- doc.save if persistable? && !assigning?
29
+ doc.save if persistable? && !_assigning?
30
30
  end
31
31
  end
32
32
  end
@@ -40,11 +40,12 @@ module Mongoid # :nodoc:
40
40
  # person.people.build(:name => "Bozo")
41
41
  #
42
42
  # @param [ Hash ] attributes The attributes to build the document with.
43
+ # @param [ Hash ] options The scoped assignment options.
43
44
  # @param [ Class ] type Optional class to build the document with.
44
45
  #
45
46
  # @return [ Document ] The new document.
46
- def build(attributes = {}, type = nil)
47
- Factory.build(type || metadata.klass, attributes).tap do |doc|
47
+ def build(attributes = {}, options = {}, type = nil)
48
+ Factory.build(type || metadata.klass, attributes, options).tap do |doc|
48
49
  doc.identify
49
50
  append(doc)
50
51
  yield(doc) if block_given?
@@ -86,11 +87,12 @@ module Mongoid # :nodoc:
86
87
  # person.movies.create(:name => "Bozo")
87
88
  #
88
89
  # @param [ Hash ] attributes The attributes to build the document with.
90
+ # @param [ Hash ] options The scoped assignment options.
89
91
  # @param [ Class ] type Optional class to create the document with.
90
92
  #
91
93
  # @return [ Document ] The newly created document.
92
- def create(attributes = {}, type = nil, &block)
93
- build(attributes, type, &block).tap { |doc| doc.save }
94
+ def create(attributes = {}, options = {}, type = nil, &block)
95
+ build(attributes, options, type, &block).tap { |doc| doc.save }
94
96
  end
95
97
 
96
98
  # Create a new document in the relation. This is essentially the same
@@ -101,13 +103,14 @@ module Mongoid # :nodoc:
101
103
  # person.addresses.create!(:street => "Unter der Linden")</tt>
102
104
  #
103
105
  # @param [ Hash ] attributes The attributes to build the document with.
106
+ # @param [ Hash ] options The scoped assignment options.
104
107
  # @param [ Class ] type Optional class to create the document with.
105
108
  #
106
109
  # @raise [ Errors::Validations ] If a validation error occured.
107
110
  #
108
111
  # @return [ Document ] The newly created document.
109
- def create!(attributes = {}, type = nil, &block)
110
- build(attributes, type, &block).tap { |doc| doc.save! }
112
+ def create!(attributes = {}, options = {}, type = nil, &block)
113
+ build(attributes, options, type, &block).tap { |doc| doc.save! }
111
114
  end
112
115
 
113
116
  # Delete the supplied document from the target. This method is proxied
@@ -124,7 +127,7 @@ module Mongoid # :nodoc:
124
127
  def delete(document)
125
128
  target.delete_one(document).tap do |doc|
126
129
  if doc && !binding?
127
- if assigning?
130
+ if _assigning?
128
131
  base.add_atomic_pull(doc)
129
132
  else
130
133
  doc.delete(:suppress => true)
@@ -232,7 +235,7 @@ module Mongoid # :nodoc:
232
235
  def substitute(replacement)
233
236
  tap do |proxy|
234
237
  if replacement.blank?
235
- if assigning? && !proxy.empty?
238
+ if _assigning? && !proxy.empty?
236
239
  base.atomic_unsets.push(proxy.first.atomic_path)
237
240
  end
238
241
  proxy.clear
@@ -242,13 +245,13 @@ module Mongoid # :nodoc:
242
245
  replacement = Many.builder(metadata, replacement).build
243
246
  end
244
247
  proxy.target = replacement.compact
245
- if assigning?
248
+ if _assigning?
246
249
  base.delayed_atomic_sets[metadata.name.to_s] = proxy.as_document
247
250
  end
248
251
  proxy.target.each_with_index do |doc, index|
249
252
  integrate(doc)
250
253
  doc._index = index
251
- doc.save if base.persisted? && !assigning?
254
+ doc.save if base.persisted? && !_assigning?
252
255
  end
253
256
  end
254
257
  end
@@ -400,7 +403,7 @@ module Mongoid # :nodoc:
400
403
  criteria.size.tap do
401
404
  criteria.each do |doc|
402
405
  target.delete_one(doc)
403
- doc.send(method, :suppress => true) unless assigning?
406
+ doc.send(method, :suppress => true) unless _assigning?
404
407
  unbind_one(doc)
405
408
  end
406
409
  reindex
@@ -515,7 +518,7 @@ module Mongoid # :nodoc:
515
518
  #
516
519
  # @since 2.1.0
517
520
  def valid_options
518
- [ :as, :cyclic, :order, :versioned ]
521
+ [ :as, :cascade_callbacks, :cyclic, :order, :versioned ]
519
522
  end
520
523
 
521
524
  # Get the default validation setting for the relation. Determines if
@@ -36,7 +36,7 @@ module Mongoid # :nodoc:
36
36
  # @since 2.0.0.rc.1
37
37
  def substitute(replacement)
38
38
  tap do |proxy|
39
- if assigning?
39
+ if _assigning?
40
40
  base.atomic_unsets.push(proxy.atomic_path)
41
41
  else
42
42
  proxy.delete if persistable?
@@ -73,7 +73,7 @@ module Mongoid # :nodoc:
73
73
  #
74
74
  # @since 2.1.0
75
75
  def persistable?
76
- base.persisted? && !binding? && !building? && !assigning?
76
+ base.persisted? && !binding? && !_building? && !_assigning?
77
77
  end
78
78
 
79
79
  class << self
@@ -183,7 +183,7 @@ module Mongoid # :nodoc:
183
183
  #
184
184
  # @since 2.1.0
185
185
  def valid_options
186
- [ :as, :cyclic ]
186
+ [ :as, :cascade_callbacks, :cyclic ]
187
187
  end
188
188
 
189
189
  # Get the default validation setting for the relation. Determines if
@@ -89,6 +89,19 @@ module Mongoid # :nodoc:
89
89
  end
90
90
  end
91
91
 
92
+ # Is this an embedded relations that allows callbacks to cascade down to
93
+ # it?
94
+ #
95
+ # @example Does the relation have cascading callbacks?
96
+ # metadata.cascading_callbacks?
97
+ #
98
+ # @return [ true, false ] If the relation cascades callbacks.
99
+ #
100
+ # @since 2.3.0
101
+ def cascading_callbacks?
102
+ !!self[:cascade_callbacks]
103
+ end
104
+
92
105
  # Returns the name of the class that this relation contains. If the
93
106
  # class_name was provided as an option this will return that, otherwise
94
107
  # it will determine the name from the name property.
@@ -189,7 +202,10 @@ module Mongoid # :nodoc:
189
202
  #
190
203
  # @since 2.2.0
191
204
  def eager_load(criteria)
192
- relation.eager_load(self, criteria.clone)
205
+ relation.eager_load(
206
+ self,
207
+ criteria.clone.tap { |crit| crit.inclusions.clear }
208
+ )
193
209
  end
194
210
 
195
211
  # Will determine if the relation is an embedded one or not. Currently
@@ -229,19 +245,6 @@ module Mongoid # :nodoc:
229
245
  !!extension
230
246
  end
231
247
 
232
- # Does this metadata have a forced nil inverse_of defined. (Used in many
233
- # to manies)
234
- #
235
- # @example Is this a forced nil inverse?
236
- # metadata.forced_nil_inverse?
237
- #
238
- # @return [ true, false ] If inverse_of has been explicitly set to nil.
239
- #
240
- # @since 2.3.3
241
- def forced_nil_inverse?
242
- has_key?(:inverse_of) && inverse_of.nil?
243
- end
244
-
245
248
  # Handles all the logic for figuring out what the foreign_key is for each
246
249
  # relations query. The logic is as follows:
247
250
  #
@@ -773,7 +776,7 @@ module Mongoid # :nodoc:
773
776
  #
774
777
  # @since 2.0.0.rc.1
775
778
  def determine_cyclic_inverse
776
- underscored = class_name.underscore
779
+ underscored = class_name.demodulize.underscore
777
780
  klass.relations.each_pair do |key, meta|
778
781
  if key =~ /#{underscored.singularize}|#{underscored.pluralize}/ &&
779
782
  meta.relation != relation
@@ -823,6 +826,7 @@ module Mongoid # :nodoc:
823
826
  default = klass.relations[inverse_klass.name.underscore]
824
827
  return default.name if default
825
828
  klass.relations.each_pair do |key, meta|
829
+ next if meta.versioned? || meta.name == name
826
830
  if meta.class_name == inverse_class_name
827
831
  return key.to_sym
828
832
  end
@@ -17,7 +17,6 @@ module Mongoid # :nodoc:
17
17
  # Backwards compatibility with Mongoid beta releases.
18
18
  delegate :klass, :to => :metadata
19
19
  delegate :bind_one, :unbind_one, :to => :binding
20
- delegate :collection_name, :to => :base
21
20
 
22
21
  # Convenience for setting the target and the metadata properties since
23
22
  # all proxies will need to do this.
@@ -54,12 +53,12 @@ module Mongoid # :nodoc:
54
53
  # Is the current thread in assigning mode?
55
54
  #
56
55
  # @example Is the current thread in assigning mode?
57
- # proxy.assigning?
56
+ # proxy._assigning?
58
57
  #
59
58
  # @return [ true, false ] If the thread is assigning.
60
59
  #
61
60
  # @since 2.1.0
62
- def assigning?
61
+ def _assigning?
63
62
  Threaded.assigning?
64
63
  end
65
64
 
@@ -78,12 +77,12 @@ module Mongoid # :nodoc:
78
77
  # Is the current thread in building mode?
79
78
  #
80
79
  # @example Is the current thread in building mode?
81
- # proxy.building?
80
+ # proxy._building?
82
81
  #
83
82
  # @return [ true, false ] If the thread is building.
84
83
  #
85
84
  # @since 2.1.0
86
- def building?
85
+ def _building?
87
86
  Threaded.building?
88
87
  end
89
88