mongoid 2.0.2 → 2.1.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 (240) hide show
  1. data/README.rdoc +3 -1
  2. data/Rakefile +3 -2
  3. data/lib/config/locales/bg.yml +6 -0
  4. data/lib/config/locales/de.yml +6 -0
  5. data/lib/config/locales/en-GB.yml +48 -0
  6. data/lib/config/locales/en.yml +6 -3
  7. data/lib/config/locales/es.yml +6 -0
  8. data/lib/config/locales/fr.yml +6 -0
  9. data/lib/config/locales/hi.yml +39 -0
  10. data/lib/config/locales/hu.yml +13 -7
  11. data/lib/config/locales/id.yml +3 -0
  12. data/lib/config/locales/it.yml +7 -1
  13. data/lib/config/locales/ja.yml +4 -1
  14. data/lib/config/locales/kr.yml +9 -34
  15. data/lib/config/locales/nl.yml +6 -0
  16. data/lib/config/locales/pl.yml +6 -0
  17. data/lib/config/locales/pt-BR.yml +6 -0
  18. data/lib/config/locales/pt.yml +6 -0
  19. data/lib/config/locales/ro.yml +6 -0
  20. data/lib/config/locales/ru.yml +6 -0
  21. data/lib/config/locales/sv.yml +6 -0
  22. data/lib/config/locales/vi.yml +3 -0
  23. data/lib/config/locales/zh-CN.yml +6 -0
  24. data/lib/mongoid.rb +51 -45
  25. data/lib/mongoid/atomic.rb +145 -0
  26. data/lib/mongoid/atomic/modifiers.rb +109 -0
  27. data/lib/mongoid/atomic/paths.rb +3 -0
  28. data/lib/mongoid/atomic/paths/embedded.rb +43 -0
  29. data/lib/mongoid/atomic/paths/embedded/many.rb +44 -0
  30. data/lib/mongoid/atomic/paths/embedded/one.rb +43 -0
  31. data/lib/mongoid/atomic/paths/root.rb +40 -0
  32. data/lib/mongoid/attributes.rb +12 -23
  33. data/lib/mongoid/attributes/processing.rb +5 -5
  34. data/lib/mongoid/callbacks.rb +2 -0
  35. data/lib/mongoid/collection.rb +12 -59
  36. data/lib/mongoid/collections.rb +23 -20
  37. data/lib/mongoid/collections/master.rb +6 -4
  38. data/lib/mongoid/collections/operations.rb +1 -0
  39. data/lib/mongoid/collections/retry.rb +7 -0
  40. data/lib/mongoid/components.rb +2 -2
  41. data/lib/mongoid/config.rb +42 -55
  42. data/lib/mongoid/config/database.rb +6 -2
  43. data/lib/mongoid/config/replset_database.rb +7 -3
  44. data/lib/mongoid/contexts.rb +9 -3
  45. data/lib/mongoid/contexts/enumerable.rb +7 -3
  46. data/lib/mongoid/contexts/mongo.rb +139 -101
  47. data/lib/mongoid/criteria.rb +86 -69
  48. data/lib/mongoid/criterion/complex.rb +32 -5
  49. data/lib/mongoid/criterion/inclusion.rb +4 -2
  50. data/lib/mongoid/criterion/optional.rb +111 -86
  51. data/lib/mongoid/criterion/selector.rb +8 -4
  52. data/lib/mongoid/cursor.rb +27 -27
  53. data/lib/mongoid/dirty.rb +54 -214
  54. data/lib/mongoid/document.rb +37 -39
  55. data/lib/mongoid/errors/document_not_found.rb +3 -4
  56. data/lib/mongoid/errors/invalid_collection.rb +2 -3
  57. data/lib/mongoid/errors/invalid_database.rb +2 -3
  58. data/lib/mongoid/errors/invalid_field.rb +2 -3
  59. data/lib/mongoid/errors/invalid_options.rb +19 -7
  60. data/lib/mongoid/errors/invalid_type.rb +2 -3
  61. data/lib/mongoid/errors/mongoid_error.rb +5 -6
  62. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +2 -3
  63. data/lib/mongoid/errors/unsupported_version.rb +2 -3
  64. data/lib/mongoid/errors/validations.rb +2 -3
  65. data/lib/mongoid/extensions.rb +8 -62
  66. data/lib/mongoid/extensions/array/deletion.rb +29 -0
  67. data/lib/mongoid/extensions/false_class/equality.rb +14 -1
  68. data/lib/mongoid/extensions/hash/criteria_helpers.rb +21 -10
  69. data/lib/mongoid/extensions/hash/scoping.rb +14 -1
  70. data/lib/mongoid/extensions/nil/collectionization.rb +12 -1
  71. data/lib/mongoid/extensions/object/reflections.rb +33 -2
  72. data/lib/mongoid/extensions/object_id/conversions.rb +2 -36
  73. data/lib/mongoid/extensions/proc/scoping.rb +14 -1
  74. data/lib/mongoid/extensions/string/conversions.rb +4 -16
  75. data/lib/mongoid/extensions/string/inflections.rb +35 -14
  76. data/lib/mongoid/extensions/symbol/inflections.rb +38 -12
  77. data/lib/mongoid/extensions/true_class/equality.rb +14 -1
  78. data/lib/mongoid/extras.rb +11 -30
  79. data/lib/mongoid/factory.rb +1 -1
  80. data/lib/mongoid/fields.rb +121 -29
  81. data/lib/mongoid/fields/mappings.rb +36 -0
  82. data/lib/mongoid/fields/serializable.rb +131 -0
  83. data/lib/mongoid/fields/serializable/array.rb +64 -0
  84. data/lib/mongoid/fields/serializable/big_decimal.rb +42 -0
  85. data/lib/mongoid/fields/serializable/bignum.rb +10 -0
  86. data/lib/mongoid/fields/serializable/binary.rb +11 -0
  87. data/lib/mongoid/fields/serializable/boolean.rb +44 -0
  88. data/lib/mongoid/fields/serializable/date.rb +51 -0
  89. data/lib/mongoid/fields/serializable/date_time.rb +28 -0
  90. data/lib/mongoid/fields/serializable/fixnum.rb +10 -0
  91. data/lib/mongoid/fields/serializable/float.rb +33 -0
  92. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +56 -0
  93. data/lib/mongoid/fields/serializable/foreign_keys/object.rb +43 -0
  94. data/lib/mongoid/fields/serializable/hash.rb +25 -0
  95. data/lib/mongoid/fields/serializable/integer.rb +33 -0
  96. data/lib/mongoid/fields/serializable/object.rb +11 -0
  97. data/lib/mongoid/fields/serializable/object_id.rb +32 -0
  98. data/lib/mongoid/fields/serializable/range.rb +42 -0
  99. data/lib/mongoid/fields/serializable/set.rb +42 -0
  100. data/lib/mongoid/fields/serializable/string.rb +28 -0
  101. data/lib/mongoid/fields/serializable/symbol.rb +28 -0
  102. data/lib/mongoid/fields/serializable/time.rb +12 -0
  103. data/lib/mongoid/fields/serializable/time_with_zone.rb +12 -0
  104. data/lib/mongoid/fields/serializable/timekeeping.rb +102 -0
  105. data/lib/mongoid/finders.rb +61 -37
  106. data/lib/mongoid/hierarchy.rb +43 -8
  107. data/lib/mongoid/identity_map.rb +106 -0
  108. data/lib/mongoid/indexes.rb +17 -1
  109. data/lib/mongoid/javascript.rb +2 -3
  110. data/lib/mongoid/keys.rb +10 -21
  111. data/lib/mongoid/logger.rb +22 -1
  112. data/lib/mongoid/matchers/all.rb +10 -0
  113. data/lib/mongoid/matchers/default.rb +1 -1
  114. data/lib/mongoid/matchers/exists.rb +10 -0
  115. data/lib/mongoid/matchers/gt.rb +10 -0
  116. data/lib/mongoid/matchers/gte.rb +10 -0
  117. data/lib/mongoid/matchers/in.rb +10 -0
  118. data/lib/mongoid/matchers/lt.rb +10 -0
  119. data/lib/mongoid/matchers/lte.rb +10 -0
  120. data/lib/mongoid/matchers/ne.rb +10 -0
  121. data/lib/mongoid/matchers/nin.rb +10 -0
  122. data/lib/mongoid/matchers/or.rb +7 -4
  123. data/lib/mongoid/matchers/size.rb +10 -0
  124. data/lib/mongoid/multi_database.rb +26 -6
  125. data/lib/mongoid/multi_parameter_attributes.rb +40 -17
  126. data/lib/mongoid/named_scope.rb +1 -2
  127. data/lib/mongoid/nested_attributes.rb +4 -1
  128. data/lib/mongoid/observer.rb +108 -5
  129. data/lib/mongoid/paranoia.rb +26 -26
  130. data/lib/mongoid/persistence.rb +15 -21
  131. data/lib/mongoid/persistence/atomic.rb +135 -0
  132. data/lib/mongoid/persistence/atomic/add_to_set.rb +11 -8
  133. data/lib/mongoid/persistence/atomic/bit.rb +37 -0
  134. data/lib/mongoid/persistence/atomic/inc.rb +9 -6
  135. data/lib/mongoid/persistence/atomic/operation.rb +48 -7
  136. data/lib/mongoid/persistence/atomic/pop.rb +34 -0
  137. data/lib/mongoid/persistence/atomic/pull.rb +34 -0
  138. data/lib/mongoid/persistence/atomic/pull_all.rb +10 -9
  139. data/lib/mongoid/persistence/atomic/push.rb +8 -5
  140. data/lib/mongoid/persistence/atomic/push_all.rb +31 -0
  141. data/lib/mongoid/persistence/atomic/rename.rb +31 -0
  142. data/lib/mongoid/persistence/atomic/set.rb +30 -0
  143. data/lib/mongoid/persistence/atomic/unset.rb +28 -0
  144. data/lib/mongoid/persistence/deletion.rb +32 -0
  145. data/lib/mongoid/persistence/insertion.rb +41 -0
  146. data/lib/mongoid/persistence/modification.rb +37 -0
  147. data/lib/mongoid/persistence/operations.rb +214 -0
  148. data/lib/mongoid/persistence/operations/embedded/insert.rb +42 -0
  149. data/lib/mongoid/persistence/operations/embedded/remove.rb +40 -0
  150. data/lib/mongoid/persistence/operations/insert.rb +34 -0
  151. data/lib/mongoid/persistence/operations/remove.rb +33 -0
  152. data/lib/mongoid/persistence/operations/update.rb +53 -0
  153. data/lib/mongoid/railtie.rb +21 -33
  154. data/lib/mongoid/railties/database.rake +12 -12
  155. data/lib/mongoid/relations.rb +9 -5
  156. data/lib/mongoid/relations/accessors.rb +15 -36
  157. data/lib/mongoid/relations/auto_save.rb +2 -2
  158. data/lib/mongoid/relations/binding.rb +28 -1
  159. data/lib/mongoid/relations/bindings/embedded/in.rb +17 -30
  160. data/lib/mongoid/relations/bindings/embedded/many.rb +16 -21
  161. data/lib/mongoid/relations/bindings/embedded/one.rb +11 -16
  162. data/lib/mongoid/relations/bindings/referenced/in.rb +31 -32
  163. data/lib/mongoid/relations/bindings/referenced/many.rb +19 -61
  164. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +15 -63
  165. data/lib/mongoid/relations/bindings/referenced/one.rb +18 -26
  166. data/lib/mongoid/relations/builder.rb +4 -2
  167. data/lib/mongoid/relations/builders.rb +21 -2
  168. data/lib/mongoid/relations/builders/embedded/in.rb +5 -1
  169. data/lib/mongoid/relations/builders/embedded/many.rb +12 -4
  170. data/lib/mongoid/relations/builders/embedded/one.rb +5 -1
  171. data/lib/mongoid/relations/builders/nested_attributes/many.rb +2 -2
  172. data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
  173. data/lib/mongoid/relations/builders/referenced/in.rb +2 -5
  174. data/lib/mongoid/relations/builders/referenced/many.rb +2 -3
  175. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +14 -5
  176. data/lib/mongoid/relations/builders/referenced/one.rb +2 -3
  177. data/lib/mongoid/relations/embedded/atomic.rb +2 -2
  178. data/lib/mongoid/relations/embedded/in.rb +72 -41
  179. data/lib/mongoid/relations/embedded/many.rb +116 -120
  180. data/lib/mongoid/relations/embedded/one.rb +59 -41
  181. data/lib/mongoid/relations/embedded/sort.rb +31 -0
  182. data/lib/mongoid/relations/macros.rb +28 -24
  183. data/lib/mongoid/relations/many.rb +10 -103
  184. data/lib/mongoid/relations/metadata.rb +335 -38
  185. data/lib/mongoid/relations/one.rb +7 -32
  186. data/lib/mongoid/relations/options.rb +47 -0
  187. data/lib/mongoid/relations/proxy.rb +29 -28
  188. data/lib/mongoid/relations/referenced/batch.rb +2 -3
  189. data/lib/mongoid/relations/referenced/in.rb +66 -53
  190. data/lib/mongoid/relations/referenced/many.rb +216 -143
  191. data/lib/mongoid/relations/referenced/many_to_many.rb +132 -163
  192. data/lib/mongoid/relations/referenced/one.rb +76 -58
  193. data/lib/mongoid/relations/synchronization.rb +113 -0
  194. data/lib/mongoid/relations/targets.rb +2 -0
  195. data/lib/mongoid/relations/targets/enumerable.rb +329 -0
  196. data/lib/mongoid/safety.rb +24 -156
  197. data/lib/mongoid/serialization.rb +21 -0
  198. data/lib/mongoid/state.rb +34 -0
  199. data/lib/mongoid/threaded.rb +175 -0
  200. data/lib/mongoid/timestamps/updated.rb +1 -1
  201. data/lib/mongoid/validations.rb +3 -7
  202. data/lib/mongoid/version.rb +1 -1
  203. data/lib/mongoid/versioning.rb +61 -7
  204. data/lib/rack/mongoid.rb +2 -0
  205. data/lib/rack/mongoid/middleware/identity_map.rb +38 -0
  206. data/lib/rails/generators/mongoid/model/model_generator.rb +1 -1
  207. data/lib/rails/generators/mongoid/model/templates/{model.rb → model.rb.tt} +0 -0
  208. data/lib/rails/generators/mongoid/observer/observer_generator.rb +1 -1
  209. data/lib/rails/generators/mongoid/observer/templates/{observer.rb → observer.rb.tt} +0 -0
  210. data/lib/rails/mongoid.rb +17 -17
  211. metadata +136 -102
  212. data/lib/mongoid/atomicity.rb +0 -111
  213. data/lib/mongoid/collections/cyclic_iterator.rb +0 -34
  214. data/lib/mongoid/collections/slaves.rb +0 -61
  215. data/lib/mongoid/extensions/array/conversions.rb +0 -23
  216. data/lib/mongoid/extensions/array/parentization.rb +0 -13
  217. data/lib/mongoid/extensions/big_decimal/conversions.rb +0 -19
  218. data/lib/mongoid/extensions/binary/conversions.rb +0 -17
  219. data/lib/mongoid/extensions/boolean/conversions.rb +0 -27
  220. data/lib/mongoid/extensions/date/conversions.rb +0 -25
  221. data/lib/mongoid/extensions/datetime/conversions.rb +0 -12
  222. data/lib/mongoid/extensions/float/conversions.rb +0 -20
  223. data/lib/mongoid/extensions/hash/conversions.rb +0 -19
  224. data/lib/mongoid/extensions/integer/conversions.rb +0 -20
  225. data/lib/mongoid/extensions/object/conversions.rb +0 -25
  226. data/lib/mongoid/extensions/range/conversions.rb +0 -25
  227. data/lib/mongoid/extensions/set/conversions.rb +0 -20
  228. data/lib/mongoid/extensions/symbol/conversions.rb +0 -21
  229. data/lib/mongoid/extensions/time_conversions.rb +0 -38
  230. data/lib/mongoid/field.rb +0 -162
  231. data/lib/mongoid/paths.rb +0 -61
  232. data/lib/mongoid/persistence/command.rb +0 -71
  233. data/lib/mongoid/persistence/insert.rb +0 -53
  234. data/lib/mongoid/persistence/insert_embedded.rb +0 -43
  235. data/lib/mongoid/persistence/remove.rb +0 -44
  236. data/lib/mongoid/persistence/remove_all.rb +0 -40
  237. data/lib/mongoid/persistence/remove_embedded.rb +0 -48
  238. data/lib/mongoid/persistence/update.rb +0 -77
  239. data/lib/mongoid/safe.rb +0 -23
  240. data/lib/mongoid/validations/referenced.rb +0 -58
