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