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
@@ -20,98 +20,101 @@ module Mongoid #:nodoc
20
20
  end
21
21
  end
22
22
 
23
- # Get all child +Documents+ to this +Document+, going n levels deep if
24
- # necessary. This is used when calling update persistence operations from
25
- # the root document, where changes in the entire tree need to be
26
- # determined. Note that persistence from the embedded documents will
27
- # always be preferred, since they are optimized calls... This operation
28
- # can get expensive in domains with large hierarchies.
29
- #
30
- # @example Get all the document's children.
31
- # person._children
32
- #
33
- # @return [ Array<Document> ] All child documents in the hierarchy.
34
- def _children
35
- @_children ||=
36
- [].tap do |children|
37
- relations.each_pair do |name, metadata|
38
- if metadata.embedded?
39
- child = send(name)
40
- child.to_a.each do |doc|
41
- children.push(doc)
42
- children.concat(doc._children) unless metadata.versioned?
43
- end if child
23
+ module InstanceMethods #:nodoc:
24
+
25
+ # Get all child +Documents+ to this +Document+, going n levels deep if
26
+ # necessary. This is used when calling update persistence operations from
27
+ # the root document, where changes in the entire tree need to be
28
+ # determined. Note that persistence from the embedded documents will
29
+ # always be preferred, since they are optimized calls... This operation
30
+ # can get expensive in domains with large hierarchies.
31
+ #
32
+ # @example Get all the document's children.
33
+ # person._children
34
+ #
35
+ # @return [ Array<Document> ] All child documents in the hierarchy.
36
+ def _children
37
+ @_children ||=
38
+ [].tap do |children|
39
+ relations.each_pair do |name, metadata|
40
+ if metadata.embedded?
41
+ child = send(name)
42
+ child.to_a.each do |doc|
43
+ children.push(doc)
44
+ children.concat(doc._children) unless metadata.versioned?
45
+ end if child
46
+ end
44
47
  end
45
48
  end
46
- end
47
- end
49
+ end
48
50
 
49
- # Determines if the document is a subclass of another document.
50
- #
51
- # @example Check if the document is a subclass
52
- # Square.new.hereditary?
53
- #
54
- # @return [ true, false ] True if hereditary, false if not.
55
- def hereditary?
56
- self.class.hereditary?
57
- end
51
+ # Determines if the document is a subclass of another document.
52
+ #
53
+ # @example Check if the document is a subclass
54
+ # Square.new.hereditary?
55
+ #
56
+ # @return [ true, false ] True if hereditary, false if not.
57
+ def hereditary?
58
+ self.class.hereditary?
59
+ end
58
60
 
59
- # Sets up a child/parent association. This is used for newly created
60
- # objects so they can be properly added to the graph.
61
- #
62
- # @example Set the parent document.
63
- # document.parentize(parent)
64
- #
65
- # @param [ Document ] document The parent document.
66
- #
67
- # @return [ Document ] The parent document.
68
- def parentize(document)
69
- self._parent = document
70
- end
61
+ # Sets up a child/parent association. This is used for newly created
62
+ # objects so they can be properly added to the graph.
63
+ #
64
+ # @example Set the parent document.
65
+ # document.parentize(parent)
66
+ #
67
+ # @param [ Document ] document The parent document.
68
+ #
69
+ # @return [ Document ] The parent document.
70
+ def parentize(document)
71
+ self._parent = document
72
+ end
71
73
 
72
- # Remove a child document from this parent. If an embeds one then set to
73
- # nil, otherwise remove from the embeds many.
74
- #
75
- # This is called from the +RemoveEmbedded+ persistence command.
76
- #
77
- # @example Remove the child.
78
- # document.remove_child(child)
79
- #
80
- # @param [ Document ] child The child (embedded) document to remove.
81
- #
82
- # @since 2.0.0.beta.1
83
- def remove_child(child)
84
- name = child.metadata.name
85
- child.embedded_one? ? remove_ivar(name) : send(name).delete_one(child)
86
- end
74
+ # Remove a child document from this parent. If an embeds one then set to
75
+ # nil, otherwise remove from the embeds many.
76
+ #
77
+ # This is called from the +RemoveEmbedded+ persistence command.
78
+ #
79
+ # @example Remove the child.
80
+ # document.remove_child(child)
81
+ #
82
+ # @param [ Document ] child The child (embedded) document to remove.
83
+ #
84
+ # @since 2.0.0.beta.1
85
+ def remove_child(child)
86
+ name = child.metadata.name
87
+ child.embedded_one? ? remove_ivar(name) : send(name).delete_one(child)
88
+ end
87
89
 
