djsun-mongo_mapper 0.5.6.6 → 0.5.8.1
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.
- 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 == ''
|