djsun-mongo_mapper 0.5.6.6 → 0.5.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/Rakefile +13 -8
- data/VERSION +1 -1
- data/djsun-mongo_mapper.gemspec +17 -21
- data/lib/mongo_mapper/associations/base.rb +32 -36
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -2
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +19 -10
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +2 -2
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +21 -36
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +1 -1
- data/lib/mongo_mapper/associations/proxy.rb +3 -2
- data/lib/mongo_mapper/associations.rb +114 -8
- data/lib/mongo_mapper/callbacks.rb +18 -0
- data/lib/mongo_mapper/document.rb +173 -37
- data/lib/mongo_mapper/dynamic_finder.rb +1 -1
- data/lib/mongo_mapper/embedded_document.rb +9 -13
- data/lib/mongo_mapper/finder_options.rb +67 -44
- data/lib/mongo_mapper/pagination.rb +2 -0
- data/lib/mongo_mapper/serialization.rb +1 -1
- data/lib/mongo_mapper/serializers/json_serializer.rb +1 -1
- data/lib/mongo_mapper/support.rb +9 -0
- data/lib/mongo_mapper/validations.rb +12 -42
- data/lib/mongo_mapper.rb +11 -5
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +5 -5
- data/test/functional/associations/test_belongs_to_proxy.rb +29 -31
- data/test/functional/associations/test_many_documents_as_proxy.rb +5 -5
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +27 -3
- data/test/functional/associations/test_many_embedded_proxy.rb +58 -38
- data/test/functional/associations/test_many_polymorphic_proxy.rb +45 -3
- data/test/functional/associations/test_many_proxy.rb +61 -11
- data/test/functional/test_associations.rb +3 -3
- data/test/functional/test_binary.rb +1 -1
- data/test/functional/test_callbacks.rb +1 -1
- data/test/functional/test_dirty.rb +3 -3
- data/test/functional/test_document.rb +62 -58
- data/test/functional/test_embedded_document.rb +1 -1
- data/test/functional/test_pagination.rb +1 -1
- data/test/functional/test_rails_compatibility.rb +1 -1
- data/test/functional/test_validations.rb +46 -14
- data/test/models.rb +87 -35
- data/test/support/{test_timing.rb → timing.rb} +1 -1
- data/test/test_helper.rb +8 -13
- data/test/unit/serializers/test_json_serializer.rb +0 -4
- data/test/unit/test_association_base.rb +24 -8
- data/test/unit/test_document.rb +40 -71
- data/test/unit/test_embedded_document.rb +27 -67
- data/test/unit/test_finder_options.rb +16 -0
- data/test/unit/test_key.rb +5 -17
- data/test/unit/test_mongomapper.rb +2 -2
- data/test/unit/test_pagination.rb +4 -0
- metadata +10 -12
- data/mongo_mapper.gemspec +0 -170
- data/test/functional/associations/test_namespace.rb +0 -27
@@ -13,12 +13,12 @@ module MongoMapper
|
|
13
13
|
extend Validations::Macros
|
14
14
|
extend ClassMethods
|
15
15
|
extend Finders
|
16
|
-
|
16
|
+
|
17
17
|
def self.per_page
|
18
18
|
25
|
19
19
|
end unless respond_to?(:per_page)
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
descendants << model
|
23
23
|
end
|
24
24
|
|
@@ -32,18 +32,30 @@ module MongoMapper
|
|
32
32
|
create_indexes_for(key)
|
33
33
|
key
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def ensure_index(name_or_array, options={})
|
37
37
|
keys_to_index = if name_or_array.is_a?(Array)
|
38
38
|
name_or_array.map { |pair| [pair[0], pair[1]] }
|
39
39
|
else
|
40
40
|
name_or_array
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
MongoMapper.ensure_index(self, keys_to_index, options)
|
44
44
|
end
|
45
|
-
|
46
|
-
|
45
|
+
|
46
|
+
# @overload find(:first, options)
|
47
|
+
# @see Document.first
|
48
|
+
#
|
49
|
+
# @overload find(:last, options)
|
50
|
+
# @see Document.last
|
51
|
+
#
|
52
|
+
# @overload find(:all, options)
|
53
|
+
# @see Document.all
|
54
|
+
#
|
55
|
+
# @overload find(ids, options)
|
56
|
+
#
|
57
|
+
# @raise DocumentNotFound raised when no ID or arguments are provided
|
58
|
+
def find!(*args)
|
47
59
|
options = args.extract_options!
|
48
60
|
case args.first
|
49
61
|
when :first then first(options)
|
@@ -61,6 +73,12 @@ module MongoMapper
|
|
61
73
|
end
|
62
74
|
end
|
63
75
|
end
|
76
|
+
|
77
|
+
def find(*args)
|
78
|
+
find!(*args)
|
79
|
+
rescue DocumentNotFound
|
80
|
+
nil
|
81
|
+
end
|
64
82
|
|
65
83
|
def paginate(options)
|
66
84
|
per_page = options.delete(:per_page) || self.per_page
|
@@ -73,15 +91,39 @@ module MongoMapper
|
|
73
91
|
pagination
|
74
92
|
end
|
75
93
|
|
94
|
+
# @param [Hash] options any conditions understood by
|
95
|
+
# FinderOptions.to_mongo_criteria
|
96
|
+
#
|
97
|
+
# @return the first document in the ordered collection as described by
|
98
|
+
# +options+
|
99
|
+
#
|
100
|
+
# @see FinderOptions
|
76
101
|
def first(options={})
|
77
102
|
find_one(options)
|
78
103
|
end
|
79
104
|
|
105
|
+
# @param [Hash] options any conditions understood by
|
106
|
+
# FinderOptions.to_mongo_criteria
|
107
|
+
# @option [String] :order this *mandatory* option describes how to
|
108
|
+
# identify the ordering of the documents in your collection. Note that
|
109
|
+
# the *last* document in this collection will be selected.
|
110
|
+
#
|
111
|
+
# @return the last document in the ordered collection as described by
|
112
|
+
# +options+
|
113
|
+
#
|
114
|
+
# @raise Exception when no <tt>:order</tt> option has been defined
|
80
115
|
def last(options={})
|
81
116
|
raise ':order option must be provided when using last' if options[:order].blank?
|
82
117
|
find_one(options.merge(:order => invert_order_clause(options[:order])))
|
83
118
|
end
|
84
119
|
|
120
|
+
# @param [Hash] options any conditions understood by
|
121
|
+
# FinderOptions.to_mongo_criteria
|
122
|
+
#
|
123
|
+
# @return [Array] all documents in your collection that match the
|
124
|
+
# provided conditions
|
125
|
+
#
|
126
|
+
# @see FinderOptions
|
85
127
|
def all(options={})
|
86
128
|
find_every(options)
|
87
129
|
end
|
@@ -98,18 +140,51 @@ module MongoMapper
|
|
98
140
|
!count(options).zero?
|
99
141
|
end
|
100
142
|
|
143
|
+
# @overload create(doc_attributes)
|
144
|
+
# Create a single new document
|
145
|
+
# @param [Hash] doc_attributes key/value pairs to create a new
|
146
|
+
# document
|
147
|
+
#
|
148
|
+
# @overload create(docs_attributes)
|
149
|
+
# Create many new documents
|
150
|
+
# @param [Array<Hash>] provide many Hashes of key/value pairs to create
|
151
|
+
# multiple documents
|
152
|
+
#
|
153
|
+
# @example Creating a single document
|
154
|
+
# MyModel.create({ :foo => "bar" })
|
155
|
+
#
|
156
|
+
# @example Creating multiple documents
|
157
|
+
# MyModel.create([{ :foo => "bar" }, { :foo => "baz" })
|
158
|
+
#
|
159
|
+
# @return [Boolean] when a document is successfully created, +true+ will
|
160
|
+
# be returned. If a document fails to create, +false+ will be returned.
|
101
161
|
def create(*docs)
|
102
162
|
initialize_each(*docs) { |doc| doc.save }
|
103
163
|
end
|
104
164
|
|
165
|
+
# @see Document.create
|
166
|
+
#
|
167
|
+
# @raise [DocumentNotValid] raised if a document fails to create
|
105
168
|
def create!(*docs)
|
106
169
|
initialize_each(*docs) { |doc| doc.save! }
|
107
170
|
end
|
108
171
|
|
109
|
-
#
|
172
|
+
# @overload update(id, attributes)
|
173
|
+
# Update a single document
|
174
|
+
# @param id the ID of the document you wish to update
|
175
|
+
# @param [Hash] attributes the key to update on the document with a new
|
176
|
+
# value
|
177
|
+
#
|
178
|
+
# @overload update(ids_and_attributes)
|
179
|
+
# Update multiple documents
|
180
|
+
# @param [Hash] ids_and_attributes each key is the ID of some document
|
181
|
+
# you wish to update. The value each key points toward are those
|
182
|
+
# applied to the target document
|
183
|
+
#
|
184
|
+
# @example Updating single document
|
110
185
|
# Person.update(1, {:foo => 'bar'})
|
111
186
|
#
|
112
|
-
#
|
187
|
+
# @example Updating multiple documents at once:
|
113
188
|
# Person.update({'1' => {:foo => 'bar'}, '2' => {:baz => 'wick'}})
|
114
189
|
def update(*args)
|
115
190
|
if args.length == 1
|
@@ -120,6 +195,10 @@ module MongoMapper
|
|
120
195
|
end
|
121
196
|
end
|
122
197
|
|
198
|
+
# Removes ("deletes") one or many documents from the collection. Note
|
199
|
+
# that this will bypass any +destroy+ hooks defined by your class.
|
200
|
+
#
|
201
|
+
# @param [Array] ids the ID or IDs of the records you wish to delete
|
123
202
|
def delete(*ids)
|
124
203
|
collection.remove(to_criteria(:_id => ids.flatten))
|
125
204
|
end
|
@@ -128,6 +207,27 @@ module MongoMapper
|
|
128
207
|
collection.remove(to_criteria(options))
|
129
208
|
end
|
130
209
|
|
210
|
+
# Iterates over each document found by the provided IDs and calls their
|
211
|
+
# +destroy+ method. This has the advantage of processing your document's
|
212
|
+
# +destroy+ call-backs.
|
213
|
+
#
|
214
|
+
# @overload destroy(id)
|
215
|
+
# Destroy a single document by ID
|
216
|
+
# @param id the ID of the document to destroy
|
217
|
+
#
|
218
|
+
# @overload destroy(ids)
|
219
|
+
# Destroy many documents by their IDs
|
220
|
+
# @param [Array] the IDs of each document you wish to destroy
|
221
|
+
#
|
222
|
+
# @example Destroying a single document
|
223
|
+
# Person.destroy("34")
|
224
|
+
#
|
225
|
+
# @example Destroying multiple documents
|
226
|
+
# Person.destroy("34", "45", ..., "54")
|
227
|
+
#
|
228
|
+
# # OR...
|
229
|
+
#
|
230
|
+
# Person.destroy(["34", "45", ..., "54"])
|
131
231
|
def destroy(*ids)
|
132
232
|
find_some(ids.flatten).each(&:destroy)
|
133
233
|
end
|
@@ -136,6 +236,14 @@ module MongoMapper
|
|
136
236
|
all(options).each(&:destroy)
|
137
237
|
end
|
138
238
|
|
239
|
+
# @overload connection()
|
240
|
+
# @return [Mongo::Connection] the connection used by your document class
|
241
|
+
#
|
242
|
+
# @overload connection(mongo_connection)
|
243
|
+
# @param [Mongo::Connection] mongo_connection a new connection for your
|
244
|
+
# document class to use
|
245
|
+
# @return [Mongo::Connection] a new Mongo::Connection for yoru document
|
246
|
+
# class
|
139
247
|
def connection(mongo_connection=nil)
|
140
248
|
if mongo_connection.nil?
|
141
249
|
@connection ||= MongoMapper.connection
|
@@ -145,49 +253,73 @@ module MongoMapper
|
|
145
253
|
@connection
|
146
254
|
end
|
147
255
|
|
148
|
-
|
149
|
-
|
150
|
-
|
256
|
+
# Changes the database name from the default to whatever you want
|
257
|
+
#
|
258
|
+
# @param [#to_s] name the new database name to use.
|
259
|
+
def set_database_name(name)
|
260
|
+
@database_name = name
|
261
|
+
end
|
262
|
+
|
263
|
+
# Returns the database name
|
264
|
+
#
|
265
|
+
# @return [String] the database name
|
266
|
+
def database_name
|
267
|
+
@database_name
|
268
|
+
end
|
269
|
+
|
270
|
+
# Returns the database the document should use. Defaults to
|
271
|
+
# MongoMapper.database if other database is not set.
|
272
|
+
#
|
273
|
+
# @return [Mongo::DB] the mongo database instance
|
274
|
+
def database
|
275
|
+
if database_name.nil?
|
276
|
+
MongoMapper.database
|
151
277
|
else
|
152
|
-
|
278
|
+
connection.db(database_name)
|
153
279
|
end
|
154
|
-
@database
|
155
280
|
end
|
156
|
-
|
281
|
+
|
157
282
|
# Changes the collection name from the default to whatever you want
|
158
|
-
|
159
|
-
|
283
|
+
#
|
284
|
+
# @param [#to_s] name the new collection name to use.
|
285
|
+
def set_collection_name(name)
|
160
286
|
@collection_name = name
|
161
287
|
end
|
162
|
-
|
288
|
+
|
163
289
|
# Returns the collection name, if not set, defaults to class name tableized
|
290
|
+
#
|
291
|
+
# @return [String] the collection name, if not set, defaults to class
|
292
|
+
# name tableized
|
164
293
|
def collection_name
|
165
|
-
@collection_name ||= self.to_s.
|
294
|
+
@collection_name ||= self.to_s.tableize.gsub(/\//, '.')
|
166
295
|
end
|
167
296
|
|
168
|
-
#
|
297
|
+
# @return the Mongo Ruby driver +collection+ object
|
169
298
|
def collection
|
170
|
-
|
299
|
+
database.collection(collection_name)
|
171
300
|
end
|
172
301
|
|
302
|
+
# Defines a +created_at+ and +updated_at+ attribute (with a +Time+
|
303
|
+
# value) on your document. These attributes are updated by an
|
304
|
+
# injected +update_timestamps+ +before_save+ hook.
|
173
305
|
def timestamps!
|
174
306
|
key :created_at, Time
|
175
307
|
key :updated_at, Time
|
176
308
|
class_eval { before_save :update_timestamps }
|
177
309
|
end
|
178
|
-
|
310
|
+
|
179
311
|
def single_collection_inherited?
|
180
312
|
keys.has_key?('_type') && single_collection_inherited_superclass?
|
181
313
|
end
|
182
|
-
|
314
|
+
|
183
315
|
def single_collection_inherited_superclass?
|
184
316
|
superclass.respond_to?(:keys) && superclass.keys.has_key?('_type')
|
185
317
|
end
|
186
|
-
|
318
|
+
|
187
319
|
protected
|
188
320
|
def method_missing(method, *args)
|
189
321
|
finder = DynamicFinder.new(method)
|
190
|
-
|
322
|
+
|
191
323
|
if finder.found?
|
192
324
|
meta_def(finder.method) { |*args| dynamic_find(finder, args) }
|
193
325
|
send(finder.method, *args)
|
@@ -200,7 +332,7 @@ module MongoMapper
|
|
200
332
|
def create_indexes_for(key)
|
201
333
|
ensure_index key.name if key.options[:index]
|
202
334
|
end
|
203
|
-
|
335
|
+
|
204
336
|
def initialize_each(*docs)
|
205
337
|
instances = []
|
206
338
|
docs = [{}] if docs.blank?
|
@@ -211,7 +343,7 @@ module MongoMapper
|
|
211
343
|
end
|
212
344
|
instances.size == 1 ? instances[0] : instances
|
213
345
|
end
|
214
|
-
|
346
|
+
|
215
347
|
def initialize_doc(doc)
|
216
348
|
begin
|
217
349
|
klass = doc['_type'].present? ? doc['_type'].constantize : self
|
@@ -220,38 +352,38 @@ module MongoMapper
|
|
220
352
|
new(doc)
|
221
353
|
end
|
222
354
|
end
|
223
|
-
|
355
|
+
|
224
356
|
def find_every(options)
|
225
357
|
criteria, options = to_finder_options(options)
|
226
358
|
collection.find(criteria, options).to_a.map do |doc|
|
227
359
|
initialize_doc(doc)
|
228
360
|
end
|
229
361
|
end
|
230
|
-
|
362
|
+
|
231
363
|
def find_some(ids, options={})
|
232
364
|
ids = ids.flatten.compact.uniq
|
233
365
|
documents = find_every(options.merge(:_id => ids))
|
234
|
-
|
366
|
+
|
235
367
|
if ids.size == documents.size
|
236
368
|
documents
|
237
369
|
else
|
238
370
|
raise DocumentNotFound, "Couldn't find all of the ids (#{ids.to_sentence}). Found #{documents.size}, but was expecting #{ids.size}"
|
239
371
|
end
|
240
372
|
end
|
241
|
-
|
373
|
+
|
242
374
|
def find_one(options={})
|
243
375
|
criteria, options = to_finder_options(options)
|
244
376
|
if doc = collection.find_one(criteria, options)
|
245
377
|
initialize_doc(doc)
|
246
378
|
end
|
247
379
|
end
|
248
|
-
|
380
|
+
|
249
381
|
def find_one!(options={})
|
250
382
|
find_one(options) || raise(DocumentNotFound, "Document match #{options.inspect} does not exist in #{collection.name} collection")
|
251
383
|
end
|
252
384
|
|
253
385
|
def invert_order_clause(order)
|
254
|
-
order.split(',').map do |order_segment|
|
386
|
+
order.split(',').map do |order_segment|
|
255
387
|
if order_segment =~ /\sasc/i
|
256
388
|
order_segment.sub /\sasc/i, ' desc'
|
257
389
|
elsif order_segment =~ /\sdesc/i
|
@@ -281,17 +413,17 @@ module MongoMapper
|
|
281
413
|
docs.each_pair { |id, attrs| instances << update(id, attrs) }
|
282
414
|
instances
|
283
415
|
end
|
284
|
-
|
416
|
+
|
285
417
|
def to_criteria(options={})
|
286
418
|
FinderOptions.new(self, options).criteria
|
287
419
|
end
|
288
|
-
|
420
|
+
|
289
421
|
def to_finder_options(options={})
|
290
422
|
FinderOptions.new(self, options).to_a
|
291
423
|
end
|
292
424
|
end
|
293
425
|
|
294
|
-
module InstanceMethods
|
426
|
+
module InstanceMethods
|
295
427
|
def collection
|
296
428
|
self.class.collection
|
297
429
|
end
|
@@ -314,6 +446,10 @@ module MongoMapper
|
|
314
446
|
freeze
|
315
447
|
end
|
316
448
|
|
449
|
+
def reload
|
450
|
+
self.class.find(id)
|
451
|
+
end
|
452
|
+
|
317
453
|
private
|
318
454
|
def create_or_update
|
319
455
|
result = new? ? create : update
|
@@ -324,7 +460,7 @@ module MongoMapper
|
|
324
460
|
assign_id
|
325
461
|
save_to_collection
|
326
462
|
end
|
327
|
-
|
463
|
+
|
328
464
|
def assign_id
|
329
465
|
if read_attribute(:_id).blank?
|
330
466
|
write_attribute(:_id, Mongo::ObjectID.new.to_s)
|
@@ -345,7 +481,7 @@ module MongoMapper
|
|
345
481
|
write_attribute('created_at', now) if new?
|
346
482
|
write_attribute('updated_at', now)
|
347
483
|
end
|
348
|
-
|
484
|
+
|
349
485
|
def clear_custom_id_flag
|
350
486
|
@using_custom_id = nil
|
351
487
|
end
|
@@ -241,7 +241,7 @@ module MongoMapper
|
|
241
241
|
|
242
242
|
_keys.each_pair do |name, key|
|
243
243
|
value = key.set(read_attribute(key.name))
|
244
|
-
attrs[name] = value
|
244
|
+
attrs[name] = value unless value.nil?
|
245
245
|
end
|
246
246
|
|
247
247
|
embedded_associations.each do |association|
|
@@ -269,13 +269,7 @@ module MongoMapper
|
|
269
269
|
end
|
270
270
|
|
271
271
|
def ==(other)
|
272
|
-
|
273
|
-
ignore = { '_id' => nil }
|
274
|
-
attributes.merge(ignore) == other.attributes.merge(ignore)
|
275
|
-
end
|
276
|
-
|
277
|
-
def eql?(other)
|
278
|
-
other.is_a?(self.class) && (self == other)
|
272
|
+
other.is_a?(self.class) && id == other.id
|
279
273
|
end
|
280
274
|
|
281
275
|
def id
|
@@ -337,11 +331,13 @@ module MongoMapper
|
|
337
331
|
end
|
338
332
|
|
339
333
|
def read_attribute(name)
|
340
|
-
key = _keys[name]
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
334
|
+
if key = _keys[name]
|
335
|
+
value = key.get(instance_variable_get("@#{name}"))
|
336
|
+
instance_variable_set "@#{name}", value if !frozen?
|
337
|
+
value
|
338
|
+
else
|
339
|
+
raise KeyNotFound, "Could not find key: #{name.inspect}"
|
340
|
+
end
|
345
341
|
end
|
346
342
|
|
347
343
|
def read_attribute_before_typecast(name)
|
@@ -1,15 +1,35 @@
|
|
1
1
|
module MongoMapper
|
2
|
+
# Controls the parsing and handling of options used by finders.
|
3
|
+
#
|
4
|
+
# == Important Note
|
5
|
+
#
|
6
|
+
# This class is private to MongoMapper and should not be considered part of
|
7
|
+
# MongoMapper's public API. Some documentation herein, however, may prove
|
8
|
+
# useful for understanding how MongoMapper handles the parsing of finder
|
9
|
+
# conditions and options.
|
10
|
+
#
|
11
|
+
# @private
|
12
|
+
class FinderOperator
|
13
|
+
def initialize(field, operator)
|
14
|
+
@field, @operator = field, operator
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_criteria(value)
|
18
|
+
{@field => {@operator => value}}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
2
22
|
class FinderOptions
|
3
23
|
OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
|
4
|
-
|
5
|
-
attr_reader :model, :options
|
6
|
-
|
24
|
+
|
7
25
|
def initialize(model, options)
|
8
|
-
raise ArgumentError, "
|
26
|
+
raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
|
9
27
|
options.symbolize_keys!
|
10
28
|
|
11
|
-
@model
|
12
|
-
|
29
|
+
@model = model
|
30
|
+
@options = {}
|
31
|
+
@conditions = options.delete(:conditions) || {}
|
32
|
+
|
13
33
|
options.each_pair do |key, value|
|
14
34
|
if OptionKeys.include?(key)
|
15
35
|
@options[key] = value
|
@@ -17,37 +37,50 @@ module MongoMapper
|
|
17
37
|
@conditions[key] = value
|
18
38
|
end
|
19
39
|
end
|
40
|
+
|
41
|
+
add_sci_scope
|
20
42
|
end
|
21
43
|
|
44
|
+
# @return [Hash] Mongo compatible criteria options
|
45
|
+
#
|
46
|
+
# @see FinderOptions#to_mongo_criteria
|
22
47
|
def criteria
|
23
|
-
to_mongo_criteria(
|
48
|
+
to_mongo_criteria(@conditions)
|
24
49
|
end
|
25
50
|
|
51
|
+
# @return [Hash] Mongo compatible options
|
26
52
|
def options
|
27
|
-
|
53
|
+
options = @options.dup
|
54
|
+
|
55
|
+
fields = options.delete(:fields) || options.delete(:select)
|
56
|
+
skip = options.delete(:skip) || options.delete(:offset) || 0
|
57
|
+
limit = options.delete(:limit) || 0
|
58
|
+
sort = options.delete(:sort) || convert_order_to_sort(options.delete(:order))
|
59
|
+
|
60
|
+
{:fields => to_mongo_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
|
28
61
|
end
|
29
62
|
|
63
|
+
# @return [Array<Hash>] Mongo criteria and options enclosed in an Array
|
30
64
|
def to_a
|
31
65
|
[criteria, options]
|
32
66
|
end
|
33
|
-
|
67
|
+
|
34
68
|
private
|
35
|
-
def to_mongo_criteria(
|
69
|
+
def to_mongo_criteria(conditions, parent_key=nil)
|
36
70
|
criteria = {}
|
37
|
-
add_sci_scope(model, criteria)
|
38
71
|
|
39
72
|
conditions.each_pair do |field, value|
|
40
|
-
field =
|
73
|
+
field = normalized_field(field)
|
74
|
+
if field.is_a?(FinderOperator)
|
75
|
+
criteria.merge!(field.to_criteria(value))
|
76
|
+
next
|
77
|
+
end
|
41
78
|
case value
|
42
79
|
when Array
|
43
|
-
operator_present = field.to_s =~ /^\$/
|
44
|
-
criteria[field] =
|
45
|
-
value
|
46
|
-
else
|
47
|
-
{'$in' => value}
|
48
|
-
end
|
80
|
+
operator_present = field.to_s =~ /^\$/
|
81
|
+
criteria[field] = operator?(field) ? value : {'$in' => value}
|
49
82
|
when Hash
|
50
|
-
criteria[field] = to_mongo_criteria(
|
83
|
+
criteria[field] = to_mongo_criteria(value, field)
|
51
84
|
else
|
52
85
|
criteria[field] = value
|
53
86
|
end
|
@@ -55,48 +88,38 @@ module MongoMapper
|
|
55
88
|
|
56
89
|
criteria
|
57
90
|
end
|
58
|
-
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
91
|
+
|
92
|
+
def operator?(field)
|
93
|
+
field.to_s =~ /^\$/
|
94
|
+
end
|
95
|
+
|
96
|
+
def normalized_field(field)
|
97
|
+
field.to_s == 'id' ? :_id : field
|
65
98
|
end
|
66
99
|
|
67
100
|
# adds _type single collection inheritance scope for models that need it
|
68
|
-
def add_sci_scope
|
69
|
-
if model.single_collection_inherited?
|
70
|
-
|
101
|
+
def add_sci_scope
|
102
|
+
if @model.single_collection_inherited?
|
103
|
+
@conditions[:_type] = @model.to_s
|
71
104
|
end
|
72
105
|
end
|
73
106
|
|
74
|
-
def to_mongo_options(model, options)
|
75
|
-
options = options.dup
|
76
|
-
{
|
77
|
-
:fields => to_mongo_fields(options.delete(:fields) || options.delete(:select)),
|
78
|
-
:skip => (options.delete(:skip) || options.delete(:offset) || 0).to_i,
|
79
|
-
:limit => (options.delete(:limit) || 0).to_i,
|
80
|
-
:sort => options.delete(:sort) || to_mongo_sort(options.delete(:order))
|
81
|
-
}
|
82
|
-
end
|
83
|
-
|
84
107
|
def to_mongo_fields(fields)
|
85
108
|
return if fields.blank?
|
86
|
-
|
109
|
+
|
87
110
|
if fields.is_a?(String)
|
88
111
|
fields.split(',').map { |field| field.strip }
|
89
112
|
else
|
90
113
|
fields.flatten.compact
|
91
114
|
end
|
92
115
|
end
|
93
|
-
|
94
|
-
def
|
116
|
+
|
117
|
+
def convert_order_to_sort(sort)
|
95
118
|
return if sort.blank?
|
96
119
|
pieces = sort.split(',')
|
97
120
|
pieces.map { |s| to_mongo_sort_piece(s) }
|
98
121
|
end
|
99
|
-
|
122
|
+
|
100
123
|
def to_mongo_sort_piece(str)
|
101
124
|
field, direction = str.strip.split(' ')
|
102
125
|
direction ||= 'ASC'
|
@@ -104,4 +127,4 @@ module MongoMapper
|
|
104
127
|
[field, direction]
|
105
128
|
end
|
106
129
|
end
|
107
|
-
end
|
130
|
+
end
|
@@ -49,7 +49,7 @@ module MongoMapper #:nodoc:
|
|
49
49
|
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
50
50
|
# "created_at": "2006/08/01", "awesome": true,
|
51
51
|
# "permalink": "1-konata-izumi"}
|
52
|
-
def to_json(options
|
52
|
+
def to_json(options={})
|
53
53
|
apply_to_json_defaults(options)
|
54
54
|
|
55
55
|
if include_root_in_json
|
data/lib/mongo_mapper/support.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
class BasicObject #:nodoc:
|
2
|
+
alias_method :proxy_extend, :extend
|
2
3
|
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|^methods$|instance_eval|proxy_|^object_id$)/ }
|
3
4
|
end unless defined?(BasicObject)
|
4
5
|
|
@@ -129,6 +130,14 @@ class String
|
|
129
130
|
end
|
130
131
|
end
|
131
132
|
|
133
|
+
class Symbol
|
134
|
+
%w{gt lt gte lte ne in nin mod size where exists}.each do |operator|
|
135
|
+
define_method operator do
|
136
|
+
MongoMapper::FinderOperator.new(self, "$#{operator}")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
132
141
|
class Time
|
133
142
|
def self.to_mongo(value)
|
134
143
|
if value.nil? || value == ''
|