@@ -100,7 +100,7 @@ module Mongoid #:nodoc:
100
100
  #
101
101
  # @since 1.0.0
102
102
  def typecast_value_for(field, value)
103
- return field.set(value) if field.type === value
103
+ return field.serialize(value) if field.type === value
104
104
  case value
105
105
  when Hash
106
106
  value = value.dup
@@ -112,7 +112,11 @@ module Mongoid #:nodoc:
112
112
  when Regexp
113
113
  value
114
114
  else
115
- field.type == Array ? value.class.set(value) : field.set(value)
115
+ if field.type == Array
116
+ Serialization.mongoize(value, value.class)
117
+ else
118
+ field.serialize(value)
119
+ end
116
120
  end
117
121
  end
118
122
 
@@ -131,9 +135,9 @@ module Mongoid #:nodoc:
131
135
  def typecast_hash_value(field, key, value)
132
136
  case key
133
137
  when "$exists"
134
- Boolean.set(value)
138
+ Serialization.mongoize(value, Boolean)
135
139
  when "$size"
136
- Integer.set(value)
140
+ Serialization.mongoize(value, Integer)
137
141
  else
138
142
  typecast_value_for(field, value)
139
143
  end
@@ -1,8 +1,11 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc
3
+
4
+ # Mongoid wrapper of the Ruby Driver cursor.
3
5
  class Cursor
