stonegao-mongoid 2.0.0.rc.6

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 (199) 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 +44 -0
  5. data/lib/config/locales/de.yml +44 -0
  6. data/lib/config/locales/en.yml +45 -0
  7. data/lib/config/locales/es.yml +44 -0
  8. data/lib/config/locales/fr.yml +45 -0
  9. data/lib/config/locales/hu.yml +47 -0
  10. data/lib/config/locales/it.yml +42 -0
  11. data/lib/config/locales/kr.yml +68 -0
  12. data/lib/config/locales/nl.yml +42 -0
  13. data/lib/config/locales/pl.yml +42 -0
  14. data/lib/config/locales/pt-br.yml +43 -0
  15. data/lib/config/locales/pt.yml +43 -0
  16. data/lib/config/locales/ro.yml +49 -0
  17. data/lib/config/locales/sv.yml +43 -0
  18. data/lib/config/locales/zh-CN.yml +34 -0
  19. data/lib/mongoid/atomicity.rb +111 -0
  20. data/lib/mongoid/attributes.rb +251 -0
  21. data/lib/mongoid/callbacks.rb +13 -0
  22. data/lib/mongoid/collection.rb +137 -0
  23. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  24. data/lib/mongoid/collections/master.rb +29 -0
  25. data/lib/mongoid/collections/operations.rb +42 -0
  26. data/lib/mongoid/collections/slaves.rb +45 -0
  27. data/lib/mongoid/collections.rb +70 -0
  28. data/lib/mongoid/components.rb +45 -0
  29. data/lib/mongoid/config/database.rb +167 -0
  30. data/lib/mongoid/config/replset_database.rb +48 -0
  31. data/lib/mongoid/config.rb +343 -0
  32. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  33. data/lib/mongoid/contexts/enumerable.rb +226 -0
  34. data/lib/mongoid/contexts/ids.rb +25 -0
  35. data/lib/mongoid/contexts/mongo.rb +345 -0
  36. data/lib/mongoid/contexts/paging.rb +50 -0
  37. data/lib/mongoid/contexts.rb +21 -0
  38. data/lib/mongoid/copyable.rb +44 -0
  39. data/lib/mongoid/criteria.rb +325 -0
  40. data/lib/mongoid/criterion/complex.rb +34 -0
  41. data/lib/mongoid/criterion/creational.rb +34 -0
  42. data/lib/mongoid/criterion/exclusion.rb +67 -0
  43. data/lib/mongoid/criterion/inclusion.rb +134 -0
  44. data/lib/mongoid/criterion/inspection.rb +20 -0
  45. data/lib/mongoid/criterion/optional.rb +213 -0
  46. data/lib/mongoid/criterion/selector.rb +74 -0
  47. data/lib/mongoid/cursor.rb +81 -0
  48. data/lib/mongoid/default_scope.rb +28 -0
  49. data/lib/mongoid/dirty.rb +251 -0
  50. data/lib/mongoid/document.rb +256 -0
  51. data/lib/mongoid/errors/document_not_found.rb +29 -0
  52. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  53. data/lib/mongoid/errors/invalid_database.rb +20 -0
  54. data/lib/mongoid/errors/invalid_field.rb +19 -0
  55. data/lib/mongoid/errors/invalid_options.rb +16 -0
  56. data/lib/mongoid/errors/invalid_type.rb +26 -0
  57. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  58. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  59. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  60. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  61. data/lib/mongoid/errors/validations.rb +24 -0
  62. data/lib/mongoid/errors.rb +12 -0
  63. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  64. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  65. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  66. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  67. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  68. data/lib/mongoid/extensions/date/conversions.rb +25 -0
  69. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  70. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  71. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  72. data/lib/mongoid/extensions/hash/conversions.rb +19 -0
  73. data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
  74. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  75. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  76. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  77. data/lib/mongoid/extensions/object/conversions.rb +25 -0
  78. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  79. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  80. data/lib/mongoid/extensions/object_id/conversions.rb +57 -0
  81. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  82. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  83. data/lib/mongoid/extensions/string/conversions.rb +34 -0
  84. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  85. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  86. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  87. data/lib/mongoid/extensions/time_conversions.rb +38 -0
  88. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  89. data/lib/mongoid/extensions.rb +116 -0
  90. data/lib/mongoid/extras.rb +61 -0
  91. data/lib/mongoid/factory.rb +20 -0
  92. data/lib/mongoid/field.rb +95 -0
  93. data/lib/mongoid/fields.rb +138 -0
  94. data/lib/mongoid/finders.rb +173 -0
  95. data/lib/mongoid/hierarchy.rb +85 -0
  96. data/lib/mongoid/identity.rb +89 -0
  97. data/lib/mongoid/indexes.rb +38 -0
  98. data/lib/mongoid/inspection.rb +58 -0
  99. data/lib/mongoid/javascript/functions.yml +37 -0
  100. data/lib/mongoid/javascript.rb +21 -0
  101. data/lib/mongoid/json.rb +16 -0
  102. data/lib/mongoid/keys.rb +77 -0
  103. data/lib/mongoid/logger.rb +18 -0
  104. data/lib/mongoid/matchers/all.rb +11 -0
  105. data/lib/mongoid/matchers/default.rb +27 -0
  106. data/lib/mongoid/matchers/exists.rb +13 -0
  107. data/lib/mongoid/matchers/gt.rb +11 -0
  108. data/lib/mongoid/matchers/gte.rb +11 -0
  109. data/lib/mongoid/matchers/in.rb +11 -0
  110. data/lib/mongoid/matchers/lt.rb +11 -0
  111. data/lib/mongoid/matchers/lte.rb +11 -0
  112. data/lib/mongoid/matchers/ne.rb +11 -0
  113. data/lib/mongoid/matchers/nin.rb +11 -0
  114. data/lib/mongoid/matchers/size.rb +11 -0
  115. data/lib/mongoid/matchers.rb +55 -0
  116. data/lib/mongoid/modifiers/command.rb +18 -0
  117. data/lib/mongoid/modifiers/inc.rb +24 -0
  118. data/lib/mongoid/modifiers.rb +24 -0
  119. data/lib/mongoid/multi_database.rb +11 -0
  120. data/lib/mongoid/multi_parameter_attributes.rb +80 -0
  121. data/lib/mongoid/named_scope.rb +36 -0
  122. data/lib/mongoid/nested_attributes.rb +43 -0
  123. data/lib/mongoid/paranoia.rb +103 -0
  124. data/lib/mongoid/paths.rb +61 -0
  125. data/lib/mongoid/persistence/command.rb +59 -0
  126. data/lib/mongoid/persistence/insert.rb +53 -0
  127. data/lib/mongoid/persistence/insert_embedded.rb +42 -0
  128. data/lib/mongoid/persistence/remove.rb +44 -0
  129. data/lib/mongoid/persistence/remove_all.rb +40 -0
  130. data/lib/mongoid/persistence/remove_embedded.rb +48 -0
  131. data/lib/mongoid/persistence/update.rb +76 -0
  132. data/lib/mongoid/persistence.rb +237 -0
  133. data/lib/mongoid/railtie.rb +129 -0
  134. data/lib/mongoid/railties/database.rake +171 -0
  135. data/lib/mongoid/railties/document.rb +12 -0
  136. data/lib/mongoid/relations/accessors.rb +157 -0
  137. data/lib/mongoid/relations/auto_save.rb +34 -0
  138. data/lib/mongoid/relations/binding.rb +26 -0
  139. data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
  140. data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
  141. data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
  142. data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
  143. data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
  144. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +99 -0
  145. data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
  146. data/lib/mongoid/relations/bindings.rb +9 -0
  147. data/lib/mongoid/relations/builder.rb +42 -0
  148. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  149. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  150. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  151. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  152. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  153. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  154. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  155. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  156. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  157. data/lib/mongoid/relations/builders.rb +79 -0
  158. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  159. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  160. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  161. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  162. data/lib/mongoid/relations/cascading.rb +55 -0
  163. data/lib/mongoid/relations/constraint.rb +45 -0
  164. data/lib/mongoid/relations/cyclic.rb +97 -0
  165. data/lib/mongoid/relations/embedded/in.rb +173 -0
  166. data/lib/mongoid/relations/embedded/many.rb +483 -0
  167. data/lib/mongoid/relations/embedded/one.rb +170 -0
  168. data/lib/mongoid/relations/macros.rb +306 -0
  169. data/lib/mongoid/relations/many.rb +171 -0
  170. data/lib/mongoid/relations/metadata.rb +533 -0
  171. data/lib/mongoid/relations/nested_builder.rb +68 -0
  172. data/lib/mongoid/relations/one.rb +47 -0
  173. data/lib/mongoid/relations/polymorphic.rb +54 -0
  174. data/lib/mongoid/relations/proxy.rb +128 -0
  175. data/lib/mongoid/relations/referenced/in.rb +216 -0
  176. data/lib/mongoid/relations/referenced/many.rb +443 -0
  177. data/lib/mongoid/relations/referenced/many_to_many.rb +344 -0
  178. data/lib/mongoid/relations/referenced/one.rb +206 -0
  179. data/lib/mongoid/relations/reflections.rb +45 -0
  180. data/lib/mongoid/relations.rb +105 -0
  181. data/lib/mongoid/safe.rb +23 -0
  182. data/lib/mongoid/safety.rb +207 -0
  183. data/lib/mongoid/scope.rb +31 -0
  184. data/lib/mongoid/serialization.rb +99 -0
  185. data/lib/mongoid/state.rb +66 -0
  186. data/lib/mongoid/timestamps.rb +38 -0
  187. data/lib/mongoid/validations/associated.rb +42 -0
  188. data/lib/mongoid/validations/uniqueness.rb +85 -0
  189. data/lib/mongoid/validations.rb +117 -0
  190. data/lib/mongoid/version.rb +4 -0
  191. data/lib/mongoid/versioning.rb +51 -0
  192. data/lib/mongoid.rb +139 -0
  193. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  194. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +23 -0
  195. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  196. data/lib/rails/generators/mongoid/model/templates/model.rb +17 -0
  197. data/lib/rails/generators/mongoid_generator.rb +61 -0
  198. data/lib/rails/mongoid.rb +57 -0
  199. metadata +380 -0