88
- # After children are persisted we can call this to move all their changes
89
- # and flag them as persisted in one call.
90
- #
91
- # @example Reset the children.
92
- # document.reset_persisted_children
93
- #
94
- # @return [ Array<Document> ] The children.
95
- #
96
- # @since 2.1.0
97
- def reset_persisted_children
98
- _children.each do |child|
99
- child.move_changes
100
- child.new_record = false
90
+ # After children are persisted we can call this to move all their changes
91
+ # and flag them as persisted in one call.
92
+ #
93
+ # @example Reset the children.
94
+ # document.reset_persisted_children
95
+ #
96
+ # @return [ Array<Document> ] The children.
97
+ #
98
+ # @since 2.1.0
99
+ def reset_persisted_children
100
+ _children.each do |child|
101
+ child.move_changes
102
+ child.new_record = false
103
+ end
101
104
  end
102
- end
103
105
 
104
- # Return the root document in the object graph. If the current document
105
- # is the root object in the graph it will return self.
106
- #
107
- # @example Get the root document in the hierarchy.
108
- # document._root
109
- #
110
- # @return [ Document ] The root document in the hierarchy.
111
- def _root
112
- object = self
113
- while (object._parent) do object = object._parent; end
114
- object || self
106
+ # Return the root document in the object graph. If the current document
107
+ # is the root object in the graph it will return self.
108
+ #
109
+ # @example Get the root document in the hierarchy.
110
+ # document._root
111
+ #
112
+ # @return [ Document ] The root document in the hierarchy.
113
+ def _root
114
+ object = self
115
+ while (object._parent) do object = object._parent; end
116
+ object || self
117
+ end
115
118
  end
116
119
  end
117
120
  end
@@ -69,9 +69,11 @@ module Mongoid #:nodoc:
69
69
  #
70
70
  # @return [ Array<Object> ] The array of keys.
71
71
  def compose
72
+ kf = document.key_formatter
72
73
  document.primary_key.collect do |key|
73
- document.attributes[key.to_s]
74
- end.reject { |val| val.nil? }
74
+ val = document.attributes[key.to_s]
75
+ val && kf ? kf.call(val) : val
76
+ end.compact
75
77
  end
76
78
 
77
79
  # Determines if the document stores the type information. This is if it is
@@ -9,7 +9,7 @@ module Mongoid #:nodoc:
9
9
  attr_reader :identifier
10
10
 
11
11
  included do
12
- cattr_accessor :primary_key, :using_object_ids
12
+ cattr_accessor :primary_key, :using_object_ids, :key_formatter
13
13
  self.using_object_ids = true
14
14
  end
15
15
 
@@ -122,6 +122,7 @@ module Mongoid #:nodoc:
122
122
  # @since 1.0.0
123
123
  def key(*fields)
124
124
  self.primary_key = fields
125
+ self.key_formatter = block_given? ? Proc.new : nil
125
126
  identity(:type => String)
126
127
  set_callback(:save, :around, :set_composite_key)
127
128
  end
@@ -4,13 +4,7 @@ module Mongoid #:nodoc:
4
4
  # The Mongoid logger which wraps some other ruby compliant logger class.
5
5
  class Logger
6
6
 
7
- delegate \
8
- :info,
9
- :debug,
10
- :error,
11
- :fatal,
12
- :level,
13
- :unknown, :to => :logger, :allow_nil => true
7
+ delegate :info, :debug, :error, :fatal, :unknown, :to => :logger, :allow_nil => true
14
8
 
15
9
  # Emit a warning log message.