4
6
  include Mongoid::Collections::Retry
5
7
  include Enumerable
8
+
6
9
  # Operations on the Mongo::Cursor object that will not get overriden by the
7
10
  # Mongoid::Cursor are defined here.
8
11
  OPERATIONS = [
@@ -24,42 +27,37 @@ module Mongoid #:nodoc
24
27
  :timeout
25
28
  ]
26
29
 
27
- attr_reader :collection
30
+ attr_reader :collection, :cursor, :klass
28
31
 
29
32
  # The operations above will all delegate to the proxied Mongo::Cursor.
30
- #
31
- # Example:
32
- #
33
- # <tt>cursor.close</tt>
34
33
  OPERATIONS.each do |name|
35
- define_method(name) do |*args|
36
- retry_on_connection_failure do
37
- @cursor.send(name, *args)
34
+ class_eval <<-EOS, __FILE__, __LINE__
35
+ def #{name}(*args)
36
+ retry_on_connection_failure do
37
+ cursor.#{name}(*args)
38
+ end
38
39
  end
39
- end
40
+ EOS
40
41
  end
41
42
 
42
43
  # Iterate over each document in the cursor and yield to it.
43
44
  #
44
- # Example:
45
- #
46
- # <tt>cursor.each { |doc| p doc.title }</tt>
45
+ # @example Iterate over the cursor.
46
+ # cursor.each { |doc| p doc.title }
47
47
  def each
