mongoid-rails2 1.9.3

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 (104) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +49 -0
  3. data/lib/mongoid/associations/belongs_to_related.rb +58 -0
  4. data/lib/mongoid/associations/embedded_in.rb +72 -0
  5. data/lib/mongoid/associations/embeds_many.rb +254 -0
  6. data/lib/mongoid/associations/embeds_one.rb +96 -0
  7. data/lib/mongoid/associations/has_many_related.rb +181 -0
  8. data/lib/mongoid/associations/has_one_related.rb +85 -0
  9. data/lib/mongoid/associations/meta_data.rb +29 -0
  10. data/lib/mongoid/associations/options.rb +57 -0
  11. data/lib/mongoid/associations/proxy.rb +24 -0
  12. data/lib/mongoid/associations.rb +300 -0
  13. data/lib/mongoid/attributes.rb +204 -0
  14. data/lib/mongoid/callbacks.rb +23 -0
  15. data/lib/mongoid/collection.rb +120 -0
  16. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  17. data/lib/mongoid/collections/master.rb +29 -0
  18. data/lib/mongoid/collections/operations.rb +41 -0
  19. data/lib/mongoid/collections/slaves.rb +45 -0
  20. data/lib/mongoid/collections.rb +41 -0
  21. data/lib/mongoid/components.rb +27 -0
  22. data/lib/mongoid/concern.rb +31 -0
  23. data/lib/mongoid/config.rb +191 -0
  24. data/lib/mongoid/contexts/enumerable.rb +151 -0
  25. data/lib/mongoid/contexts/ids.rb +25 -0
  26. data/lib/mongoid/contexts/mongo.rb +285 -0
  27. data/lib/mongoid/contexts/paging.rb +50 -0
  28. data/lib/mongoid/contexts.rb +25 -0
  29. data/lib/mongoid/criteria.rb +249 -0
  30. data/lib/mongoid/criterion/complex.rb +21 -0
  31. data/lib/mongoid/criterion/exclusion.rb +65 -0
  32. data/lib/mongoid/criterion/inclusion.rb +110 -0
  33. data/lib/mongoid/criterion/optional.rb +136 -0
  34. data/lib/mongoid/cursor.rb +81 -0
  35. data/lib/mongoid/deprecation.rb +22 -0
  36. data/lib/mongoid/dirty.rb +253 -0
  37. data/lib/mongoid/document.rb +311 -0
  38. data/lib/mongoid/errors.rb +108 -0
  39. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  40. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  41. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  42. data/lib/mongoid/extensions/array/conversions.rb +29 -0
  43. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  44. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  45. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  46. data/lib/mongoid/extensions/boolean/conversions.rb +22 -0
  47. data/lib/mongoid/extensions/date/conversions.rb +24 -0
  48. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  49. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  50. data/lib/mongoid/extensions/hash/accessors.rb +38 -0
  51. data/lib/mongoid/extensions/hash/assimilation.rb +39 -0
  52. data/lib/mongoid/extensions/hash/conversions.rb +45 -0
  53. data/lib/mongoid/extensions/hash/criteria_helpers.rb +21 -0
  54. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  55. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  56. data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
  57. data/lib/mongoid/extensions/object/conversions.rb +33 -0
  58. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  59. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  60. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  61. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  62. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  63. data/lib/mongoid/extensions/time_conversions.rb +35 -0
  64. data/lib/mongoid/extensions.rb +101 -0
  65. data/lib/mongoid/extras.rb +61 -0
  66. data/lib/mongoid/factory.rb +20 -0
  67. data/lib/mongoid/field.rb +59 -0
  68. data/lib/mongoid/fields.rb +65 -0
  69. data/lib/mongoid/finders.rb +144 -0
  70. data/lib/mongoid/identity.rb +39 -0
  71. data/lib/mongoid/indexes.rb +30 -0
  72. data/lib/mongoid/javascript/functions.yml +37 -0
  73. data/lib/mongoid/javascript.rb +21 -0
  74. data/lib/mongoid/matchers/all.rb +11 -0
  75. data/lib/mongoid/matchers/default.rb +26 -0
  76. data/lib/mongoid/matchers/exists.rb +13 -0
  77. data/lib/mongoid/matchers/gt.rb +11 -0
  78. data/lib/mongoid/matchers/gte.rb +11 -0
  79. data/lib/mongoid/matchers/in.rb +11 -0
  80. data/lib/mongoid/matchers/lt.rb +11 -0
  81. data/lib/mongoid/matchers/lte.rb +11 -0
  82. data/lib/mongoid/matchers/ne.rb +11 -0
  83. data/lib/mongoid/matchers/nin.rb +11 -0
  84. data/lib/mongoid/matchers/size.rb +11 -0
  85. data/lib/mongoid/matchers.rb +36 -0
  86. data/lib/mongoid/memoization.rb +33 -0
  87. data/lib/mongoid/named_scope.rb +37 -0
  88. data/lib/mongoid/observable.rb +30 -0
  89. data/lib/mongoid/paths.rb +62 -0
  90. data/lib/mongoid/persistence/command.rb +39 -0
  91. data/lib/mongoid/persistence/insert.rb +50 -0
  92. data/lib/mongoid/persistence/insert_embedded.rb +38 -0
  93. data/lib/mongoid/persistence/remove.rb +39 -0
  94. data/lib/mongoid/persistence/remove_all.rb +37 -0
  95. data/lib/mongoid/persistence/remove_embedded.rb +50 -0
  96. data/lib/mongoid/persistence/update.rb +63 -0
  97. data/lib/mongoid/persistence.rb +222 -0
  98. data/lib/mongoid/scope.rb +75 -0
  99. data/lib/mongoid/state.rb +39 -0
  100. data/lib/mongoid/timestamps.rb +27 -0
  101. data/lib/mongoid/version.rb +4 -0
  102. data/lib/mongoid/versioning.rb +27 -0
  103. data/lib/mongoid.rb +122 -0
  104. metadata +298 -0
