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