48
- @cursor.each do |document|
49
- yield Mongoid::Factory.from_db(@klass, document)
48
+ cursor.each do |document|
49
+ yield Mongoid::Factory.from_db(klass, document)
50
50
  end
51
51
  end
52
52
 
53
53
  # Create the new +Mongoid::Cursor+.
54
54
  #
55
- # Options:
56
- #
57
- # collection: The Mongoid::Collection instance.
58
- # cursor: The Mongo::Cursor to be proxied.
59
- #
60
- # Example:
55
+ # @example Instantiate the cursor.
56
+ # Mongoid::Cursor.new(Person, cursor)
61
57
  #
62
- # <tt>Mongoid::Cursor.new(Person, cursor)</tt>
58
+ # @param [ Class ] klass The class associated with the cursor.
59
+ # @param [ Collection ] collection The Mongoid::Collection instance.
60
+ # @param [ Mongo::Cursor ] cursor The Mongo::Cursor to be proxied.
63
61
  def initialize(klass, collection, cursor)
64
62
  @klass, @collection, @cursor = klass, collection, cursor
65
63
  end
@@ -67,20 +65,22 @@ module Mongoid #:nodoc
67
65
  # Return the next document in the cursor. Will instantiate a new Mongoid
68
66
  # document with the attributes.