16
10
  #
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+
5
+ # Defines behavior for handling $or expressions in embedded documents.
6
+ class And < Default
7
+
8
+ # Does the supplied query match the attribute?
9
+ #
10
+ # @example Does this match?
11
+ # matcher.matches?("$and" => [ { field => value } ])
12
+ #
13
+ # @param [ Array ] conditions The or expression.
14
+ #
15
+ # @return [ true, false ] True if matches, false if not.
16
+ #
17
+ # @since 2.3.0
18
+ def matches?(conditions)
19
+ conditions.each do |condition|
20
+ condition.keys.each do |k|
21
+ key = k
22
+ value = condition[k]
23
+ return false unless Strategies.matcher(document, key, value).matches?(value)
24
+ end
25
+ end
26
+ true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -14,7 +14,7 @@ module Mongoid #:nodoc:
14
14
  #
15
15
  # @return [ true, false ] If a value exists.
16
16
  def matches?(value)
17
- value.values.first.include?(@attribute)
17
+ Array.wrap(@attribute).any? { |e| value.values.first.include?(e) }
18
18
  end
19
19
  end
20
20
  end
@@ -14,7 +14,7 @@ module Mongoid #:nodoc:
14
14
  #
15
15
  # @return [ true, false ] If a value exists.
16
16
  def matches?(value)
17
- !value.values.first.include?(@attribute)
17
+ Array.wrap(@attribute).none? { |e| value.values.first.include?(e) }
18
18
  end
19
19
  end
20
20
  end
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require "mongoid/matchers/default"
3
3
  require "mongoid/matchers/all"
4
+ require "mongoid/matchers/and"
4
5
  require "mongoid/matchers/exists"
5
6
  require "mongoid/matchers/gt"
6
7
  require "mongoid/matchers/gte"
@@ -22,6 +23,7 @@ module Mongoid #:nodoc:
22
23
 
23
24
  MATCHERS = {
24
25
  "$all" => Matchers::All,
26
+ "$and" => Matchers::And,
25
27
  "$exists" => Matchers::Exists,
26
28
  "$gt" => Matchers::Gt,
27
29
  "$gte" => Matchers::Gte,
@@ -51,10 +53,10 @@ module Mongoid #:nodoc:
51
53
  if value.is_a?(Hash)
52
54
  MATCHERS[value.keys.first].new(extract_attribute(document, key))
53
55
  else
54
- if key == "$or"
55
- Matchers::Or.new(value, document)
56
- else
57
- Default.new(extract_attribute(document, key))
56
+ case key
57
+ when "$or" then Matchers::Or.new(value, document)
58
+ when "$and" then Matchers::And.new(value, document)
59
+ else Default.new(extract_attribute(document, key))
58
60
  end
59
61
  end
60
62
  end
@@ -44,10 +44,11 @@ module Mongoid #:nodoc:
44
44
  # person.process(:title => "sir", :age => 40)
45
45
  #
46
46
  # @param [ Hash ] attrs The attributes to set.
47
+ # @param [ Symbol ] role A role for scoped mass assignment.
47
48
  # @param [ Boolean ] guard_protected_attributes False to skip mass assignment protection.
48
49
  #
49
50
  # @since 2.0.0.rc.7
50
- def process(attrs = nil, guard_protected_attributes = true)
51
+ def process(attrs = nil, role = :default, guard_protected_attributes = true)
51
52
  if attrs
52
53
  errors = []
53
54
  attributes = {}
@@ -81,7 +82,7 @@ module Mongoid #:nodoc:
81
82
  )
82
83
  end
83
84
 
84
- super attributes, guard_protected_attributes
85
+ super attributes, role, guard_protected_attributes
85
86
  else
86
87
  super
87
88
  end
@@ -125,20 +125,10 @@ module Mongoid #:nodoc:
125
125
 
126
126
  protected
127
127
 
128
- # Warns if overriding another scope or method.
129
- #
130
- # @example Warn if name exists.
131
- # Model.valid_scope_name?("test")
132
- #
133
- # @param [ String, Symbol ] name The name of the scope.
134
128
  def valid_scope_name?(name)