@@ -0,0 +1,213 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ module Optional
5
+
6
+ # Adds fields to be sorted in ascending order. Will add them in the order
7
+ # they were passed into the method.
8
+ #
9
+ # Example:
10
+ #
11
+ # <tt>criteria.ascending(:title, :dob)</tt>
12
+ def ascending(*fields)
13
+ clone.tap do |crit|
14
+ crit.options[:sort] = [] unless options[:sort] || fields.first.nil?
15
+ fields.flatten.each { |field| crit.options[:sort] << [ field, :asc ] }
16
+ end
17
+ end
18
+ alias :asc :ascending
19
+
20
+ # Tells the criteria that the cursor that gets returned needs to be
21
+ # cached. This is so multiple iterations don't hit the database multiple
22
+ # times, however this is not advisable when working with large data sets
23
+ # as the entire results will get stored in memory.
24
+ #
25
+ # Example:
26
+ #
27
+ # <tt>criteria.cache</tt>
28
+ def cache
29
+ clone.tap { |crit| crit.options.merge!(:cache => true) }
30
+ end
31
+
32
+ # Will return true if the cache option has been set.
33
+ #
34
+ # Example:
35
+ #
36
+ # <tt>criteria.cached?</tt>
37
+ def cached?
38
+ options[:cache] == true
39
+ end
40
+
41
+ # Adds fields to be sorted in descending order. Will add them in the order
42
+ # they were passed into the method.
43
+ #
44
+ # Example:
45
+ #
46
+ # <tt>criteria.descending(:title, :dob)</tt>
47
+ def descending(*fields)
48
+ clone.tap do |crit|
49
+ crit.options[:sort] = [] unless options[:sort] || fields.first.nil?
50
+ fields.flatten.each { |field| crit.options[:sort] << [ field, :desc ] }
51
+ end
52
+ end
53
+ alias :desc :descending
54
+
55
+ # Flags the criteria to execute against a read-only slave in the pool
56
+ # instead of master.
57
+ #
58
+ # Example:
59
+ #
60
+ # <tt>criteria.enslave</tt>
61
+ def enslave
62
+ clone.tap { |crit| crit.options.merge!(:enslave => true) }
63
+ end
64
+
65
+ # Will return true if the criteria is enslaved.
66
+ #
67
+ # Example:
68
+ #
69
+ # <tt>criteria.enslaved?</tt>
70
+ def enslaved?
71
+ options[:enslave] == true
72
+ end
73
+
74
+ # Adds a criterion to the +Criteria+ that specifies additional options
75
+ # to be passed to the Ruby driver, in the exact format for the driver.
76
+ #
77
+ # Options:
78
+ #
79
+ # extras: A +Hash+ that gets set to the driver options.
80
+ #
81
+ # Example:
82
+ #
83
+ # <tt>criteria.extras(:limit => 20, :skip => 40)</tt>
84
+ #
85
+ # Returns: <tt>self</tt>
86
+ def extras(extras)
87
+ clone.tap do |crit|
88
+ crit.options.merge!(extras)
89
+ crit.filter_options
90
+ end
91
+ end
92
+
93
+ # Adds a criterion to the +Criteria+ that specifies an id that must be matched.
94
+ #
95
+ # Options:
96
+ #
97
+ # object_id: A single id or an array of ids in +String+ or <tt>BSON::ObjectId</tt> format
98
+ #
99
+ # Example:
100
+ #
101
+ # <tt>criteria.id("4ab2bc4b8ad548971900005c")</tt>
102
+ # <tt>criteria.id(["4ab2bc4b8ad548971900005c", "4c454e7ebf4b98032d000001"])</tt>
103
+ #
104
+ # Returns: <tt>self</tt>
105
+ def id(*ids)
106
+ ids.flatten!
107
+ if ids.size > 1
108
+ any_in(
109
+ :_id => ::BSON::ObjectId.cast!(klass, ids, klass.primary_key.nil?)
110
+ )
111
+ else
112
+ clone.tap do |crit|
113
+ crit.selector[:_id] =
114
+ ::BSON::ObjectId.cast!(klass, ids.first, klass.primary_key.nil?)
115
+ end
116
+ end
117
+ end
118
+
119
+ # Adds a criterion to the +Criteria+ that specifies the maximum number of
120
+ # results to return. This is mostly used in conjunction with <tt>skip()</tt>
121
+ # to handle paginated results.
122
+ #
123
+ # Options:
124
+ #
125
+ # value: An +Integer+ specifying the max number of results. Defaults to 20.
126
+ #
127
+ # Example:
128
+ #
129
+ # <tt>criteria.limit(100)</tt>
130
+ #
131
+ # Returns: <tt>self</tt>
132
+ def limit(value = 20)
133
+ clone.tap { |crit| crit.options[:limit] = value }
134
+ end
135
+
136
+ # Returns the offset option. If a per_page option is in the list then it
137
+ # will replace it with a skip parameter and return the same value. Defaults
138
+ # to 20 if nothing was provided.
139
+ def offset(*args)
140
+ args.size > 0 ? skip(args.first) : options[:skip]
141
+ end
142
+
143
+ # Adds a criterion to the +Criteria+ that specifies the sort order of
144
+ # the returned documents in the database. Similar to a SQL "ORDER BY".
145
+ #
146
+ # Options:
147
+ #
148
+ # params: An +Array+ of [field, direction] sorting pairs.
149
+ #
150
+ # Example:
151
+ #
152
+ # <tt>criteria.order_by([[:field1, :asc], [:field2, :desc]])</tt>
153
+ #
154
+ # Returns: <tt>self</tt>
155
+ def order_by(*args)
156
+ clone.tap do |crit|
157
+ crit.options[:sort] = [] unless options[:sort] || args.first.nil?
158
+ arguments = args.first
159
+ case arguments
160
+ when Hash
161
+ arguments.each do |field, direction|
162
+ crit.options[:sort] << [ field, direction ]
163
+ end
164
+ when Array
165
+ crit.options[:sort].concat(arguments)
166
+ when Complex
167
+ args.flatten.each do |complex|
168
+ crit.options[:sort] << [ complex.key, complex.operator.to_sym ]
169
+ end
170
+ end
171
+ end
172
+ end
173
+ alias :order :order_by
174
+
175
+ # Adds a criterion to the +Criteria+ that specifies how many results to skip
176
+ # when returning Documents. This is mostly used in conjunction with
177
+ # <tt>limit()</tt> to handle paginated results, and is similar to the
178
+ # traditional "offset" parameter.
179
+ #
180
+ # Options:
181
+ #
182
+ # value: An +Integer+ specifying the number of results to skip. Defaults to 0.
183
+ #
184
+ # Example:
185
+ #
186
+ # <tt>criteria.skip(20)</tt>
187
+ #
188
+ # Returns: <tt>self</tt>
189
+ def skip(value = 0)
190
+ clone.tap { |crit| crit.options[:skip] = value }
191
+ end
192
+
193
+ # Adds a criterion to the +Criteria+ that specifies a type or an Array of
194
+ # type that must be matched.
195
+ #
196
+ # Options:
197
+ #
198
+ # types : An +Array+ of types of a +String+ representing the Type of you search
199
+ #
200
+ # Example:
201
+ #
202
+ # <tt>criteria.type('Browser')</tt>
203
+ # <tt>criteria.type(['Firefox', 'Browser'])</tt>
204
+ #
205
+ # Returns: <tt>self</tt>
206
+ def type(types)
207
+ types = [types] unless types.is_a?(Array)
208
+ any_in(:_type => types)
209
+ end
210
+
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+
5
+ class Selector < Hash
6
+ attr_reader :klass
7
+
8
+ def initialize(klass)
9
+ @klass = klass
10
+ end
11
+
12
+ def []=(key, value)
13
+ super(key, try_to_typecast(key, value))
14
+ end
15
+
16
+ def merge!(other)
17
+ other.each_pair do |key, value|
18
+ self[key] = value
19
+ end
20
+ self
21
+ end
22
+ alias update merge!
23
+
24
+ if RUBY_VERSION < '1.9'
25
+ def inspect
26
+ ret = self.keys.inject([]) do |ret, key|
27
+ ret << "#{key.inspect}=>#{self[key].inspect}"
28
+ end
29
+ "{#{ret.sort.join(', ')}}"
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def try_to_typecast(key, value)
36
+ access = key.to_s
37
+ return value unless klass.fields.has_key?(access)
38
+
39
+ field = klass.fields[access]
40
+ typecast_value_for(field, value)
41
+ end
42
+
43
+ def typecast_value_for(field, value)
44
+ return field.set(value) if field.type === value
45
+ case value
46
+ when Hash
47
+ value = value.dup
48
+ value.each_pair do |k, v|
49
+ value[k] = typecast_hash_value(field, k, v)
50
+ end
51
+ when Array
52
+ value.map { |v| typecast_value_for(field, v) }
53
+ when Regexp
54
+ value
55
+ else
56
+ field.type == Array ? value.class.set(value) : field.set(value)
57
+ end
58
+ end
59
+
60
+ def typecast_hash_value(field, key, value)
61
+ case key
62
+ when "$exists"
63
+ Boolean.set(value)
64
+ when "$size"
65
+ Integer.set(value)
66
+ else
67
+ typecast_value_for(field, value)
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ class Cursor
4
+ include Enumerable
5
+ # Operations on the Mongo::Cursor object that will not get overriden by the
6
+ # Mongoid::Cursor are defined here.
7
+ OPERATIONS = [
8
+ :close,
9
+ :closed?,
10
+ :count,
11
+ :explain,
12
+ :fields,
13
+ :full_collection_name,
14
+ :hint,
15
+ :limit,
16
+ :order,
17
+ :query_options_hash,
18
+ :query_opts,
19
+ :selector,
20
+ :skip,
21
+ :snapshot,
22
+ :sort,
23
+ :timeout
24
+ ]
25
+
26
+ attr_reader :collection
27
+
28
+ # The operations above will all delegate to the proxied Mongo::Cursor.
29
+ #
30
+ # Example:
31
+ #
32
+ # <tt>cursor.close</tt>
33
+ OPERATIONS.each do |name|
34
+ define_method(name) { |*args| @cursor.send(name, *args) }
35
+ end
36
+
37
+ # Iterate over each document in the cursor and yield to it.
38
+ #
39
+ # Example:
40
+ #
41
+ # <tt>cursor.each { |doc| p doc.title }</tt>
42
+ def each
43
+ @cursor.each do |document|
44
+ yield Mongoid::Factory.build(@klass, document)
45
+ end
46
+ end
47
+
48
+ # Create the new +Mongoid::Cursor+.
49
+ #
50
+ # Options:
51
+ #
52
+ # collection: The Mongoid::Collection instance.
53
+ # cursor: The Mongo::Cursor to be proxied.
54
+ #
55
+ # Example:
56
+ #
57
+ # <tt>Mongoid::Cursor.new(Person, cursor)</tt>
58
+ def initialize(klass, collection, cursor)
59
+ @klass, @collection, @cursor = klass, collection, cursor
60
+ end
61
+
62
+ # Return the next document in the cursor. Will instantiate a new Mongoid
63
+ # document with the attributes.
64
+ #
65
+ # Example:
66
+ #
67
+ # <tt>cursor.next_document</tt>
68
+ def next_document
69
+ Mongoid::Factory.build(@klass, @cursor.next_document)
70
+ end
71
+
72
+ # Returns an array of all the documents in the cursor.
73
+ #
74
+ # Example:
75
+ #
76
+ # <tt>cursor.to_a</tt>
77
+ def to_a
78
+ @cursor.to_a.collect { |attrs| Mongoid::Factory.build(@klass, attrs) }
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+
4
+ # This module handles functionality for creating default scopes.
5
+ module DefaultScope
6
+
7
+ # Creates a default_scope for the +Document+, similar to ActiveRecord's
8
+ # default_scope. +DefaultScopes+ are proxied +Criteria+ objects that are
9
+ # applied by default to all queries for the class.
10
+ #
11
+ # @example Create a default scope.
12
+ #
13
+ # class Person
14
+ # include Mongoid::Document
15
+ # field :active, :type => Boolean
16
+ # field :count, :type => Integer
17
+ #
18
+ # default_scope :where => { :active => true }
19
+ # end
20
+ #
21
+ # @param [ Hash ] conditions The conditions to create with.
22
+ #
23
+ # @since 2.0.0.rc.1
24
+ def default_scope(conditions = {}, &block)
25
+ self.scope_stack << criteria.fuse(Scope.new(conditions, &block).conditions.scoped)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,251 @@
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
+ @previous_modifications = modifications.dup
106
+ @modifications = {}
107
+ end
108
+
109
+ # Gets all the new values for each of the changed fields, to be passed to
110
+ # a MongoDB $set modifier.
111
+ #
112
+ # Example:
113
+ #
114
+ # person = Person.new(:title => "Sir")
115
+ # person.title = "Madam"
116
+ # person.setters # returns { "title" => "Madam" }
117
+ #
118
+ # Returns:
119
+ #
120
+ # A +Hash+ of new values.
121
+ def setters
122
+ modifications.inject({}) do |sets, (field, changes)|
123
+ key = embedded? ? "#{_position}.#{field}" : field
124
+ sets[key] = changes[1]; sets
125
+ end
126
+ end
127
+
128
+ # Gets all the modifications that have happened to the object before the
129
+ # object was saved.
130
+ #
131
+ # Example:
132
+ #
133
+ # person = Person.new(:title => "Sir")
134
+ # person.title = "Madam"
135
+ # person.save!
136
+ # person.previous_changes # returns { "title" => [ "Sir", "Madam" ] }
137
+ #
138
+ # Returns:
139
+ #
140
+ # A +Hash+ of changes before save.
141
+ def previous_changes
142
+ @previous_modifications
143
+ end
144
+
145
+ # Resets a changed field back to its old value.
146
+ #
147
+ # Example:
148
+ #
149
+ # person = Person.new(:title => "Sir")
150
+ # person.title = "Madam"
151
+ # person.reset_attribute!("title")
152
+ # person.title # "Sir"
153
+ #
154
+ # Returns:
155
+ #
156
+ # The old field value.
157
+ def reset_attribute!(name)
158
+ value = attribute_was(name)
159
+ value ? @attributes[name] = value : @attributes.delete(name)
160
+ modifications.delete(name)
161
+ end
162
+
163
+ # Sets up the modifications hash. This occurs just after the document is
164
+ # instantiated.
165
+ #
166
+ # Example:
167
+ #
168
+ # <tt>document.setup_notifications</tt>
169
+ def setup_modifications
170
+ @accessed ||= {}
171
+ @modifications ||= {}
172
+ @previous_modifications ||= {}
173
+ end
174
+
175
+ # Reset all modifications for the document. This will wipe all the marked
176
+ # changes, but not reset the values.
177
+ #
178
+ # Example:
179
+ #
180
+ # <tt>document.reset_modifications</tt>
181
+ def reset_modifications
182
+ @accessed = {}
183
+ @modifications = {}
184
+ end
185
+
186
+ protected
187
+
188
+ # Audit the original value for a field that can be modified in place.
189
+ #
190
+ # Example:
191
+ #
192
+ # <tt>person.accessed("aliases", [ "007" ])</tt>
193
+ def accessed(name, value)
194
+ @accessed ||= {}
195
+ @accessed[name] = value.dup if (value.is_a?(Array) || value.is_a?(Hash)) && !@accessed.has_key?(name)
196
+ value
197
+ end
198
+
199
+ # Get all normal modifications plus in place potential changes.
200
+ #
201
+ # Example:
202
+ #
203
+ # <tt>person.modifications</tt>
204
+ #
205
+ # Returns:
206
+ #
207
+ # All changes to the document.
208
+ def modifications
209
+ reset_modifications unless @modifications && @accessed
210
+ @accessed.each_pair do |field, value|
211
+ current = @attributes[field]
212
+ @modifications[field] = [ value, current ] if current != value
213
+ end
214
+ @accessed.clear
215
+ @modifications
216
+ end
217
+
218
+ # Audit the change of a field's value.
219
+ #
220
+ # Example:
221
+ #
222
+ # <tt>person.modify("name", "Jack", "John")</tt>
223
+ def modify(name, old_value, new_value)
224
+ @attributes[name] = new_value
225
+ if @modifications && (old_value != new_value)
226
+ original = @modifications[name].first if @modifications[name]
227
+ @modifications[name] = [ (original || old_value), new_value ]
228
+ end
229
+ end
230
+
231
+ module ClassMethods #:nodoc:
232
+ # Add the dynamic dirty methods. These are custom methods defined on a
233
+ # field by field basis that wrap the dirty attribute methods.
234
+ #
235
+ # Example:
236
+ #
237
+ # person = Person.new(:title => "Sir")
238
+ # person.title = "Madam"
239
+ # person.title_change # [ "Sir", "Madam" ]
240
+ # person.title_changed? # true
241
+ # person.title_was # "Sir"
242
+ # person.reset_title!
243
+ def add_dirty_methods(name)
244
+ define_method("#{name}_change") { attribute_change(name) } unless instance_methods.include?("#{name}_change")
245
+ define_method("#{name}_changed?") { attribute_changed?(name) } unless instance_methods.include?("#{name}_changed?")
246
+ define_method("#{name}_was") { attribute_was(name) } unless instance_methods.include?("#{name}_was")
247
+ define_method("reset_#{name}!") { reset_attribute!(name) } unless instance_methods.include?("reset_#{name}!")
248
+ end
249
+ end
250
+ end
251
+ end