69
67
  #
70
- # Example:
68
+ # @example Get the next document.
69
+ # cursor.next_document
71
70
  #
72
- # <tt>cursor.next_document</tt>
71
+ # @return [ Document ] The next document in the cursor.
73
72
  def next_document
74
- Mongoid::Factory.from_db(@klass, @cursor.next_document)
73
+ Mongoid::Factory.from_db(klass, cursor.next_document)
75
74
  end
76
75
 
77
76
  # Returns an array of all the documents in the cursor.
78
77
  #
79
- # Example:
78
+ # @example Get the cursor as an array.
79
+ # cursor.to_a
80
80
  #
81
- # <tt>cursor.to_a</tt>
81
+ # @return [ Array<Document> ] An array of documents.
82
82
  def to_a
83
- @cursor.to_a.collect { |attrs| Mongoid::Factory.from_db(@klass, attrs) }
83
+ cursor.to_a.collect { |attrs| Mongoid::Factory.from_db(klass, attrs) }
84
84
  end
85
85
  end
86
86
  end
@@ -2,252 +2,92 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Dirty #:nodoc:
4
4
  extend ActiveSupport::Concern
5
+ include ActiveModel::Dirty
5
6
 
6
- # Gets the changes for a specific field.
7
+ # Get the changed values for the document. This is a hash with the name of
8
+ # the field as the keys, and the values being an array of previous and
9
+ # current pairs.
7
10
  #
8
- # Example:
11
+ # @example Get the changes.
12
+ # document.changes
9
13
  #
10
- # person = Person.new(:title => "Sir")
11
- # person.title = "Madam"
12
- # person.attribute_change("title") # [ "Sir", "Madam" ]
13
- #
14
- # Returns:
15
- #
16
- # An +Array+ containing the old and new values.
17
- def attribute_change(name)
18
- modifications[name]
19
- end
20
-
21
- # Determines if a specific field has chaged.
22
- #
23
- # Example:
24
- #
25
- # person = Person.new(:title => "Sir")
26
- # person.title = "Madam"
27
- # person.attribute_changed?("title") # true
28
- #
29
- # Returns:
30
- #
31
- # +true+ if changed, +false+ if not.
32
- def attribute_changed?(name)
33
- modifications.include?(name)
34
- end
35
-
36
- # Gets the old value for a specific field.
37
- #
38
- # Example:
39
- #
40
- # person = Person.new(:title => "Sir")
41
- # person.title = "Madam"
42
- # person.attribute_was("title") # "Sir"
43
- #
44
- # Returns:
45
- #
46
- # The old field value.
47
- def attribute_was(name)
48
- change = modifications[name]
49
- change ? change[0] : attributes[name]
50
- end
51
-
52
- # Gets the names of all the fields that have changed in the document.
14
+ # @note This is overriding the AM::Dirty implementation to handle
15
+ # enumerable fields being in the hash when not actually changed.
53
16
  #
