mongoid-locomotive 2.0.0.beta9

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 (274) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +47 -0
  3. data/lib/mongoid.rb +141 -0
  4. data/lib/mongoid/associations.rb +306 -0
  5. data/lib/mongoid/associations/embedded_in.rb +74 -0
  6. data/lib/mongoid/associations/embeds_many.rb +280 -0
  7. data/lib/mongoid/associations/embeds_one.rb +97 -0
  8. data/lib/mongoid/associations/foreign_key.rb +35 -0
  9. data/lib/mongoid/associations/meta_data.rb +38 -0
  10. data/lib/mongoid/associations/options.rb +62 -0
  11. data/lib/mongoid/associations/proxy.rb +33 -0
  12. data/lib/mongoid/associations/referenced_in.rb +59 -0
  13. data/lib/mongoid/associations/references_many.rb +245 -0
  14. data/lib/mongoid/associations/references_many_as_array.rb +78 -0
  15. data/lib/mongoid/associations/references_one.rb +99 -0
  16. data/lib/mongoid/atomicity.rb +55 -0
  17. data/lib/mongoid/attributes.rb +242 -0
  18. data/lib/mongoid/callbacks.rb +21 -0
  19. data/lib/mongoid/collection.rb +120 -0
  20. data/lib/mongoid/collections.rb +71 -0
  21. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  22. data/lib/mongoid/collections/master.rb +29 -0
  23. data/lib/mongoid/collections/operations.rb +41 -0
  24. data/lib/mongoid/collections/slaves.rb +45 -0
  25. data/lib/mongoid/components.rb +34 -0
  26. data/lib/mongoid/config.rb +263 -0
  27. data/lib/mongoid/contexts.rb +24 -0
  28. data/lib/mongoid/contexts/enumerable.rb +156 -0
  29. data/lib/mongoid/contexts/ids.rb +25 -0
  30. data/lib/mongoid/contexts/mongo.rb +285 -0
  31. data/lib/mongoid/contexts/paging.rb +50 -0
  32. data/lib/mongoid/criteria.rb +248 -0
  33. data/lib/mongoid/criterion/complex.rb +21 -0
  34. data/lib/mongoid/criterion/exclusion.rb +65 -0
  35. data/lib/mongoid/criterion/inclusion.rb +110 -0
  36. data/lib/mongoid/criterion/optional.rb +189 -0
  37. data/lib/mongoid/cursor.rb +81 -0
  38. data/lib/mongoid/deprecation.rb +21 -0
  39. data/lib/mongoid/dirty.rb +252 -0
  40. data/lib/mongoid/document.rb +210 -0
  41. data/lib/mongoid/errors.rb +131 -0
  42. data/lib/mongoid/extensions.rb +115 -0
  43. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  44. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  45. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  46. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  47. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  48. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  49. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  50. data/lib/mongoid/extensions/date/conversions.rb +24 -0
  51. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  52. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  53. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  54. data/lib/mongoid/extensions/hash/accessors.rb +42 -0
  55. data/lib/mongoid/extensions/hash/assimilation.rb +40 -0
  56. data/lib/mongoid/extensions/hash/conversions.rb +42 -0
  57. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  58. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  59. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  60. data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
  61. data/lib/mongoid/extensions/object/conversions.rb +21 -0
  62. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  63. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  64. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  65. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  66. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  67. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  68. data/lib/mongoid/extensions/time_conversions.rb +35 -0
  69. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  70. data/lib/mongoid/extras.rb +61 -0
  71. data/lib/mongoid/factory.rb +20 -0
  72. data/lib/mongoid/field.rb +83 -0
  73. data/lib/mongoid/fields.rb +62 -0
  74. data/lib/mongoid/finders.rb +145 -0
  75. data/lib/mongoid/hierarchy.rb +74 -0
  76. data/lib/mongoid/identity.rb +47 -0
  77. data/lib/mongoid/indexes.rb +27 -0
  78. data/lib/mongoid/javascript.rb +21 -0
  79. data/lib/mongoid/javascript/functions.yml +37 -0
  80. data/lib/mongoid/logger.rb +19 -0
  81. data/lib/mongoid/matchers.rb +35 -0
  82. data/lib/mongoid/matchers/all.rb +11 -0
  83. data/lib/mongoid/matchers/default.rb +26 -0
  84. data/lib/mongoid/matchers/exists.rb +13 -0
  85. data/lib/mongoid/matchers/gt.rb +11 -0
  86. data/lib/mongoid/matchers/gte.rb +11 -0
  87. data/lib/mongoid/matchers/in.rb +11 -0
  88. data/lib/mongoid/matchers/lt.rb +11 -0
  89. data/lib/mongoid/matchers/lte.rb +11 -0
  90. data/lib/mongoid/matchers/ne.rb +11 -0
  91. data/lib/mongoid/matchers/nin.rb +11 -0
  92. data/lib/mongoid/matchers/size.rb +11 -0
  93. data/lib/mongoid/memoization.rb +33 -0
  94. data/lib/mongoid/named_scope.rb +37 -0
  95. data/lib/mongoid/paranoia.rb +106 -0
  96. data/lib/mongoid/paths.rb +61 -0
  97. data/lib/mongoid/persistence.rb +216 -0
  98. data/lib/mongoid/persistence/command.rb +39 -0
  99. data/lib/mongoid/persistence/insert.rb +48 -0
  100. data/lib/mongoid/persistence/insert_embedded.rb +44 -0
  101. data/lib/mongoid/persistence/remove.rb +39 -0
  102. data/lib/mongoid/persistence/remove_all.rb +38 -0
  103. data/lib/mongoid/persistence/remove_embedded.rb +50 -0
  104. data/lib/mongoid/persistence/update.rb +71 -0
  105. data/lib/mongoid/railtie.rb +67 -0
  106. data/lib/mongoid/railties/database.rake +60 -0
  107. data/lib/mongoid/scope.rb +75 -0
  108. data/lib/mongoid/state.rb +32 -0
  109. data/lib/mongoid/timestamps.rb +27 -0
  110. data/lib/mongoid/validations.rb +51 -0
  111. data/lib/mongoid/validations/associated.rb +32 -0
  112. data/lib/mongoid/validations/locale/en.yml +5 -0
  113. data/lib/mongoid/validations/uniqueness.rb +56 -0
  114. data/lib/mongoid/version.rb +4 -0
  115. data/lib/mongoid/versioning.rb +26 -0
  116. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  117. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +24 -0
  118. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  119. data/lib/rails/generators/mongoid/model/templates/model.rb +15 -0
  120. data/lib/rails/generators/mongoid_generator.rb +61 -0
  121. data/spec/integration/mongoid/association_attributes_spec.rb +71 -0
  122. data/spec/integration/mongoid/associations_spec.rb +768 -0
  123. data/spec/integration/mongoid/attributes_spec.rb +59 -0
  124. data/spec/integration/mongoid/callback_spec.rb +33 -0
  125. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  126. data/spec/integration/mongoid/criteria_spec.rb +281 -0
  127. data/spec/integration/mongoid/dirty_spec.rb +85 -0
  128. data/spec/integration/mongoid/document_spec.rb +741 -0
  129. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  130. data/spec/integration/mongoid/finders_spec.rb +119 -0
  131. data/spec/integration/mongoid/inheritance_spec.rb +171 -0
  132. data/spec/integration/mongoid/named_scope_spec.rb +58 -0
  133. data/spec/integration/mongoid/paranoia_spec.rb +44 -0
  134. data/spec/integration/mongoid/persistence/update_spec.rb +46 -0
  135. data/spec/integration/mongoid/persistence_spec.rb +311 -0
  136. data/spec/integration/mongoid/validations/uniqueness_spec.rb +206 -0
  137. data/spec/models/account.rb +5 -0
  138. data/spec/models/address.rb +40 -0
  139. data/spec/models/agent.rb +7 -0
  140. data/spec/models/animal.rb +15 -0
  141. data/spec/models/answer.rb +4 -0
  142. data/spec/models/callbacks.rb +47 -0
  143. data/spec/models/category.rb +13 -0
  144. data/spec/models/comment.rb +10 -0
  145. data/spec/models/country_code.rb +6 -0
  146. data/spec/models/employer.rb +5 -0
  147. data/spec/models/favorite.rb +8 -0
  148. data/spec/models/game.rb +9 -0
  149. data/spec/models/inheritance.rb +72 -0
  150. data/spec/models/location.rb +5 -0
  151. data/spec/models/login.rb +6 -0
  152. data/spec/models/mixed_drink.rb +4 -0
  153. data/spec/models/name.rb +13 -0
  154. data/spec/models/namespacing.rb +11 -0
  155. data/spec/models/paranoid_post.rb +18 -0
  156. data/spec/models/parents.rb +32 -0
  157. data/spec/models/patient.rb +15 -0
  158. data/spec/models/person.rb +106 -0
  159. data/spec/models/pet.rb +7 -0
  160. data/spec/models/pet_owner.rb +6 -0
  161. data/spec/models/phone.rb +7 -0
  162. data/spec/models/post.rb +25 -0
  163. data/spec/models/preference.rb +7 -0
  164. data/spec/models/question.rb +8 -0
  165. data/spec/models/survey.rb +6 -0
  166. data/spec/models/translation.rb +5 -0
  167. data/spec/models/user.rb +6 -0
  168. data/spec/models/user_accout.rb +5 -0
  169. data/spec/models/vet_visit.rb +5 -0
  170. data/spec/models/video.rb +5 -0
  171. data/spec/spec_helper.rb +33 -0
  172. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  173. data/spec/unit/mongoid/associations/embeds_many_spec.rb +626 -0
  174. data/spec/unit/mongoid/associations/embeds_one_spec.rb +287 -0
  175. data/spec/unit/mongoid/associations/foreign_key_spec.rb +90 -0
  176. data/spec/unit/mongoid/associations/meta_data_spec.rb +110 -0
  177. data/spec/unit/mongoid/associations/options_spec.rb +215 -0
  178. data/spec/unit/mongoid/associations/referenced_in_spec.rb +145 -0
  179. data/spec/unit/mongoid/associations/references_many_as_array_spec.rb +424 -0
  180. data/spec/unit/mongoid/associations/references_many_spec.rb +502 -0
  181. data/spec/unit/mongoid/associations/references_one_spec.rb +204 -0
  182. data/spec/unit/mongoid/associations_spec.rb +688 -0
  183. data/spec/unit/mongoid/atomicity_spec.rb +164 -0
  184. data/spec/unit/mongoid/attributes_spec.rb +646 -0
  185. data/spec/unit/mongoid/callbacks_spec.rb +85 -0
  186. data/spec/unit/mongoid/collection_spec.rb +187 -0
  187. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  188. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  189. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  190. data/spec/unit/mongoid/collections_spec.rb +98 -0
  191. data/spec/unit/mongoid/config_spec.rb +298 -0
  192. data/spec/unit/mongoid/contexts/enumerable_spec.rb +447 -0
  193. data/spec/unit/mongoid/contexts/mongo_spec.rb +703 -0
  194. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  195. data/spec/unit/mongoid/criteria_spec.rb +873 -0
  196. data/spec/unit/mongoid/criterion/complex_spec.rb +17 -0
  197. data/spec/unit/mongoid/criterion/exclusion_spec.rb +121 -0
  198. data/spec/unit/mongoid/criterion/inclusion_spec.rb +274 -0
  199. data/spec/unit/mongoid/criterion/optional_spec.rb +483 -0
  200. data/spec/unit/mongoid/cursor_spec.rb +80 -0
  201. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  202. data/spec/unit/mongoid/dirty_spec.rb +430 -0
  203. data/spec/unit/mongoid/document_spec.rb +623 -0
  204. data/spec/unit/mongoid/errors_spec.rb +154 -0
  205. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  206. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  207. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +52 -0
  208. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  209. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +36 -0
  210. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  211. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  212. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +145 -0
  213. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +14 -0
  214. data/spec/unit/mongoid/extensions/false_class/equality_spec.rb +35 -0
  215. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  216. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  217. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +59 -0
  218. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +35 -0
  219. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  220. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  221. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  222. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +29 -0
  223. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +44 -0
  224. data/spec/unit/mongoid/extensions/objectid/conversions_spec.rb +22 -0
  225. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  226. data/spec/unit/mongoid/extensions/set/conversions_spec.rb +21 -0
  227. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +28 -0
  228. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  229. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +107 -0
  230. data/spec/unit/mongoid/extensions/time_conversions_spec.rb +186 -0
  231. data/spec/unit/mongoid/extensions/true_class/equality_spec.rb +35 -0
  232. data/spec/unit/mongoid/extras_spec.rb +102 -0
  233. data/spec/unit/mongoid/factory_spec.rb +31 -0
  234. data/spec/unit/mongoid/field_spec.rb +169 -0
  235. data/spec/unit/mongoid/fields_spec.rb +181 -0
  236. data/spec/unit/mongoid/finders_spec.rb +439 -0
  237. data/spec/unit/mongoid/hierarchy_spec.rb +68 -0
  238. data/spec/unit/mongoid/identity_spec.rb +109 -0
  239. data/spec/unit/mongoid/indexes_spec.rb +99 -0
  240. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  241. data/spec/unit/mongoid/logger_spec.rb +38 -0
  242. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  243. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  244. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  245. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  246. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  247. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  248. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  249. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  250. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  251. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  252. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  253. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  254. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  255. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  256. data/spec/unit/mongoid/paranoia_spec.rb +108 -0
  257. data/spec/unit/mongoid/paths_spec.rb +272 -0
  258. data/spec/unit/mongoid/persistence/insert_embedded_spec.rb +154 -0
  259. data/spec/unit/mongoid/persistence/insert_spec.rb +144 -0
  260. data/spec/unit/mongoid/persistence/remove_all_spec.rb +82 -0
  261. data/spec/unit/mongoid/persistence/remove_embedded_spec.rb +152 -0
  262. data/spec/unit/mongoid/persistence/remove_spec.rb +89 -0
  263. data/spec/unit/mongoid/persistence/update_spec.rb +177 -0
  264. data/spec/unit/mongoid/persistence_spec.rb +452 -0
  265. data/spec/unit/mongoid/scope_spec.rb +240 -0
  266. data/spec/unit/mongoid/serialization_spec.rb +43 -0
  267. data/spec/unit/mongoid/state_spec.rb +94 -0
  268. data/spec/unit/mongoid/timestamps_spec.rb +30 -0
  269. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  270. data/spec/unit/mongoid/validations/uniqueness_spec.rb +201 -0
  271. data/spec/unit/mongoid/validations_spec.rb +43 -0
  272. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  273. data/spec/unit/mongoid_spec.rb +46 -0
  274. metadata +433 -0
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ require "mongoid/contexts/ids"
3
+ require "mongoid/contexts/paging"
4
+ require "mongoid/contexts/enumerable"
5
+ require "mongoid/contexts/mongo"
6
+
7
+ module Mongoid
8
+ module Contexts
9
+ # Determines the context to be used for this criteria. If the class is an
10
+ # embedded document, then the context will be the array in the has_many
11
+ # association it is in. If the class is a root, then the database itself
12
+ # will be the context.
13
+ #
14
+ # Example:
15
+ #
16
+ # <tt>Contexts.context_for(criteria)</tt>
17
+ def self.context_for(criteria)
18
+ if criteria.klass.embedded?
19
+ return Contexts::Enumerable.new(criteria)
20
+ end
21
+ Contexts::Mongo.new(criteria)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,156 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ class Enumerable
5
+ include Ids, Paging
6
+ attr_reader :criteria
7
+
8
+ delegate :blank?, :empty?, :first, :last, :to => :execute
9
+ delegate :klass, :documents, :options, :selector, :to => :criteria
10
+
11
+ # Return aggregation counts of the grouped documents. This will count by
12
+ # the first field provided in the fields array.
13
+ #
14
+ # Returns:
15
+ #
16
+ # A +Hash+ with field values as keys, count as values
17
+ def aggregate
18
+ counts = {}
19
+ group.each_pair { |key, value| counts[key] = value.size }
20
+ counts
21
+ end
22
+
23
+ # Get the average value for the supplied field.
24
+ #
25
+ # Example:
26
+ #
27
+ # <tt>context.avg(:age)</tt>
28
+ #
29
+ # Returns:
30
+ #
31
+ # A numeric value that is the average.
32
+ def avg(field)
33
+ total = sum(field)
34
+ total ? (total.to_f / count) : nil
35
+ end
36
+
37
+ # Gets the number of documents in the array. Delegates to size.
38
+ def count
39
+ @count ||= filter.size
40
+ end
41
+
42
+ # Gets an array of distinct values for the supplied field across the
43
+ # entire array or the susbset given the criteria.
44
+ #
45
+ # Example:
46
+ #
47
+ # <tt>context.distinct(:title)</tt>
48
+ def distinct(field)
49
+ execute.collect { |doc| doc.send(field) }.uniq
50
+ end
51
+
52
+ # Enumerable implementation of execute. Returns matching documents for
53
+ # the selector, and adds options if supplied.
54
+ #
55
+ # Returns:
56
+ #
57
+ # An +Array+ of documents that matched the selector.
58
+ def execute(paginating = false)
59
+ limit(filter) || []
60
+ end
61
+
62
+ # Groups the documents by the first field supplied in the field options.
63
+ #
64
+ # Returns:
65
+ #
66
+ # A +Hash+ with field values as keys, arrays of documents as values.
67
+ def group
68
+ field = options[:fields].first
69
+ execute.group_by { |doc| doc.send(field) }
70
+ end
71
+
72
+ # Create the new enumerable context. This will need the selector and
73
+ # options from a +Criteria+ and a documents array that is the underlying
74
+ # array of embedded documents from a has many association.
75
+ #
76
+ # Example:
77
+ #
78
+ # <tt>Mongoid::Contexts::Enumerable.new(criteria)</tt>
79
+ def initialize(criteria)
80
+ @criteria = criteria
81
+ end
82
+
83
+ # Iterate over each +Document+ in the results. This can take an optional
84
+ # block to pass to each argument in the results.
85
+ #
86
+ # Example:
87
+ #
88
+ # <tt>context.iterate { |doc| p doc }</tt>
89
+ def iterate(&block)
90
+ execute.each(&block)
91
+ end
92
+
93
+ # Get the largest value for the field in all the documents.
94
+ #
95
+ # Returns:
96
+ #
97
+ # The numerical largest value.
98
+ def max(field)
99
+ determine(field, :>=)
100
+ end
101
+
102
+ # Get the smallest value for the field in all the documents.
103
+ #
104
+ # Returns:
105
+ #
106
+ # The numerical smallest value.
107
+ def min(field)
108
+ determine(field, :<=)
109
+ end
110
+
111
+ # Get one document.
112
+ #
113
+ # Returns:
114
+ #
115
+ # The first document in the +Array+
116
+ alias :one :first
117
+
118
+ # Get the sum of the field values for all the documents.
119
+ #
120
+ # Returns:
121
+ #
122
+ # The numerical sum of all the document field values.
123
+ def sum(field)
124
+ sum = execute.inject(nil) do |memo, doc|
125
+ value = doc.send(field)
126
+ memo ? memo += value : value
127
+ end
128
+ end
129
+
130
+ protected
131
+ # Filters the documents against the criteria's selector
132
+ def filter
133
+ documents.select { |document| document.matches?(selector) }
134
+ end
135
+
136
+ # If the field exists, perform the comparison and set if true.
137
+ def determine(field, operator)
138
+ matching = documents.inject(nil) do |memo, doc|
139
+ value = doc.send(field)
140
+ (memo && memo.send(operator, value)) ? memo : value
141
+ end
142
+ end
143
+
144
+ # Limits the result set if skip and limit options.
145
+ def limit(documents)
146
+ skip, limit = options[:skip], options[:limit]
147
+ if skip && limit
148
+ return documents.slice(skip, limit)
149
+ elsif limit
150
+ return documents.first(limit)
151
+ end
152
+ documents
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Ids
5
+ # Return documents based on an id search. Will handle if a single id has
6
+ # been passed or mulitple ids.
7
+ #
8
+ # Example:
9
+ #
10
+ # context.id_criteria([1, 2, 3])
11
+ #
12
+ # Returns:
13
+ #
14
+ # The single or multiple documents.
15
+ def id_criteria(params)
16
+ criteria.id(params)
17
+ result = params.is_a?(Array) ? criteria.entries : one
18
+ if Mongoid.raise_not_found_error
19
+ raise Errors::DocumentNotFound.new(klass, params) if result.blank?
20
+ end
21
+ return result
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,285 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ class Mongo
5
+ include Ids, Paging
6
+ attr_reader :criteria
7
+
8
+ delegate :klass, :options, :selector, :to => :criteria
9
+
10
+ # Aggregate the context. This will take the internally built selector and options
11
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
12
+ # collection itself will be retrieved from the class provided, and once the
13
+ # query has returned it will provided a grouping of keys with counts.
14
+ #
15
+ # Example:
16
+ #
17
+ # <tt>context.aggregate</tt>
18
+ #
19
+ # Returns:
20
+ #
21
+ # A +Hash+ with field values as keys, counts as values
22
+ def aggregate
23
+ klass.collection.group(options[:fields], selector, { :count => 0 }, Javascript.aggregate, true)
24
+ end
25
+
26
+ # Get the average value for the supplied field.
27
+ #
28
+ # This will take the internally built selector and options
29
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
30
+ # collection itself will be retrieved from the class provided, and once the
31
+ # query has returned it will provided a grouping of keys with averages.
32
+ #
33
+ # Example:
34
+ #
35
+ # <tt>context.avg(:age)</tt>
36
+ #
37
+ # Returns:
38
+ #
39
+ # A numeric value that is the average.
40
+ def avg(field)
41
+ total = sum(field)
42
+ total ? (total / count) : nil
43
+ end
44
+
45
+ # Determine if the context is empty or blank given the criteria. Will
46
+ # perform a quick has_one asking only for the id.
47
+ #
48
+ # Example:
49
+ #
50
+ # <tt>context.blank?</tt>
51
+ def blank?
52
+ klass.collection.find_one(selector, { :fields => [ :_id ] }).nil?
53
+ end
54
+
55
+ alias :empty? :blank?
56
+
57
+ # Get the count of matching documents in the database for the context.
58
+ #
59
+ # Example:
60
+ #
61
+ # <tt>context.count</tt>
62
+ #
63
+ # Returns:
64
+ #
65
+ # An +Integer+ count of documents.
66
+ def count
67
+ @count ||= klass.collection.find(selector, process_options).count
68
+ end
69
+
70
+ # Gets an array of distinct values for the supplied field across the
71
+ # entire collection or the susbset given the criteria.
72
+ #
73
+ # Example:
74
+ #
75
+ # <tt>context.distinct(:title)</tt>
76
+ def distinct(field)
77
+ klass.collection.distinct(field, selector)
78
+ end
79
+
80
+ # Execute the context. This will take the selector and options
81
+ # and pass them on to the Ruby driver's +find()+ method on the collection. The
82
+ # collection itself will be retrieved from the class provided, and once the
83
+ # query has returned new documents of the type of class provided will be instantiated.
84
+ #
85
+ # Example:
86
+ #
87
+ # <tt>context.execute</tt>
88
+ #
89
+ # Returns:
90
+ #
91
+ # An enumerable +Cursor+.
92
+ def execute(paginating = false)
93
+ cursor = klass.collection.find(selector, process_options)
94
+ if cursor
95
+ @count = cursor.count if paginating
96
+ cursor
97
+ else
98
+ []
99
+ end
100
+ end
101
+
102
+ # Groups the context. This will take the internally built selector and options
103
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
104
+ # collection itself will be retrieved from the class provided, and once the
105
+ # query has returned it will provided a grouping of keys with objects.
106
+ #
107
+ # Example:
108
+ #
109
+ # <tt>context.group</tt>
110
+ #
111
+ # Returns:
112
+ #
113
+ # A +Hash+ with field values as keys, arrays of documents as values.
114
+ def group
115
+ klass.collection.group(
116
+ options[:fields],
117
+ selector,
118
+ { :group => [] },
119
+ Javascript.group
120
+ ).collect do |docs|
121
+ docs["group"] = docs["group"].collect do |attrs|
122
+ Mongoid::Factory.build(klass, attrs)
123
+ end
124
+ docs
125
+ end
126
+ end
127
+
128
+ # Create the new mongo context. This will execute the queries given the
129
+ # selector and options against the database.
130
+ #
131
+ # Example:
132
+ #
133
+ # <tt>Mongoid::Contexts::Mongo.new(criteria)</tt>
134
+ def initialize(criteria)
135
+ @criteria = criteria
136
+ if klass.hereditary && !criteria.selector.keys.include?(:_type)
137
+ criteria.in(:_type => criteria.klass._types)
138
+ end
139
+ criteria.enslave if klass.enslaved?
140
+ criteria.cache if klass.cached?
141
+ end
142
+
143
+ # Iterate over each +Document+ in the results. This can take an optional
144
+ # block to pass to each argument in the results.
145
+ #
146
+ # Example:
147
+ #
148
+ # <tt>context.iterate { |doc| p doc }</tt>
149
+ def iterate(&block)
150
+ return caching(&block) if criteria.cached?
151
+ if block_given?
152
+ execute.each { |doc| yield doc }
153
+ end
154
+ end
155
+
156
+ # Return the last result for the +Context+. Essentially does a find_one on
157
+ # the collection with the sorting reversed. If no sorting parameters have
158
+ # been provided it will default to ids.
159
+ #
160
+ # Example:
161
+ #
162
+ # <tt>context.last</tt>
163
+ #
164
+ # Returns:
165
+ #
166
+ # The last document in the collection.
167
+ def last
168
+ opts = process_options
169
+ sorting = opts[:sort]
170
+ sorting = [[:_id, :asc]] unless sorting
171
+ opts[:sort] = sorting.collect { |option| [ option[0], option[1].invert ] }
172
+ attributes = klass.collection.find_one(selector, opts)
173
+ attributes ? Mongoid::Factory.build(klass, attributes) : nil
174
+ end
175
+
176
+ # Return the max value for a field.
177
+ #
178
+ # This will take the internally built selector and options
179
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
180
+ # collection itself will be retrieved from the class provided, and once the
181
+ # query has returned it will provided a grouping of keys with sums.
182
+ #
183
+ # Example:
184
+ #
185
+ # <tt>context.max(:age)</tt>
186
+ #
187
+ # Returns:
188
+ #
189
+ # A numeric max value.
190
+ def max(field)
191
+ grouped(:max, field.to_s, Javascript.max)
192
+ end
193
+
194
+ # Return the min value for a field.
195
+ #
196
+ # This will take the internally built selector and options
197
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
198
+ # collection itself will be retrieved from the class provided, and once the
199
+ # query has returned it will provided a grouping of keys with sums.
200
+ #
201
+ # Example:
202
+ #
203
+ # <tt>context.min(:age)</tt>
204
+ #
205
+ # Returns:
206
+ #
207
+ # A numeric minimum value.
208
+ def min(field)
209
+ grouped(:min, field.to_s, Javascript.min)
210
+ end
211
+
212
+ # Return the first result for the +Context+.
213
+ #
214
+ # Example:
215
+ #
216
+ # <tt>context.one</tt>
217
+ #
218
+ # Return:
219
+ #
220
+ # The first document in the collection.
221
+ def one
222
+ attributes = klass.collection.find_one(selector, process_options)
223
+ attributes ? Mongoid::Factory.build(klass, attributes) : nil
224
+ end
225
+
226
+ alias :first :one
227
+
228
+ # Sum the context.
229
+ #
230
+ # This will take the internally built selector and options
231
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
232
+ # collection itself will be retrieved from the class provided, and once the
233
+ # query has returned it will provided a grouping of keys with sums.
234
+ #
235
+ # Example:
236
+ #
237
+ # <tt>context.sum(:age)</tt>
238
+ #
239
+ # Returns:
240
+ #
241
+ # A numeric value that is the sum.
242
+ def sum(field)
243
+ grouped(:sum, field.to_s, Javascript.sum)
244
+ end
245
+
246
+ # Common functionality for grouping operations. Currently used by min, max
247
+ # and sum. Will gsub the field name in the supplied reduce function.
248
+ def grouped(start, field, reduce)
249
+ collection = klass.collection.group(
250
+ nil,
251
+ selector,
252
+ { start => "start" },
253
+ reduce.gsub("[field]", field)
254
+ )
255
+ collection.empty? ? nil : collection.first[start.to_s]
256
+ end
257
+
258
+ # Filters the field list. If no fields have been supplied, then it will be
259
+ # empty. If fields have been defined then _type will be included as well.
260
+ def process_options
261
+ fields = options[:fields]
262
+ if fields && fields.size > 0 && !fields.include?(:_type)
263
+ fields << :_type
264
+ options[:fields] = fields
265
+ end
266
+ options.dup
267
+ end
268
+
269
+ protected
270
+
271
+ # Iterate over each +Document+ in the results and cache the collection.
272
+ def caching(&block)
273
+ if defined? @collection
274
+ @collection.each(&block)
275
+ else
276
+ @collection = []
277
+ execute.each do |doc|
278
+ @collection << doc
279
+ yield doc if block_given?
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end