@@ -0,0 +1,191 @@
1
+ # encoding: utf-8
2
+ require "uri"
3
+
4
+ module Mongoid #:nodoc
5
+ class Config #:nodoc
6
+ include Singleton
7
+
8
+ attr_accessor \
9
+ :allow_dynamic_fields,
10
+ :reconnect_time,
11
+ :parameterize_keys,
12
+ :persist_in_safe_mode,
13
+ :raise_not_found_error,
14
+ :use_object_ids
15
+
16
+ # Defaults the configuration options to true.
17
+ def initialize
18
+ reset
19
+ end
20
+
21
+ # Sets whether the times returned from the database are in UTC or local time.
22
+ # If you omit this setting, then times will be returned in
23
+ # the local time zone.
24
+ #
25
+ # Example:
26
+ #
27
+ # <tt>Config.use_utc = true</tt>
28
+ #
29
+ # Returns:
30
+ #
31
+ # A boolean
32
+ def use_utc=(value)
33
+ @use_utc = value || false
34
+ end
35
+
36
+ # Returns whether times are return from the database in UTC. If
37
+ # this setting is false, then times will be returned in the local time zone.
38
+ #
39
+ # Example:
40
+ #
41
+ # <tt>Config.use_utc</tt>
42
+ #
43
+ # Returns:
44
+ #
45
+ # A boolean
46
+ attr_reader :use_utc
47
+ alias_method :use_utc?, :use_utc
48
+
49
+ # Sets the Mongo::DB master database to be used. If the object trying to be
50
+ # set is not a valid +Mongo::DB+, then an error will be raised.
51
+ #
52
+ # Example:
53
+ #
54
+ # <tt>Config.master = Mongo::Connection.db("test")</tt>
55
+ #
56
+ # Returns:
57
+ #
58
+ # The Master DB instance.
59
+ def master=(db)
60
+ check_database!(db)
61
+ @master = db
62
+ end
63
+
64
+ # Returns the master database, or if none has been set it will raise an
65
+ # error.
66
+ #
67
+ # Example:
68
+ #
69
+ # <tt>Config.master</tt>
70
+ #
71
+ # Returns:
72
+ #
73
+ # The master +Mongo::DB+
74
+ def master
75
+ @master || (raise Errors::InvalidDatabase.new(nil))
76
+ end
77
+
78
+ alias :database :master
79
+ alias :database= :master=
80
+
81
+ # Sets the Mongo::DB slave databases to be used. If the objects trying to me
82
+ # set are not valid +Mongo::DBs+, then an error will be raise.
83
+ #
84
+ # Example:
85
+ #
86
+ # <tt>Config.slaves = [ Mongo::Connection.db("test") ]</tt>
87
+ #
88
+ # Returns:
89
+ #
90
+ # The slaves DB instances.
91
+ def slaves=(dbs)
92
+ dbs.each do |db|
93
+ check_database!(db)
94
+ end
95
+ @slaves = dbs
96
+ end
97
+
98
+ # Returns the slave databases, or if none has been set nil
99
+ #
100
+ # Example:
101
+ #
102
+ # <tt>Config.slaves</tt>
103
+ #
104
+ # Returns:
105
+ #
106
+ # The slave +Mongo::DBs+
107
+ def slaves
108
+ @slaves
109
+ end
110
+
111
+ # Confiure mongoid from a hash that was usually parsed out of yml.
112
+ #
113
+ # Example:
114
+ #
115
+ # <tt>Mongoid::Config.instance.from_hash({})</tt>
116
+ def from_hash(settings)
117
+ _master(settings)
118
+ _slaves(settings)
119
+ settings.except("database").each_pair do |name, value|
120
+ send("#{name}=", value) if respond_to?(name)
121
+ end
122
+ end
123
+
124
+ # Reset the configuration options to the defaults.
125
+ #
126
+ # Example:
127
+ #
128
+ # <tt>config.reset</tt>
129
+ def reset
130
+ @allow_dynamic_fields = true
131
+ @parameterize_keys = true
132
+ @persist_in_safe_mode = true
133
+ @raise_not_found_error = true
134
+ @reconnect_time = 3
135
+ @use_object_ids = false
136
+ @time_zone = nil
137
+ end
138
+
139
+ protected
140
+
141
+ # Check if the database is valid and the correct version.
142
+ #
143
+ # Example:
144
+ #
145
+ # <tt>config.check_database!</tt>
146
+ def check_database!(database)
147
+ raise Errors::InvalidDatabase.new(database) unless database.kind_of?(Mongo::DB)
148
+ version = database.connection.server_version
149
+ raise Errors::UnsupportedVersion.new(version) if version < Mongoid::MONGODB_VERSION
150
+ end
151
+
152
+ # Get a Rails logger or stdout logger.
153
+ #
154
+ # Example:
155
+ #
156
+ # <tt>config.logger</tt>
157
+ def logger
158
+ defined?(Rails) ? Rails.logger : Logger.new($stdout)
159
+ end
160
+
161
+ # Get a master database from settings.
162
+ #
163
+ # Example:
164
+ #
165
+ # <tt>config._master({}, "test")</tt>
166
+ def _master(settings)
167
+ name = settings["database"]
168
+ host = settings.delete("host") || "localhost"
169
+ port = settings.delete("port") || 27017
170
+ self.master = Mongo::Connection.new(host, port, :logger => logger).db(name)
171
+ end
172
+
173
+ # Get a bunch-o-slaves from settings and names.
174
+ #
175
+ # Example:
176
+ #
177
+ # <tt>config._slaves({}, "test")</tt>
178
+ def _slaves(settings)
179
+ name = settings["database"]
180
+ self.slaves = []
181
+ slaves = settings.delete("slaves")
182
+ slaves.to_a.each do |slave|
183
+ self.slaves << Mongo::Connection.new(
184
+ slave["host"],
185
+ slave["port"],
186
+ :slave_ok => true
187
+ ).db(name)
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,151 @@
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 ||= documents.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(documents.select { |document| document.matches?(selector) })
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
+ documents.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 = documents.inject(nil) do |memo, doc|
125
+ value = doc.send(field)
126
+ memo ? memo += value : value
127
+ end
128
+ end
129
+
130
+ protected
131
+ # If the field exists, perform the comparison and set if true.
132
+ def determine(field, operator)
133
+ matching = documents.inject(nil) do |memo, doc|
134
+ value = doc.send(field)
135
+ (memo && memo.send(operator, value)) ? memo : value
136
+ end
137
+ end
138
+
139
+ # Limits the result set if skip and limit options.
140
+ def limit(documents)
141
+ skip, limit = options[:skip], options[:limit]
142
+ if skip && limit
143
+ return documents.slice(skip, limit)
144
+ elsif limit
145
+ return documents.first(limit)
146
+ end
147
+ documents
148
+ end
149
+ end
150
+ end
151
+ 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
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
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Paging
5
+ # Paginates the documents.
6
+ #
7
+ # Example:
8
+ #
9
+ # <tt>context.paginate(:page => 6, :per_page => 25)</tt>
10
+ #
11
+ # Returns:
12
+ #
13
+ # A collection of documents paginated.
14
+ # All previous <tt>limit</tt> and <tt>skip</tt> call will be ignored.
15
+ def paginate(pager_options={})
16
+ if pager_options[:per_page]
17
+ options[:limit] = pager_options[:per_page].to_i
18
+ if pager_options[:page]
19
+ options[:skip] = (pager_options[:page].to_i - 1) * pager_options[:per_page].to_i
20
+ end
21
+ end
22
+
23
+ @collection ||= execute(true)
24
+ WillPaginate::Collection.create(page, per_page, count) do |pager|
25
+ pager.replace(@collection.to_a)
26
+ end
27
+ end
28
+
29
+ # Either returns the page option and removes it from the options, or
30
+ # returns a default value of 1.
31
+ #
32
+ # Returns:
33
+ #
34
+ # An +Integer+ page number.
35
+ def page
36
+ skips, limits = options[:skip], options[:limit]
37
+ (skips && limits) ? (skips + limits) / limits : 1
38
+ end
39
+
40
+ # Get the number of results per page or the default of 20.
41
+ #
42
+ # Returns:
43
+ #
44
+ # The +Integer+ number of documents in each page.
45
+ def per_page
46
+ (options[:limit] || 20).to_i
47
+ end
48
+ end
49
+ end
50
+ end