54
- # Example:
17
+ # @return [ Hash ] The changed values.
55
18
  #
56
- # person = Person.new(:title => "Sir")
57
- # person.title = "Madam"
58
- # person.changed # returns [ "title" ]
59
- #
60
- # Returns:
61
- #
62
- # An +Array+ of changed field names.
63
- def changed
64
- modifications.keys
19
+ # @since 2.1.0
20
+ def changes
21
+ {}.tap do |hash|
22
+ changed.each do |name|
23
+ change = attribute_change(name)
24
+ hash[name] = change if change[0] != change[1]
25
+ end
26
+ end
65
27
  end
66
28
 
67
- # Alerts to whether the document has been modified or not.
29
+ # Call this method after save, so the changes can be properly switched.
68
30
  #
69
- # Example:
31
+ # This will unset the memoized children array, set new record to
32
+ # false, set the document as validated, and move the dirty changes.
70
33
  #
71
- # person = Person.new(:title => "Sir")
72
- # person.title = "Madam"
73
- # person.changed? # returns true
34
+ # @example Move the changes to previous.
35
+ # person.move_changes
74
36
  #
75
- # Returns:
76
- #
77
- # +true+ if changed, +false+ if not.
78
- def changed?
79
- !modifications.empty?
37
+ # @since 2.1.0
38
+ def move_changes
39
+ @_children = nil
40
+ @previously_changed = changes
41
+ @validated = false
42
+ changed_attributes.clear
80
43
  end
81
44
 
82
- # Gets all the modifications that have happened to the object as a +Hash+
83
- # with the keys being the names of the fields, and the values being an
84
- # +Array+ with the old value and new value.
45
+ # Remove a change from the dirty attributes hash. Used by the single field
46
+ # atomic updators.
85
47
  #
86
- # Example:
48
+ # @example Remove a flagged change.
49
+ # model.remove_change(:field)
87
50
  #
88
- # person = Person.new(:title => "Sir")
89
- # person.title = "Madam"
90
- # person.changes # returns { "title" => [ "Sir", "Madam" ] }
51
+ # @param [ Symbol, String ] name The name of the field.
91
52
  #
92
- # Returns:
93
- #
94
- # A +Hash+ of changes.
95
- def changes
96
- modifications
97
- end
98
-
99
- # Call this method after save, so the changes can be properly switched.
100
- #
101
- # Example:
102
- #
103
- # <tt>person.move_changes</tt>
104
- def move_changes
105
- @validated = false
106
- @previous_modifications = modifications.dup
107
- @modifications = {}
53
+ # @since 2.1.0
54
+ def remove_change(name)
55
+ changed_attributes.delete(name.to_s)
108
56
  end
109
57
 
110
58
  # Gets all the new values for each of the changed fields, to be passed to
111
59
  # a MongoDB $set modifier.
112
60
  #
113
- # Example:
114
- #
61
+ # @example Get the setters for the atomic updates.
115
62
  # person = Person.new(:title => "Sir")
116
63
  # person.title = "Madam"
117
64
  # person.setters # returns { "title" => "Madam" }
118
65
  #
119
- # Returns:
120
- #
121
- # A +Hash+ of new values.
66
+ # @return [ Hash ] A +Hash+ of atomic setters.
122
67
  def setters
123
- modifications.inject({}) do |sets, (field, changes)|
124
- key = embedded? ? "#{_position}.#{field}" : field
125
- sets[key] = changes[1]; sets
68
+ {}.tap do |modifications|
69
+ changes.each_pair do |field, changes|
70
+ key = embedded? ? "#{atomic_position}.#{field}" : field
71
+ modifications[key] = changes[1]
72
+ end
126
73
  end
127
74
  end
128
75
 
