mongoid-braxton 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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,78 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Config #:nodoc:
4
+ class ReplsetDatabase < Hash
5
+
6
+ # Configure the database connections. This will return an array
7
+ # containing one Mongo::DB and nil (to keep compatibility with Mongoid::Config::Database)
8
+ # If you want the reads to go to a secondary node use the :read_secondary(true): option
9
+ #
10
+ # @example Configure the connection.
11
+ # db.configure
12
+ #
13
+ # @return [ Array<Mongo::DB, nil ] The Mongo databases.
14
+ #
15
+ # @since 2.0.0.rc.5
16
+ def configure
17
+ #yes, construction is weird but the driver wants "A list of host-port pairs ending with a hash containing any options"
18
+ #mongo likes symbols
19
+ options = self.inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo}
20
+ connection = Mongo::ReplSetConnection.new(*(hosts << options))
21
+
22
+ if authenticating?
23
+ connection.add_auth(database, username, password)
24
+ connection.apply_saved_authentication
25
+ end
26
+
27
+ [ connection.db(database), nil ]
28
+ end
29
+
30
+ # Do we need to authenticate against the database?
31
+ #
32
+ # @example Are we authenticating?
33
+ # db.authenticating?
34
+ #
35
+ # @return [ true, false ] True if auth is needed, false if not.
36
+ #
37
+ # @since 2.0.2
38
+ def authenticating?
39
+ username || password
40
+ end
41
+
42
+ # Convenience for accessing the hash via dot notation.
43
+ #
44
+ # @example Access a value in alternate syntax.
45
+ # db.host
46
+ #
47
+ # @return [ Object ] The value in the hash.
48
+ #
49
+ # @since 2.0.2
50
+ def method_missing(name, *args, &block)
51
+ self[name.to_s]
52
+ end
53
+
54
+ # Create the new db configuration class.
55
+ #
56
+ # @example Initialize the class.
57
+ # Config::ReplsetDatabase.new(
58
+ # "hosts" => [[host1,port1],[host2,port2]]
59
+ # )
60
+ #
61
+ # replSet does not supports auth
62
+ #
63
+ # @param [ Hash ] options The configuration options.
64
+ #
65
+ # @option options [ Array ] :hosts The database host.
66
+ # @option options [ String ] :database The database name.
67
+ # @option options [ Boolean ] :read_secondary Tells the driver to read from secondaries.
68
+ # ...
69
+ #
70
+ # @see Mongo::ReplSetConnection for all options
71
+ #
72
+ # @since 2.0.0.rc.5
73
+ def initialize(options = {})
74
+ merge!(options)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ require "mongoid/contexts/enumerable"
3
+ require "mongoid/contexts/mongo"
4
+
5
+ module Mongoid
6
+ module Contexts
7
+ # Determines the context to be used for this criteria. If the class is an
8
+ # embedded document, then the context will be the array in the has_many
9
+ # association it is in. If the class is a root, then the database itself
10
+ # will be the context.
11
+ #
12
+ # Example:
13
+ #
14
+ # <tt>Contexts.context_for(criteria)</tt>
15
+ def self.context_for(criteria, embedded = false)
16
+ embedded ? Enumerable.new(criteria) : Mongo.new(criteria)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,275 @@
1
+ # encoding: utf-8
2
+ require 'mongoid/contexts/enumerable/sort'
3
+
4
+ module Mongoid #:nodoc:
5
+ module Contexts #:nodoc:
6
+ class Enumerable
7
+ include Relations::Embedded::Atomic
8
+
9
+ attr_accessor :collection, :criteria
10
+
11
+ delegate :blank?, :empty?, :first, :last, :to => :execute
12
+ delegate :klass, :documents, :options, :field_list, :selector, :to => :criteria
13
+
14
+ # Return aggregation counts of the grouped documents. This will count by
15
+ # the first field provided in the fields array.
16
+ #
17
+ # @example Aggregate on a field.
18
+ # person.addresses.only(:street).aggregate
19
+ #
20
+ # @return [ Hash ] Field values as keys, count as values
21
+ def aggregate
22
+ {}.tap do |counts|
23
+ group.each_pair { |key, value| counts[key] = value.size }
24
+ end
25
+ end
26
+
27
+ # Get the average value for the supplied field.
28
+ #
29
+ # @example Get the average.
30
+ # context.avg(:age)
31
+ #
32
+ # @return [ Numeric ] A numeric value that is the average.
33
+ def avg(field)
34
+ total = sum(field)
35
+ total ? (total.to_f / count) : nil
36
+ end
37
+
38
+ # Gets the number of documents in the array. Delegates to size.
39
+ #
40
+ # @example Get the count.
41
+ # context.count
42
+ #
43
+ # @return [ Integer ] The count of documents.
44
+ def count
45
+ @count ||= filter.size
46
+ end
47
+
48
+ # Delete all the documents in the database matching the selector.
49
+ #
50
+ # @example Delete the documents.
51
+ # context.delete_all
52
+ #
53
+ # @return [ Integer ] The number of documents deleted.
54
+ #
55
+ # @since 2.0.0.rc.1
56
+ def delete_all
57
+ atomically(:$pull) do
58
+ set_collection
59
+ count.tap { filter.each(&:delete) }
60
+ end
61
+ end
62
+ alias :delete :delete_all
63
+
64
+ # Destroy all the documents in the database matching the selector.
65
+ #
66
+ # @example Destroy the documents.
67
+ # context.destroy_all
68
+ #
69
+ # @return [ Integer ] The number of documents destroyed.
70
+ #
71
+ # @since 2.0.0.rc.1
72
+ def destroy_all
73
+ atomically(:$pull) do
74
+ set_collection
75
+ count.tap { filter.each(&:destroy) }
76
+ end
77
+ end
78
+ alias :destroy :destroy_all
79
+
80
+ # Gets an array of distinct values for the supplied field across the
81
+ # entire array or the susbset given the criteria.
82
+ #
83
+ # @example Get the list of distinct values.
84
+ # context.distinct(:title)
85
+ #
86
+ # @return [ Array<String> ] The distinct values.
87
+ def distinct(field)
88
+ execute.collect { |doc| doc.send(field) }.uniq
89
+ end
90
+
91
+ # Enumerable implementation of execute. Returns matching documents for
92
+ # the selector, and adds options if supplied.
93
+ #
94
+ # @example Execute the context.
95
+ # context.execute
96
+ #
97
+ # @return [ Array<Document> ] Documents that matched the selector.
98
+ def execute
99
+ limit(sort(filter)) || []
100
+ end
101
+
102
+ # Groups the documents by the first field supplied in the field options.
103
+ #
104
+ # @example Group the context.
105
+ # context.group
106
+ #
107
+ # @return [ Hash ] Field values as keys, arrays of documents as values.
108
+ def group
109
+ field = field_list.first
110
+ execute.group_by { |doc| doc.send(field) }
111
+ end
112
+
113
+ # Create the new enumerable context. This will need the selector and
114
+ # options from a +Criteria+ and a documents array that is the underlying
115
+ # array of embedded documents from a has many association.
116
+ #
117
+ # @example Create a new context.
118
+ # Mongoid::Contexts::Enumerable.new(criteria)
119
+ #
120
+ # @param [ Criteria ] criteria The criteria for the context.
121
+ def initialize(criteria)
122
+ @criteria = criteria
123
+ end
124
+
125
+ # Iterate over each +Document+ in the results. This can take an optional
126
+ # block to pass to each argument in the results.
127
+ #
128
+ # @example Iterate over the documents.
129
+ # context.iterate { |doc| p doc }
130
+ def iterate(&block)
131
+ execute.each(&block)
132
+ end
133
+
134
+ # Get the largest value for the field in all the documents.
135
+ #
136
+ # @example Get the max value.
137
+ # context.max(:age)
138
+ #
139
+ # @return [ Numeric ] The numerical largest value.
140
+ def max(field)
141
+ determine(field, :>=)
142
+ end
143
+
144
+ # Get the smallest value for the field in all the documents.
145
+ #
146
+ # @example Get the minimum value.
147
+ # context.min(:age)
148
+ #
149
+ # @return [ Numeric ] The numerical smallest value.
150
+ def min(field)
151
+ determine(field, :<=)
152
+ end
153
+
154
+ # Get one document.
155
+ #
156
+ # @example Get one document.
157
+ # context.one
158
+ #
159
+ # @return [ Document ] The first document in the array.
160
+ alias :one :first
161
+
162
+ # Get one document and tell the criteria to skip this record on
163
+ # successive calls.
164
+ #
165
+ # @example Shift the documents.
166
+ # context.shift
167
+ #
168
+ # @return [ Document ] The first document in the array.
169
+ def shift
170
+ first.tap do |document|
171
+ self.criteria = criteria.skip((options[:skip] || 0) + 1)
172
+ end
173
+ end
174
+
175
+ # Get the sum of the field values for all the documents.
176
+ #
177
+ # @example Get the sum of the field.
178
+ # context.sum(:cost)
179
+ #
180
+ # @return [ Numeric ] The numerical sum of all the document field values.
181
+ def sum(field)
182
+ sum = execute.inject(nil) do |memo, doc|
183
+ value = doc.send(field) || 0
184
+ memo ? memo += value : value
185
+ end
186
+ end
187
+
188
+ # Very basic update that will perform a simple atomic $set of the
189
+ # attributes provided in the hash. Can be expanded to later for more
190
+ # robust functionality.
191
+ #
192
+ # @example Update all matching documents.
193
+ # context.update_all(:title => "Sir")
194
+ #
195
+ # @param [ Hash ] attributes The sets to perform.
196
+ #
197
+ # @since 2.0.0.rc.6
198
+ def update_all(attributes = nil)
199
+ iterate do |doc|
200
+ doc.update_attributes(attributes || {})
201
+ end
202
+ end
203
+ alias :update :update_all
204
+
205
+ protected
206
+
207
+ # Filters the documents against the criteria's selector
208
+ #
209
+ # @example Filter the documents.
210
+ # context.filter
211
+ #
212
+ # @return [ Array ] The documents filtered.
213
+ def filter
214
+ documents.select { |document| document.matches?(selector) }
215
+ end
216
+
217
+ # If the field exists, perform the comparison and set if true.
218
+ #
219
+ # @example Compare.
220
+ # context.determine
221
+ #
222
+ # @return [ Array<Document> ] The matching documents.
223
+ def determine(field, operator)
224
+ matching = documents.inject(nil) do |memo, doc|
225
+ value = doc.send(field) || 0
226
+ (memo && memo.send(operator, value)) ? memo : value
227
+ end
228
+ end
229
+
230
+ # Limits the result set if skip and limit options.
231
+ #
232
+ # @example Limit the results.
233
+ # context.limit(documents)
234
+ #
235
+ # @return [ Array<Document> ] The limited documents.
236
+ def limit(documents)
237
+ skip, limit = options[:skip], options[:limit]
238
+ if skip && limit
239
+ return documents.slice(skip, limit)
240
+ elsif limit
241
+ return documents.first(limit)
242
+ elsif skip
243
+ return documents.slice(skip..-1)
244
+ end
245
+ documents
246
+ end
247
+
248
+ # Set the collection to the collection of the root document.
249
+ #
250
+ # @example Set the collection.
251
+ # context.set_collection
252
+ #
253
+ # @return [ Collection ] The root collection.
254
+ def set_collection
255
+ root = documents.first._root
256
+ @collection = root.collection if root && !root.embedded?
257
+ end
258
+
259
+ # Sorts the result set if sort options have been set.
260
+ #
261
+ # @example Sort the documents.
262
+ # context.sort(documents)
263
+ #
264
+ # @return [ Array<Document> ] The sorted documents.
265
+ def sort(documents)
266
+ return documents if options[:sort].blank?
267
+ documents.sort_by do |document|
268
+ options[:sort].map do |key, direction|
269
+ Sort.new(document.read_attribute(key), direction)
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ class Enumerable
5
+ class Sort
6
+ attr_reader :value, :direction
7
+
8
+ # Create a new sorting object. This requires a value and a sort
9
+ # direction of +:asc+ or +:desc+.
10
+ def initialize(value, direction)
11
+ @value = value
12
+ @direction = direction
13
+ end
14
+
15
+ # Return +true+ if the direction is +:asc+, otherwise false.
16
+ def ascending?
17
+ direction == :asc
18
+ end
19
+
20
+ # Compare two +Sort+ objects against each other, taking into
21
+ # consideration the direction of the sorting.
22
+ def <=>(other)
23
+ cmp = compare(value, other.value)
24
+ ascending? ? cmp : cmp * -1
25
+ end
26
+
27
+ private
28
+
29
+ # Compare two values allowing for nil values.
30
+ def compare(a, b)
31
+ case
32
+ when a.nil?
33
+ b.nil? ? 0 : 1
34
+ when b.nil?
35
+ -1
36
+ else
37
+ a <=> b
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,345 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ class Mongo
5
+ attr_accessor :criteria
6
+
7
+ delegate :klass, :options, :field_list, :selector, :to => :criteria
8
+
9
+ # Aggregate the context. This will take the internally built selector and options
10
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
11
+ # collection itself will be retrieved from the class provided, and once the
12
+ # query has returned it will provided a grouping of keys with counts.
13
+ #
14
+ # Example:
15
+ #
16
+ # <tt>context.aggregate</tt>
17
+ #
18
+ # Returns:
19
+ #
20
+ # A +Hash+ with field values as keys, counts as values
21
+ def aggregate
22
+ klass.collection.group(
23
+ :key => field_list,
24
+ :cond => selector,
25
+ :initial => { :count => 0 },
26
+ :reduce => Javascript.aggregate
27
+ )
28
+ end
29
+
30
+ # Get the average value for the supplied field.
31
+ #
32
+ # This will take the internally built selector and options
33
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
34
+ # collection itself will be retrieved from the class provided, and once the
35
+ # query has returned it will provided a grouping of keys with averages.
36
+ #
37
+ # Example:
38
+ #
39
+ # <tt>context.avg(:age)</tt>
40
+ #
41
+ # Returns:
42
+ #
43
+ # A numeric value that is the average.
44
+ def avg(field)
45
+ total = sum(field)
46
+ total ? (total / count) : nil
47
+ end
48
+
49
+ # Determine if the context is empty or blank given the criteria. Will
50
+ # perform a quick has_one asking only for the id.
51
+ #
52
+ # Example:
53
+ #
54
+ # <tt>context.blank?</tt>
55
+ def blank?
56
+ klass.collection.find_one(selector, { :fields => [ :_id ] }).nil?
57
+ end
58
+ alias :empty? :blank?
59
+
60
+ # Get the count of matching documents in the database for the context.
61
+ #
62
+ # @example Get the count without skip and limit taken into consideration.
63
+ # context.count
64
+ #
65
+ # @example Get the count with skip and limit applied.
66
+ # context.count(true)
67
+ #
68
+ # @param [Boolean] extras True to inclued previous skip/limit
69
+ # statements in the count; false to ignore them. Defaults to `false`.
70
+ #
71
+ # @return [ Integer ] The count of documents.
72
+ def count(extras = false)
73
+ @count ||= klass.collection.find(selector, process_options).count(extras)
74
+ end
75
+
76
+ # Delete all the documents in the database matching the selector.
77
+ #
78
+ # @example Delete the documents.
79
+ # context.delete_all
80
+ #
81
+ # @return [ Integer ] The number of documents deleted.
82
+ #
83
+ # @since 2.0.0.rc.1
84
+ def delete_all
85
+ klass.delete_all(:conditions => selector)
86
+ end
87
+ alias :delete :delete_all
88
+
89
+ # Destroy all the documents in the database matching the selector.
90
+ #
91
+ # @example Destroy the documents.
92
+ # context.destroy_all
93
+ #
94
+ # @return [ Integer ] The number of documents destroyed.
95
+ #
96
+ # @since 2.0.0.rc.1
97
+ def destroy_all
98
+ klass.destroy_all(:conditions => selector)
99
+ end
100
+ alias :destroy :destroy_all
101
+
102
+ # Gets an array of distinct values for the supplied field across the
103
+ # entire collection or the susbset given the criteria.
104
+ #
105
+ # Example:
106
+ #
107
+ # <tt>context.distinct(:title)</tt>
108
+ def distinct(field)
109
+ klass.collection.distinct(field, selector)
110
+ end
111
+
112
+ # Execute the context. This will take the selector and options
113
+ # and pass them on to the Ruby driver's +find()+ method on the collection. The
114
+ # collection itself will be retrieved from the class provided, and once the
115
+ # query has returned new documents of the type of class provided will be instantiated.
116
+ #
117
+ # Example:
118
+ #
119
+ # <tt>context.execute</tt>
120
+ #
121
+ # Returns:
122
+ #
123
+ # An enumerable +Cursor+.
124
+ def execute
125
+ klass.collection.find(selector, process_options) || []
126
+ end
127
+
128
+ # Groups the context. This will take the internally built selector and options
129
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
130
+ # collection itself will be retrieved from the class provided, and once the
131
+ # query has returned it will provided a grouping of keys with objects.
132
+ #
133
+ # Example:
134
+ #
135
+ # <tt>context.group</tt>
136
+ #
137
+ # Returns:
138
+ #
139
+ # A +Hash+ with field values as keys, arrays of documents as values.
140
+ def group
141
+ klass.collection.group(
142
+ :key => field_list,
143
+ :cond => selector,
144
+ :initial => { :group => [] },
145
+ :reduce => Javascript.group
146
+ ).collect do |docs|
147
+ docs["group"] = docs["group"].collect do |attrs|
148
+ Mongoid::Factory.from_db(klass, attrs)
149
+ end
150
+ docs
151
+ end
152
+ end
153
+
154
+ # Create the new mongo context. This will execute the queries given the
155
+ # selector and options against the database.
156
+ #
157
+ # Example:
158
+ #
159
+ # <tt>Mongoid::Contexts::Mongo.new(criteria)</tt>
160
+ def initialize(criteria)
161
+ @criteria = criteria
162
+ if klass.hereditary? && !criteria.selector.keys.include?(:_type)
163
+ @criteria = criteria.in(:_type => criteria.klass._types)
164
+ end
165
+ @criteria.cache if klass.cached?
166
+ end
167
+
168
+ # Iterate over each +Document+ in the results. This can take an optional
169
+ # block to pass to each argument in the results.
170
+ #
171
+ # Example:
172
+ #
173
+ # <tt>context.iterate { |doc| p doc }</tt>
174
+ def iterate(&block)
175
+ return caching(&block) if criteria.cached?
176
+ if block_given?
177
+ execute.each { |doc| yield doc }
178
+ end
179
+ end
180
+
181
+ # Return the last result for the +Context+. Essentially does a find_one on
182
+ # the collection with the sorting reversed. If no sorting parameters have
183
+ # been provided it will default to ids.
184
+ #
185
+ # Example:
186
+ #
187
+ # <tt>context.last</tt>
188
+ #
189
+ # Returns:
190
+ #
191
+ # The last document in the collection.
192
+ def last
193
+ opts = process_options
194
+ sorting = opts[:sort]
195
+ sorting = [[:_id, :asc]] unless sorting
196
+ opts[:sort] = sorting.collect { |option| [ option[0], option[1].invert ] }
197
+ attributes = klass.collection.find_one(selector, opts)
198
+ attributes ? Mongoid::Factory.from_db(klass, attributes) : nil
199
+ end
200
+
201
+ # Return the max value for a field.
202
+ #
203
+ # This will take the internally built selector and options
204
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
205
+ # collection itself will be retrieved from the class provided, and once the
206
+ # query has returned it will provided a grouping of keys with sums.
207
+ #
208
+ # Example:
209
+ #
210
+ # <tt>context.max(:age)</tt>
211
+ #
212
+ # Returns:
213
+ #
214
+ # A numeric max value.
215
+ def max(field)
216
+ grouped(:max, field.to_s, Javascript.max)
217
+ end
218
+
219
+ # Return the min value for a field.
220
+ #
221
+ # This will take the internally built selector and options
222
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
223
+ # collection itself will be retrieved from the class provided, and once the
224
+ # query has returned it will provided a grouping of keys with sums.
225
+ #
226
+ # Example:
227
+ #
228
+ # <tt>context.min(:age)</tt>
229
+ #
230
+ # Returns:
231
+ #
232
+ # A numeric minimum value.
233
+ def min(field)
234
+ grouped(:min, field.to_s, Javascript.min)
235
+ end
236
+
237
+ # Return the first result for the +Context+.
238
+ #
239
+ # Example:
240
+ #
241
+ # <tt>context.one</tt>
242
+ #
243
+ # Return:
244
+ #
245
+ # The first document in the collection.
246
+ def one
247
+ attributes = klass.collection.find_one(selector, process_options)
248
+ attributes ? Mongoid::Factory.from_db(klass, attributes) : nil
249
+ end
250
+
251
+ alias :first :one
252
+
253
+ # Return the first result for the +Context+ and skip it
254
+ # for successive calls.
255
+ #
256
+ # Returns:
257
+ #
258
+ # The first document in the collection.
259
+ def shift
260
+ document = first
261
+ criteria.skip((options[:skip] || 0) + 1)
262
+ document
263
+ end
264
+
265
+ # Sum the context.
266
+ #
267
+ # This will take the internally built selector and options
268
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
269
+ # collection itself will be retrieved from the class provided, and once the
270
+ # query has returned it will provided a grouping of keys with sums.
271
+ #
272
+ # Example:
273
+ #
274
+ # <tt>context.sum(:age)</tt>
275
+ #
276
+ # Returns:
277
+ #
278
+ # A numeric value that is the sum.
279
+ def sum(field)
280
+ grouped(:sum, field.to_s, Javascript.sum)
281
+ end
282
+
283
+ # Common functionality for grouping operations. Currently used by min, max
284
+ # and sum. Will gsub the field name in the supplied reduce function.
285
+ def grouped(start, field, reduce)
286
+ collection = klass.collection.group(
287
+ :cond => selector,
288
+ :initial => { start => "start" },
289
+ :reduce => reduce.gsub("[field]", field)
290
+ )
291
+ collection.empty? ? nil : collection.first[start.to_s]
292
+ end
293
+
294
+ # Filters the field list. If no fields have been supplied, then it will be
295
+ # empty. If fields have been defined then _type will be included as well.
296
+ def process_options
297
+ fields = options[:fields]
298
+ if fields && fields.size > 0 && !fields.include?(:_type)
299
+ if fields.kind_of?(Hash)
300
+ fields[:_type] = 1 if fields.first.last != 0 # Not excluding
301
+ else
302
+ fields << :type
303
+ end
304
+ options[:fields] = fields
305
+ end
306
+ options.dup
307
+ end
308
+
309
+ # Very basic update that will perform a simple atomic $set of the
310
+ # attributes provided in the hash. Can be expanded to later for more
311
+ # robust functionality.
312
+ #
313
+ # @example Update all matching documents.
314
+ # context.update_all(:title => "Sir")
315
+ #
316
+ # @param [ Hash ] attributes The sets to perform.
317
+ #
318
+ # @since 2.0.0.rc.4
319
+ def update_all(attributes = {})
320
+ klass.collection.update(
321
+ selector,
322
+ { "$set" => attributes },
323
+ :multi => true,
324
+ :safe => Mongoid.persist_in_safe_mode
325
+ )
326
+ end
327
+ alias :update :update_all
328
+
329
+ protected
330
+
331
+ # Iterate over each +Document+ in the results and cache the collection.
332
+ def caching(&block)
333
+ if defined? @collection
334
+ @collection.each(&block)
335
+ else
336
+ @collection = []
337
+ execute.each do |doc|
338
+ @collection << doc
339
+ yield doc if block_given?
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end