mongoid-braxton 2.0.2

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 (226) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +50 -0
  3. data/Rakefile +51 -0
  4. data/lib/config/locales/bg.yml +41 -0
  5. data/lib/config/locales/de.yml +41 -0
  6. data/lib/config/locales/en.yml +45 -0
  7. data/lib/config/locales/es.yml +41 -0
  8. data/lib/config/locales/fr.yml +42 -0
  9. data/lib/config/locales/hu.yml +44 -0
  10. data/lib/config/locales/id.yml +46 -0
  11. data/lib/config/locales/it.yml +39 -0
  12. data/lib/config/locales/ja.yml +40 -0
  13. data/lib/config/locales/kr.yml +65 -0
  14. data/lib/config/locales/nl.yml +39 -0
  15. data/lib/config/locales/pl.yml +39 -0
  16. data/lib/config/locales/pt-BR.yml +40 -0
  17. data/lib/config/locales/pt.yml +40 -0
  18. data/lib/config/locales/ro.yml +46 -0
  19. data/lib/config/locales/ru.yml +41 -0
  20. data/lib/config/locales/sv.yml +40 -0
  21. data/lib/config/locales/vi.yml +45 -0
  22. data/lib/config/locales/zh-CN.yml +33 -0
  23. data/lib/mongoid.rb +140 -0
  24. data/lib/mongoid/atomicity.rb +111 -0
  25. data/lib/mongoid/attributes.rb +185 -0
  26. data/lib/mongoid/attributes/processing.rb +145 -0
  27. data/lib/mongoid/callbacks.rb +23 -0
  28. data/lib/mongoid/collection.rb +137 -0
  29. data/lib/mongoid/collections.rb +71 -0
  30. data/lib/mongoid/collections/master.rb +37 -0
  31. data/lib/mongoid/collections/operations.rb +42 -0
  32. data/lib/mongoid/collections/retry.rb +39 -0
  33. data/lib/mongoid/components.rb +45 -0
  34. data/lib/mongoid/config.rb +349 -0
  35. data/lib/mongoid/config/database.rb +167 -0
  36. data/lib/mongoid/config/replset_database.rb +78 -0
  37. data/lib/mongoid/contexts.rb +19 -0
  38. data/lib/mongoid/contexts/enumerable.rb +275 -0
  39. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  40. data/lib/mongoid/contexts/mongo.rb +345 -0
  41. data/lib/mongoid/copyable.rb +46 -0
  42. data/lib/mongoid/criteria.rb +357 -0
  43. data/lib/mongoid/criterion/builder.rb +34 -0
  44. data/lib/mongoid/criterion/complex.rb +34 -0
  45. data/lib/mongoid/criterion/creational.rb +34 -0
  46. data/lib/mongoid/criterion/exclusion.rb +108 -0
  47. data/lib/mongoid/criterion/inclusion.rb +198 -0
  48. data/lib/mongoid/criterion/inspection.rb +22 -0
  49. data/lib/mongoid/criterion/optional.rb +193 -0
  50. data/lib/mongoid/criterion/selector.rb +143 -0
  51. data/lib/mongoid/criterion/unconvertable.rb +20 -0
  52. data/lib/mongoid/cursor.rb +86 -0
  53. data/lib/mongoid/default_scope.rb +36 -0
  54. data/lib/mongoid/dirty.rb +253 -0
  55. data/lib/mongoid/document.rb +284 -0
  56. data/lib/mongoid/errors.rb +13 -0
  57. data/lib/mongoid/errors/document_not_found.rb +29 -0
  58. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  59. data/lib/mongoid/errors/invalid_database.rb +20 -0
  60. data/lib/mongoid/errors/invalid_field.rb +19 -0
  61. data/lib/mongoid/errors/invalid_options.rb +16 -0
  62. data/lib/mongoid/errors/invalid_type.rb +26 -0
  63. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  64. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  65. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  66. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  67. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  68. data/lib/mongoid/errors/validations.rb +24 -0
  69. data/lib/mongoid/extensions.rb +123 -0
  70. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  71. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  72. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  73. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  74. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  75. data/lib/mongoid/extensions/date/conversions.rb +25 -0
  76. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  77. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  78. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  79. data/lib/mongoid/extensions/hash/conversions.rb +19 -0
  80. data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
  81. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  82. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  83. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  84. data/lib/mongoid/extensions/object/checks.rb +32 -0
  85. data/lib/mongoid/extensions/object/conversions.rb +25 -0
  86. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  87. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  88. data/lib/mongoid/extensions/object_id/conversions.rb +96 -0
  89. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  90. data/lib/mongoid/extensions/range/conversions.rb +25 -0
  91. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  92. data/lib/mongoid/extensions/string/conversions.rb +34 -0
  93. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  94. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  95. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  96. data/lib/mongoid/extensions/time_conversions.rb +38 -0
  97. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  98. data/lib/mongoid/extras.rb +42 -0
  99. data/lib/mongoid/factory.rb +37 -0
  100. data/lib/mongoid/field.rb +162 -0
  101. data/lib/mongoid/fields.rb +183 -0
  102. data/lib/mongoid/finders.rb +127 -0
  103. data/lib/mongoid/hierarchy.rb +85 -0
  104. data/lib/mongoid/identity.rb +92 -0
  105. data/lib/mongoid/indexes.rb +38 -0
  106. data/lib/mongoid/inspection.rb +54 -0
  107. data/lib/mongoid/javascript.rb +21 -0
  108. data/lib/mongoid/javascript/functions.yml +37 -0
  109. data/lib/mongoid/json.rb +16 -0
  110. data/lib/mongoid/keys.rb +131 -0
  111. data/lib/mongoid/logger.rb +18 -0
  112. data/lib/mongoid/matchers.rb +32 -0
  113. data/lib/mongoid/matchers/all.rb +11 -0
  114. data/lib/mongoid/matchers/default.rb +70 -0
  115. data/lib/mongoid/matchers/exists.rb +13 -0
  116. data/lib/mongoid/matchers/gt.rb +11 -0
  117. data/lib/mongoid/matchers/gte.rb +11 -0
  118. data/lib/mongoid/matchers/in.rb +11 -0
  119. data/lib/mongoid/matchers/lt.rb +11 -0
  120. data/lib/mongoid/matchers/lte.rb +11 -0
  121. data/lib/mongoid/matchers/ne.rb +11 -0
  122. data/lib/mongoid/matchers/nin.rb +11 -0
  123. data/lib/mongoid/matchers/or.rb +30 -0
  124. data/lib/mongoid/matchers/size.rb +11 -0
  125. data/lib/mongoid/matchers/strategies.rb +63 -0
  126. data/lib/mongoid/multi_database.rb +11 -0
  127. data/lib/mongoid/multi_parameter_attributes.rb +82 -0
  128. data/lib/mongoid/named_scope.rb +137 -0
  129. data/lib/mongoid/nested_attributes.rb +51 -0
  130. data/lib/mongoid/observer.rb +67 -0
  131. data/lib/mongoid/paranoia.rb +103 -0
  132. data/lib/mongoid/paths.rb +61 -0
  133. data/lib/mongoid/persistence.rb +240 -0
  134. data/lib/mongoid/persistence/atomic.rb +88 -0
  135. data/lib/mongoid/persistence/atomic/add_to_set.rb +32 -0
  136. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  137. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  138. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  139. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  140. data/lib/mongoid/persistence/command.rb +71 -0
  141. data/lib/mongoid/persistence/insert.rb +53 -0
  142. data/lib/mongoid/persistence/insert_embedded.rb +43 -0
  143. data/lib/mongoid/persistence/remove.rb +44 -0
  144. data/lib/mongoid/persistence/remove_all.rb +40 -0
  145. data/lib/mongoid/persistence/remove_embedded.rb +48 -0
  146. data/lib/mongoid/persistence/update.rb +77 -0
  147. data/lib/mongoid/railtie.rb +139 -0
  148. data/lib/mongoid/railties/database.rake +171 -0
  149. data/lib/mongoid/railties/document.rb +12 -0
  150. data/lib/mongoid/relations.rb +107 -0
  151. data/lib/mongoid/relations/accessors.rb +175 -0
  152. data/lib/mongoid/relations/auto_save.rb +34 -0
  153. data/lib/mongoid/relations/binding.rb +26 -0
  154. data/lib/mongoid/relations/bindings.rb +9 -0
  155. data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
  156. data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
  157. data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
  158. data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
  159. data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
  160. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +103 -0
  161. data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
  162. data/lib/mongoid/relations/builder.rb +42 -0
  163. data/lib/mongoid/relations/builders.rb +79 -0
  164. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  165. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  166. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  167. data/lib/mongoid/relations/builders/nested_attributes/many.rb +126 -0
  168. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  169. data/lib/mongoid/relations/builders/referenced/in.rb +29 -0
  170. data/lib/mongoid/relations/builders/referenced/many.rb +47 -0
  171. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  172. data/lib/mongoid/relations/builders/referenced/one.rb +27 -0
  173. data/lib/mongoid/relations/cascading.rb +55 -0
  174. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  175. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  176. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  177. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  178. data/lib/mongoid/relations/constraint.rb +42 -0
  179. data/lib/mongoid/relations/cyclic.rb +103 -0
  180. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  181. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  182. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  183. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  184. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  185. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  186. data/lib/mongoid/relations/embedded/in.rb +173 -0
  187. data/lib/mongoid/relations/embedded/many.rb +499 -0
  188. data/lib/mongoid/relations/embedded/one.rb +170 -0
  189. data/lib/mongoid/relations/macros.rb +310 -0
  190. data/lib/mongoid/relations/many.rb +215 -0
  191. data/lib/mongoid/relations/metadata.rb +539 -0
  192. data/lib/mongoid/relations/nested_builder.rb +68 -0
  193. data/lib/mongoid/relations/one.rb +47 -0
  194. data/lib/mongoid/relations/polymorphic.rb +54 -0
  195. data/lib/mongoid/relations/proxy.rb +143 -0
  196. data/lib/mongoid/relations/referenced/batch.rb +71 -0
  197. data/lib/mongoid/relations/referenced/batch/insert.rb +57 -0
  198. data/lib/mongoid/relations/referenced/in.rb +216 -0
  199. data/lib/mongoid/relations/referenced/many.rb +516 -0
  200. data/lib/mongoid/relations/referenced/many_to_many.rb +396 -0
  201. data/lib/mongoid/relations/referenced/one.rb +222 -0
  202. data/lib/mongoid/relations/reflections.rb +45 -0
  203. data/lib/mongoid/safe.rb +23 -0
  204. data/lib/mongoid/safety.rb +207 -0
  205. data/lib/mongoid/scope.rb +31 -0
  206. data/lib/mongoid/serialization.rb +99 -0
  207. data/lib/mongoid/sharding.rb +51 -0
  208. data/lib/mongoid/state.rb +67 -0
  209. data/lib/mongoid/timestamps.rb +14 -0
  210. data/lib/mongoid/timestamps/created.rb +31 -0
  211. data/lib/mongoid/timestamps/updated.rb +33 -0
  212. data/lib/mongoid/validations.rb +124 -0
  213. data/lib/mongoid/validations/associated.rb +44 -0
  214. data/lib/mongoid/validations/referenced.rb +58 -0
  215. data/lib/mongoid/validations/uniqueness.rb +85 -0
  216. data/lib/mongoid/version.rb +4 -0
  217. data/lib/mongoid/versioning.rb +113 -0
  218. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  219. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +20 -0
  220. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  221. data/lib/rails/generators/mongoid/model/templates/model.rb +19 -0
  222. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  223. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  224. data/lib/rails/generators/mongoid_generator.rb +70 -0
  225. data/lib/rails/mongoid.rb +58 -0
  226. metadata +406 -0
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+
5
+ # The selector is a hash-like object that has special behaviour for merging
6
+ # mongoid criteria selectors.
7
+ class Selector < Hash
8
+
9
+ attr_reader :fields, :klass
10
+
11
+ # Create the new selector.
12
+ #
13
+ # @example Create the selector.
14
+ # Selector.new(Person)
15
+ #
16
+ # @param [ Class ] klass The class the selector is for.
17
+ #
18
+ # @since 1.0.0
19
+ def initialize(klass)
20
+ @fields, @klass = klass.fields.except("_id", "_type"), klass
21
+ end
22
+
23
+ # Set the value for the supplied key, attempting to typecast the value.
24
+ #
25
+ # @example Set the value for the key.
26
+ # selector["$ne"] = { :name => "Zorg" }
27
+ #
28
+ # @param [ String, Symbol ] key The hash key.
29
+ # @param [ Object ] value The value to set.
30
+ #
31
+ # @since 2.0.0
32
+ def []=(key, value)
33
+ super(key, try_to_typecast(key, value))
34
+ end
35
+
36
+ # Merge the selector with another hash.
37
+ #
38
+ # @example Merge the objects.
39
+ # selector.merge!({ :key => "value" })
40
+ #
41
+ # @param [ Hash, Selector ] other The object to merge with.
42
+ #
43
+ # @return [ Selector ] The merged selector.
44
+ #
45
+ # @since 1.0.0
46
+ def merge!(other)
47
+ tap do |selector|
48
+ other.each_pair do |key, value|
49
+ selector[key] = value
50
+ end
51
+ end
52
+ end
53
+ alias :update :merge!
54
+
55
+ if RUBY_VERSION < '1.9'
56
+
57
+ # Generate pretty inspection for old ruby versions.
58
+ #
59
+ # @example Inspect the selector.
60
+ # selector.inspect
61
+ #
62
+ # @return [ String ] The inspected selector.
63
+ def inspect
64
+ ret = self.keys.inject([]) do |ret, key|
65
+ ret << "#{key.inspect}=>#{self[key].inspect}"
66
+ end
67
+ "{#{ret.sort.join(', ')}}"
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ # If the key is defined as a field, then attempt to typecast it.
74
+ #
75
+ # @example Try to cast.
76
+ # selector.try_to_typecast(:id, 1)
77
+ #
78
+ # @param [ String, Symbol ] key The field name.
79
+ # @param [ Object ] value The value.
80
+ #
81
+ # @return [ Object ] The typecasted value.
82
+ #
83
+ # @since 1.0.0
84
+ def try_to_typecast(key, value)
85
+ access = key.to_s
86
+ return value unless fields.has_key?(access)
87
+ field = fields[access]
88
+ typecast_value_for(field, value)
89
+ end
90
+
91
+ # Get the typecast value for the defined field.
92
+ #
93
+ # @example Get the typecast value.
94
+ # selector.typecast_value_for(:name, "Corbin")
95
+ #
96
+ # @param [ Field ] field The defined field.
97
+ # @param [ Object ] value The value to cast.
98
+ #
99
+ # @return [ Object ] The cast value.
100
+ #
101
+ # @since 1.0.0
102
+ def typecast_value_for(field, value)
103
+ return field.set(value) if field.type === value
104
+ case value
105
+ when Hash
106
+ value = value.dup
107
+ value.each_pair do |k, v|
108
+ value[k] = typecast_hash_value(field, k, v)
109
+ end
110
+ when Array
111
+ value.map { |v| typecast_value_for(field, v) }
112
+ when Regexp
113
+ value
114
+ else
115
+ field.type == Array ? value.class.set(value) : field.set(value)
116
+ end
117
+ end
118
+
119
+ # Typecast the value for booleans and integers in hashes.
120
+ #
121
+ # @example Typecast the hash values.
122
+ # selector.typecast_hash_value(field, "$exists", "true")
123
+ #
124
+ # @param [ Field ] field The defined field.
125
+ # @param [ String ] key The modifier key.
126
+ # @param [ Object ] value The value to cast.
127
+ #
128
+ # @return [ Object ] The cast value.
129
+ #
130
+ # @since 1.0.0
131
+ def typecast_hash_value(field, key, value)
132
+ case key
133
+ when "$exists"
134
+ Boolean.set(value)
135
+ when "$size"
136
+ Integer.set(value)
137
+ else
138
+ typecast_value_for(field, value)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+
5
+ # Wrapper class for strings that should not be converted into
6
+ # BSON::ObjectIds.
7
+ class Unconvertable < String
8
+
9
+ # Initialize just like a normal string, and quack like it to.
10
+ #
11
+ # @example Create the new Unconvertable.
12
+ # Unconvertable.new("testing")
13
+ #
14
+ # @param [ String ] value The string.
15
+ #
16
+ # @since 2.0.2
17
+ def initialize(value); super; end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ class Cursor
4
+ include Mongoid::Collections::Retry
5
+ include Enumerable
6
+ # Operations on the Mongo::Cursor object that will not get overriden by the
7
+ # Mongoid::Cursor are defined here.
8
+ OPERATIONS = [
9
+ :close,
10
+ :closed?,
11
+ :count,
12
+ :explain,
13
+ :fields,
14
+ :full_collection_name,
15
+ :hint,
16
+ :limit,
17
+ :order,
18
+ :query_options_hash,
19
+ :query_opts,
20
+ :selector,
21
+ :skip,
22
+ :snapshot,
23
+ :sort,
24
+ :timeout
25
+ ]
26
+
27
+ attr_reader :collection
28
+
29
+ # The operations above will all delegate to the proxied Mongo::Cursor.
30
+ #
31
+ # Example:
32
+ #
33
+ # <tt>cursor.close</tt>
34
+ OPERATIONS.each do |name|
35
+ define_method(name) do |*args|
36
+ retry_on_connection_failure do
37
+ @cursor.send(name, *args)
38
+ end
39
+ end
40
+ end
41
+
42
+ # Iterate over each document in the cursor and yield to it.
43
+ #
44
+ # Example:
45
+ #
46
+ # <tt>cursor.each { |doc| p doc.title }</tt>
47
+ def each
48
+ @cursor.each do |document|
49
+ yield Mongoid::Factory.from_db(@klass, document)
50
+ end
51
+ end
52
+
53
+ # Create the new +Mongoid::Cursor+.
54
+ #
55
+ # Options:
56
+ #
57
+ # collection: The Mongoid::Collection instance.
58
+ # cursor: The Mongo::Cursor to be proxied.
59
+ #
60
+ # Example:
61
+ #
62
+ # <tt>Mongoid::Cursor.new(Person, cursor)</tt>
63
+ def initialize(klass, collection, cursor)
64
+ @klass, @collection, @cursor = klass, collection, cursor
65
+ end
66
+
67
+ # Return the next document in the cursor. Will instantiate a new Mongoid
68
+ # document with the attributes.
69
+ #
70
+ # Example:
71
+ #
72
+ # <tt>cursor.next_document</tt>
73
+ def next_document
74
+ Mongoid::Factory.from_db(@klass, @cursor.next_document)
75
+ end
76
+
77
+ # Returns an array of all the documents in the cursor.
78
+ #
79
+ # Example:
80
+ #
81
+ # <tt>cursor.to_a</tt>
82
+ def to_a
83
+ @cursor.to_a.collect { |attrs| Mongoid::Factory.from_db(@klass, attrs) }
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+
4
+ # This module handles functionality for creating default scopes.
5
+ module DefaultScope
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :default_scoping
10
+ end
11
+
12
+ module ClassMethods #:nodoc:
13
+
14
+ # Creates a default_scope for the +Document+, similar to ActiveRecord's
15
+ # default_scope. +DefaultScopes+ are proxied +Criteria+ objects that are
16
+ # applied by default to all queries for the class.
17
+ #
18
+ # @example Create a default scope.
19
+ #
20
+ # class Person
21
+ # include Mongoid::Document
22
+ # field :active, :type => Boolean
23
+ # field :count, :type => Integer
24
+ #
25
+ # default_scope :where => { :active => true }
26
+ # end
27
+ #
28
+ # @param [ Hash ] conditions The conditions to create with.
29
+ #
30
+ # @since 2.0.0.rc.1
31
+ def default_scope(conditions = {})
32
+ self.default_scoping = Scope.new(conditions).conditions.scoped
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,253 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Dirty #:nodoc:
4
+ extend ActiveSupport::Concern
5
+
6
+ # Gets the changes for a specific field.
7
+ #
8
+ # Example:
9
+ #
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.
53
+ #
54
+ # Example:
55
+ #
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
65
+ end
66
+
67
+ # Alerts to whether the document has been modified or not.
68
+ #
69
+ # Example:
70
+ #
71
+ # person = Person.new(:title => "Sir")
72
+ # person.title = "Madam"
73
+ # person.changed? # returns true
74
+ #
75
+ # Returns:
76
+ #
77
+ # +true+ if changed, +false+ if not.
78
+ def changed?
79
+ !modifications.empty?
80
+ end
81
+
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.
85
+ #
86
+ # Example:
87
+ #
88
+ # person = Person.new(:title => "Sir")
89
+ # person.title = "Madam"
90
+ # person.changes # returns { "title" => [ "Sir", "Madam" ] }
91
+ #
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 = {}
108
+ end
109
+
110
+ # Gets all the new values for each of the changed fields, to be passed to
111
+ # a MongoDB $set modifier.
112
+ #
113
+ # Example:
114
+ #
115
+ # person = Person.new(:title => "Sir")
116
+ # person.title = "Madam"
117
+ # person.setters # returns { "title" => "Madam" }
118
+ #
119
+ # Returns:
120
+ #
121
+ # A +Hash+ of new values.
122
+ def setters
123
+ modifications.inject({}) do |sets, (field, changes)|
124
+ key = embedded? ? "#{_position}.#{field}" : field
125
+ sets[key] = changes[1]; sets
126
+ end
127
+ end
128
+
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
200
+
201
+ # Get all normal modifications plus in place potential changes.
202
+ #
203
+ # Example:
204
+ #
205
+ # <tt>person.modifications</tt>
206
+ #
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
251
+ end
252
+ end
253
+ end