129
- # Gets all the modifications that have happened to the object before the
130
- # object was saved.
131
- #
132
- # Example:
133
- #
134
- # person = Person.new(:title => "Sir")
135
- # person.title = "Madam"
136
- # person.save!
137
- # person.previous_changes # returns { "title" => [ "Sir", "Madam" ] }
138
- #
139
- # Returns:
140
- #
141
- # A +Hash+ of changes before save.
142
- def previous_changes
143
- @previous_modifications
144
- end
145
-
146
- # Resets a changed field back to its old value.
147
- #
148
- # Example:
149
- #
150
- # person = Person.new(:title => "Sir")
151
- # person.title = "Madam"
152
- # person.reset_attribute!("title")
153
- # person.title # "Sir"
154
- #
155
- # Returns:
156
- #
157
- # The old field value.
158
- def reset_attribute!(name)
159
- value = attribute_was(name)
160
- value ? attributes[name] = value : attributes.delete(name)
161
- modifications.delete(name)
162
- end
163
-
164
- # Sets up the modifications hash. This occurs just after the document is
165
- # instantiated.
166
- #
167
- # Example:
168
- #
169
- # <tt>document.setup_notifications</tt>
170
- def setup_modifications
171
- @accessed ||= {}
172
- @modifications ||= {}
173
- @previous_modifications ||= {}
174
- end
175
-
176
- # Reset all modifications for the document. This will wipe all the marked
177
- # changes, but not reset the values.
178
- #
179
- # Example:
180
- #
181
- # <tt>document.reset_modifications</tt>
182
- def reset_modifications
183
- @accessed = {}
184
- @modifications = {}
185
- end
186
-
187
- protected
188
-
189
- # Audit the original value for a field that can be modified in place.
190
- #
191
- # Example:
192
- #
193
- # <tt>person.accessed("aliases", [ "007" ])</tt>
194
- def accessed(name, value)
195
- return value unless value.is_a?(Enumerable)
196
- @accessed ||= {}
197
- @accessed[name] = value.dup unless @accessed.has_key?(name)
198
- value
199
- end
76
+ private
200
77
 
201
- # Get all normal modifications plus in place potential changes.
78
+ # Get the current value for the specified attribute, if the attribute has changed.
202
79
  #
203
- # Example:
80
+ # @note This is overriding the AM::Dirty implementation to read from the mongoid
81
+ # attributes hash, which may contain a serialized version of the attributes data. It is
82
+ # necessary to read the serialized version as the changed value, to allow updates to
83
+ # the MongoDB document to persist correctly. For example, if a DateTime field is updated
84
+ # it must be persisted as a UTC Time.
204
85
  #
205
- # <tt>person.modifications</tt>
86
+ # @return [ Object ] The current value of the field, or nil if no change made.
206
87
  #
207
- # Returns:
208
- #
209
- # All changes to the document.
210
- def modifications
211
- reset_modifications unless @modifications && @accessed
212
- @accessed.each_pair do |field, value|
213
- current = attributes[field]
214
- @modifications[field] = [ value, current ] if current != value
215
- end
216
- @accessed.clear
217
- @modifications
218
- end
219
-
220
- # Audit the change of a field's value.
221
- #
222
- # Example:
223
- #
224
- # <tt>person.modify("name", "Jack", "John")</tt>
225
- def modify(name, old_value, new_value)
226
- attributes[name] = new_value
227
- if @modifications && (old_value != new_value)
228
- original = @modifications[name].first if @modifications[name]
229
- @modifications[name] = [ (original || old_value), new_value ]
230
- end
231
- end
232
-
233
- module ClassMethods #:nodoc:
234
- # Add the dynamic dirty methods. These are custom methods defined on a
235
- # field by field basis that wrap the dirty attribute methods.
236
- #
237
- # Example:
238
- #
239
- # person = Person.new(:title => "Sir")
240
- # person.title = "Madam"
241
- # person.title_change # [ "Sir", "Madam" ]
242
- # person.title_changed? # true
243
- # person.title_was # "Sir"
244
- # person.reset_title!
245
- def add_dirty_methods(name)
246
- define_method("#{name}_change") { attribute_change(name) } unless instance_methods.include?("#{name}_change") || instance_methods.include?(:"#{name}_change")
247
- define_method("#{name}_changed?") { attribute_changed?(name) } unless instance_methods.include?("#{name}_changed?") || instance_methods.include?(:"#{name}_changed?")
248
- define_method("#{name}_was") { attribute_was(name) } unless instance_methods.include?("#{name}_was") || instance_methods.include?(:"#{name}_was")
249
- define_method("reset_#{name}!") { reset_attribute!(name) } unless instance_methods.include?("reset_#{name}!") || instance_methods.include?(:"reset_#{name}!")
250
- end
88
+ # @since 2.1.0
89
+ def attribute_change(attr)
90
+ [changed_attributes[attr], attributes[attr]] if attribute_changed?(attr)
251
91
  end
252
92
  end
253
93
  end
@@ -6,7 +6,6 @@ module Mongoid #:nodoc:
6
6
  module Document
7
7
  extend ActiveSupport::Concern
8
8
  include Mongoid::Components
9
- include Mongoid::MultiDatabase
10
9
 
11
10
  included do
12
11
  attr_reader :new_record
@@ -125,13 +124,15 @@ module Mongoid #:nodoc:
125
124
  #
126
125
  # @return [ Document ] A new document.