135
- if scopes[name] || respond_to?(name, true)
136
- if Mongoid.logger
137
- Mongoid.logger.warn(
138
- "Creating scope :#{name}. " +
139
- "Overwriting existing method #{self.name}.#{name}."
140
- )
141
- end
129
+ if !scopes[name] && respond_to?(name, true)
130
+ Mongoid.logger.warn "Creating scope :#{name}. " \
131
+ "Overwriting existing method #{self.name}.#{name}." if Mongoid.logger
142
132
  end
143
133
  end
144
134
  end
@@ -54,7 +54,7 @@ module Mongoid #:nodoc:
54
54
  args.each do |name|
55
55
  self.nested_attributes += [ "#{name}_attributes=" ]
56
56
  define_method("#{name}_attributes=") do |attrs|
57
- assigning do
57
+ _assigning do
58
58
  relation = relations[name.to_s]
59
59
  relation.nested_builder(attrs, options).build(self)
60
60
  end
@@ -45,7 +45,6 @@ module Mongoid #:nodoc:
45
45
  #
46
46
  # @return [ true ] True.
47
47
  def remove(options = {})
48
- return super if embedded? && respond_to?(:version_max) && version_max
49
48
  now = Time.now
50
49
  collection.update({ :_id => id }, { '$set' => { :deleted_at => now } })
51
50
  @attributes["deleted_at"] = now
@@ -86,7 +85,7 @@ module Mongoid #:nodoc:
86
85
  #
87
86
  # @return [ Criteria ] The paranoid compliant criteria.
88
87
  def criteria(embedded = false, scoped = true)
89
- scoped ? super.where(:deleted_at.exists => false) : super
88
+ scoped ? super.merge({ :conditions => { :deleted_at => nil } }) : super
90
89
  end
91
90
 
92
91
  # Find deleted documents
@@ -98,7 +97,7 @@ module Mongoid #:nodoc:
98
97
  #
99
98
  # @return [ Criteria ] The deleted criteria.
100
99
  def deleted
101
- where(:deleted_at.exists => true)
100
+ where(:deleted_at.ne => nil)
102
101
  end
103
102
  end
104
103
  end
@@ -163,11 +163,13 @@ module Mongoid #:nodoc:
163
163
  # Person.create(:title => "Mr")
164
164
  #
165
165
  # @param [ Hash ] attributes The attributes to create with.
166
+ # @param [ Hash ] options A mass-assignment protection options. Supports
167
+ # :as and :without_protection
166
168
  #
167
169
  # @return [ Document ] The newly created document.
168
- def create(attributes = {}, &block)
170
+ def create(attributes = {}, options = {}, &block)
169
171
  creating do
170
- new(attributes, &block).tap { |doc| doc.save }
172
+ new(attributes, options, &block).tap { |doc| doc.save }
171
173
  end
172
174
  end
173
175
 
@@ -180,11 +182,13 @@ module Mongoid #:nodoc:
180
182
  # Person.create!(:title => "Mr")
181
183
  #
182
184
  # @param [ Hash ] attributes The attributes to create with.
185
+ # @param [ Hash ] options A mass-assignment protection options. Supports
186
+ # :as and :without_protection
183
187
  #
184
188
  # @return [ Document ] The newly created document.
185
- def create!(attributes = {}, &block)
189
+ def create!(attributes = {}, options = {}, &block)
186
190
  creating do
187
- new(attributes, &block).tap do |doc|
191
+ new(attributes, options, &block).tap do |doc|
188
192
  fail_validate!(doc) if doc.insert.errors.any?
189
193
  fail_callback!(doc, :create!) if doc.new?
190
194
  end
@@ -209,7 +213,7 @@ module Mongoid #:nodoc:
209
213
  selector.merge!(:_type => name) if hereditary?
210
214
  collection.find(selector).count.tap do
211
215
  collection.remove(selector, Safety.merge_safety_options)
212
- Threaded.clear_safety_options!
216
+ Threaded.clear_options!
213
217
  end
214
218
  end
215
219
 
@@ -76,7 +76,7 @@ module Mongoid #:nodoc:
76
76
  # @since 2.1.0
77
77
  def prepare
78
78
  yield(document).tap do
79
- Threaded.clear_safety_options!
79
+ Threaded.clear_options!
80
80
  end
81
81
  end
82
82
  end