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