127
126
  def initialize(attrs = nil)
128
- @new_record = true
129
- @attributes = apply_default_attributes
130
- process(attrs) do |document|
131
- yield self if block_given?
132
- identify
127
+ building do
128
+ @new_record = true
129
+ @attributes = apply_default_attributes
130
+ process(attrs) do
131
+ yield self if block_given?
132
+ identify
133
+ end
134
+ run_callbacks(:initialize) { self }
133
135
  end
134
- run_callbacks(:initialize) { self }
135
136
  end
136
137
 
137
138
  # Reloads the +Document+ attributes from the database. If the document has
@@ -150,8 +151,8 @@ module Mongoid #:nodoc:
150
151
  raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
151
152
  end
152
153
  @attributes = {}.merge(reloaded || {})
154
+ changed_attributes.clear
153
155
  apply_default_attributes
154
- reset_modifications
155
156
  tap do
156
157
  relations.keys.each do |name|
157
158
  if instance_variable_defined?("@#{name}")
@@ -161,24 +162,6 @@ module Mongoid #:nodoc:
161
162
  end
162
163
  end
163
164
 
164
- # Remove a child document from this parent. If an embeds one then set to
165
- # nil, otherwise remove from the embeds many.
166
- #
167
- # This is called from the +RemoveEmbedded+ persistence command.
168
- #
169
- # @example Remove the child.
170
- # document.remove_child(child)
171
- #
172
- # @param [ Document ] child The child (embedded) document to remove.
173
- def remove_child(child)
174
- name = child.metadata.name
175
- if child.embedded_one?
176
- remove_instance_variable("@#{name}") if instance_variable_defined?("@#{name}")
177
- else
178
- send(name).delete(child)
179
- end
180
- end
181
-
182
165
  # Return an array with this +Document+ only in it.
183
166
  #
184
167
  # @example Return the document in an array.
@@ -198,11 +181,12 @@ module Mongoid #:nodoc:
198
181
  #
199
182
  # @return [ Hash ] A hash of all attributes in the hierarchy.
200
183
  def as_document
201
- attribs = attributes
202
- attribs.tap do |attrs|
203
- relations.select { |name, meta| meta.embedded? }.each do |name, meta|
204
- relation = send(name, false, :continue => false)
205
- attrs[name] = relation.as_document unless relation.blank?
184
+ attributes.tap do |attrs|
185
+ relations.each_pair do |name, meta|
186
+ if meta.embedded?
187
+ relation = send(name, false, :continue => false)
188
+ attrs[name] = relation.as_document unless relation.blank?
189
+ end
206
190
  end
207
191
  end
208
192
  end
@@ -222,12 +206,26 @@ module Mongoid #:nodoc:
222
206
  unless klass.include?(Mongoid::Document)
223
207
  raise ArgumentError, 'A class which includes Mongoid::Document is expected'
224
208
  end
225
- became = klass.new
226
- became.instance_variable_set('@attributes', @attributes)
227
- became.instance_variable_set('@errors', @errors)
228
- became.instance_variable_set('@new_record', new_record?)
229
- became.instance_variable_set('@destroyed', destroyed?)
230
- became
209
+ klass.new.tap do |became|
210
+ became.instance_variable_set('@attributes', @attributes)
211
+ became.instance_variable_set('@errors', @errors)
212
+ became.instance_variable_set('@new_record', new_record?)
213
+ became.instance_variable_set('@destroyed', destroyed?)
214
+ end
215
+ end
216
+
217
+ private
218
+
219
+ # Implement this for calls to flatten on array.
220
+ #
221
+ # @example Get the document as an array.
222
+ # document.to_ary
223
+ #
224
+ # @return [ nil ] Always nil.
225
+ #
226
+ # @since 2.1.0
227
+ def to_ary
228
+ nil
231
229
  end
232
230
 
233
231
  module ClassMethods #:nodoc:
@@ -260,7 +258,7 @@ module Mongoid #:nodoc:
260
258
  allocate.tap do |doc|
261
259
  doc.instance_variable_set(:@attributes, attributes)
262
260
  doc.send(:apply_default_attributes)
263
- doc.setup_modifications
261
+ IdentityMap.set(doc)
264
262
  doc.run_callbacks(:initialize) { doc }
265
263
  end
266
264
  end
@@ -272,7 +270,7 @@ module Mongoid #:nodoc:
272
270
  #
273
271
  # @return [ Array<Class> ] All subclasses of the current document.
274
272
  def _types
275
- @_type ||= [descendants + [self]].flatten.uniq.map(&:to_s)
273
+ @_type ||= [descendants + [self]].flatten.uniq.map { |t| t.to_s }
276
274
  end
277
275
 
278
276
  # Set the i18n scope to overwrite ActiveModel.