danielharan-mongo_mapper 0.6.5
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 +10 -0
- data/LICENSE +20 -0
- data/README.rdoc +53 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/bin/mmconsole +60 -0
- data/lib/mongo_mapper.rb +134 -0
- data/lib/mongo_mapper/associations.rb +183 -0
- data/lib/mongo_mapper/associations/base.rb +110 -0
- data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +25 -0
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +127 -0
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +53 -0
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
- data/lib/mongo_mapper/associations/proxy.rb +80 -0
- data/lib/mongo_mapper/callbacks.rb +109 -0
- data/lib/mongo_mapper/dirty.rb +136 -0
- data/lib/mongo_mapper/document.rb +481 -0
- data/lib/mongo_mapper/dynamic_finder.rb +35 -0
- data/lib/mongo_mapper/embedded_document.rb +386 -0
- data/lib/mongo_mapper/finder_options.rb +133 -0
- data/lib/mongo_mapper/key.rb +36 -0
- data/lib/mongo_mapper/observing.rb +50 -0
- data/lib/mongo_mapper/pagination.rb +53 -0
- data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
- data/lib/mongo_mapper/serialization.rb +54 -0
- data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
- data/lib/mongo_mapper/support.rb +193 -0
- data/lib/mongo_mapper/validations.rb +41 -0
- data/mongo_mapper.gemspec +171 -0
- data/specs.watchr +32 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +48 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +196 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
- data/test/functional/associations/test_many_proxy.rb +384 -0
- data/test/functional/test_associations.rb +44 -0
- data/test/functional/test_binary.rb +18 -0
- data/test/functional/test_callbacks.rb +85 -0
- data/test/functional/test_dirty.rb +159 -0
- data/test/functional/test_document.rb +1180 -0
- data/test/functional/test_embedded_document.rb +125 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_pagination.rb +95 -0
- data/test/functional/test_rails_compatibility.rb +25 -0
- data/test/functional/test_string_id_compatibility.rb +72 -0
- data/test/functional/test_validations.rb +369 -0
- data/test/models.rb +271 -0
- data/test/support/custom_matchers.rb +55 -0
- data/test/support/timing.rb +16 -0
- data/test/test_helper.rb +27 -0
- data/test/unit/serializers/test_json_serializer.rb +189 -0
- data/test/unit/test_association_base.rb +166 -0
- data/test/unit/test_document.rb +204 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +718 -0
- data/test/unit/test_finder_options.rb +296 -0
- data/test/unit/test_key.rb +172 -0
- data/test/unit/test_mongo_mapper.rb +65 -0
- data/test/unit/test_observing.rb +101 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +49 -0
- data/test/unit/test_serializations.rb +52 -0
- data/test/unit/test_support.rb +342 -0
- data/test/unit/test_time_zones.rb +40 -0
- data/test/unit/test_validations.rb +503 -0
- metadata +233 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
class DynamicFinder
|
3
|
+
attr_reader :method, :attributes, :finder, :bang, :instantiator
|
4
|
+
|
5
|
+
def initialize(method)
|
6
|
+
@method = method
|
7
|
+
@finder = :first
|
8
|
+
@bang = false
|
9
|
+
match()
|
10
|
+
end
|
11
|
+
|
12
|
+
def found?
|
13
|
+
@finder.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def match
|
18
|
+
case method.to_s
|
19
|
+
when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
|
20
|
+
@finder = :all if $1 == 'all_by'
|
21
|
+
names = $2
|
22
|
+
when /^find_by_([_a-zA-Z]\w*)\!$/
|
23
|
+
@bang = true
|
24
|
+
names = $1
|
25
|
+
when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
|
26
|
+
@instantiator = $1 == 'initialize' ? :new : :create
|
27
|
+
names = $2
|
28
|
+
else
|
29
|
+
@finder = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
@attributes = names && names.split('_and_')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,386 @@
|
|
1
|
+
require 'observer'
|
2
|
+
|
3
|
+
module MongoMapper
|
4
|
+
module EmbeddedDocument
|
5
|
+
def self.included(model)
|
6
|
+
model.class_eval do
|
7
|
+
extend ClassMethods
|
8
|
+
include InstanceMethods
|
9
|
+
|
10
|
+
extend Associations::ClassMethods
|
11
|
+
include Associations::InstanceMethods
|
12
|
+
|
13
|
+
include RailsCompatibility::EmbeddedDocument
|
14
|
+
include Validatable
|
15
|
+
include Serialization
|
16
|
+
|
17
|
+
extend Validations::Macros
|
18
|
+
|
19
|
+
key :_id, ObjectId
|
20
|
+
attr_accessor :_root_document
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def logger
|
26
|
+
MongoMapper.logger
|
27
|
+
end
|
28
|
+
|
29
|
+
def inherited(subclass)
|
30
|
+
unless subclass.embeddable?
|
31
|
+
subclass.set_collection_name(collection_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
(@subclasses ||= []) << subclass
|
35
|
+
end
|
36
|
+
|
37
|
+
def subclasses
|
38
|
+
@subclasses
|
39
|
+
end
|
40
|
+
|
41
|
+
def keys
|
42
|
+
@keys ||= if parent = parent_model
|
43
|
+
parent.keys.dup
|
44
|
+
else
|
45
|
+
HashWithIndifferentAccess.new
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def key(*args)
|
50
|
+
key = Key.new(*args)
|
51
|
+
keys[key.name] = key
|
52
|
+
|
53
|
+
create_accessors_for(key)
|
54
|
+
create_key_in_subclasses(*args)
|
55
|
+
create_validations_for(key)
|
56
|
+
|
57
|
+
key
|
58
|
+
end
|
59
|
+
|
60
|
+
def using_object_id?
|
61
|
+
object_id_key?(:_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def object_id_key?(name)
|
65
|
+
key = keys[name.to_s]
|
66
|
+
key && key.type == ObjectId
|
67
|
+
end
|
68
|
+
|
69
|
+
def embeddable?
|
70
|
+
!self.ancestors.include?(Document)
|
71
|
+
end
|
72
|
+
|
73
|
+
def parent_model
|
74
|
+
(ancestors - [self,EmbeddedDocument]).find do |parent_class|
|
75
|
+
parent_class.ancestors.include?(EmbeddedDocument)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_mongo(instance)
|
80
|
+
return nil if instance.nil?
|
81
|
+
instance.to_mongo
|
82
|
+
end
|
83
|
+
|
84
|
+
def from_mongo(instance_or_hash)
|
85
|
+
return nil if instance_or_hash.nil?
|
86
|
+
|
87
|
+
if instance_or_hash.is_a?(self)
|
88
|
+
instance_or_hash
|
89
|
+
else
|
90
|
+
initialize_doc(instance_or_hash)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def initialize_doc(doc)
|
96
|
+
begin
|
97
|
+
klass = doc['_type'].present? ? doc['_type'].constantize : self
|
98
|
+
klass.new(doc)
|
99
|
+
rescue NameError
|
100
|
+
new(doc)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def accessors_module
|
105
|
+
module_defined = if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
|
106
|
+
const_defined?('MongoMapperKeys')
|
107
|
+
else
|
108
|
+
const_defined?('MongoMapperKeys', false)
|
109
|
+
end
|
110
|
+
|
111
|
+
if module_defined
|
112
|
+
const_get 'MongoMapperKeys'
|
113
|
+
else
|
114
|
+
const_set 'MongoMapperKeys', Module.new
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def create_accessors_for(key)
|
119
|
+
accessors_module.module_eval <<-end_eval
|
120
|
+
def #{key.name}
|
121
|
+
read_attribute(:'#{key.name}')
|
122
|
+
end
|
123
|
+
|
124
|
+
def #{key.name}_before_typecast
|
125
|
+
read_attribute_before_typecast(:'#{key.name}')
|
126
|
+
end
|
127
|
+
|
128
|
+
def #{key.name}=(value)
|
129
|
+
write_attribute(:'#{key.name}', value)
|
130
|
+
end
|
131
|
+
|
132
|
+
def #{key.name}?
|
133
|
+
read_attribute(:#{key.name}).present?
|
134
|
+
end
|
135
|
+
end_eval
|
136
|
+
include accessors_module
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_key_in_subclasses(*args)
|
140
|
+
return if subclasses.blank?
|
141
|
+
|
142
|
+
subclasses.each do |subclass|
|
143
|
+
subclass.key(*args)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def create_validations_for(key)
|
148
|
+
attribute = key.name.to_sym
|
149
|
+
|
150
|
+
if key.options[:required]
|
151
|
+
validates_presence_of(attribute)
|
152
|
+
end
|
153
|
+
|
154
|
+
if key.options[:unique]
|
155
|
+
validates_uniqueness_of(attribute)
|
156
|
+
end
|
157
|
+
|
158
|
+
if key.options[:numeric]
|
159
|
+
number_options = key.type == Integer ? {:only_integer => true} : {}
|
160
|
+
validates_numericality_of(attribute, number_options)
|
161
|
+
end
|
162
|
+
|
163
|
+
if key.options[:format]
|
164
|
+
validates_format_of(attribute, :with => key.options[:format])
|
165
|
+
end
|
166
|
+
|
167
|
+
if key.options[:length]
|
168
|
+
length_options = case key.options[:length]
|
169
|
+
when Integer
|
170
|
+
{:minimum => 0, :maximum => key.options[:length]}
|
171
|
+
when Range
|
172
|
+
{:within => key.options[:length]}
|
173
|
+
when Hash
|
174
|
+
key.options[:length]
|
175
|
+
end
|
176
|
+
validates_length_of(attribute, length_options)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
module InstanceMethods
|
182
|
+
def logger
|
183
|
+
self.class.logger
|
184
|
+
end
|
185
|
+
|
186
|
+
def initialize(attrs={})
|
187
|
+
unless attrs.nil?
|
188
|
+
self.class.associations.each_pair do |name, association|
|
189
|
+
if collection = attrs.delete(name)
|
190
|
+
if association.many? && association.klass.embeddable?
|
191
|
+
root_document = attrs[:_root_document] || self
|
192
|
+
collection.each do |doc|
|
193
|
+
doc[:_root_document] = root_document
|
194
|
+
end
|
195
|
+
end
|
196
|
+
send("#{association.name}=", collection)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
self.attributes = attrs
|
201
|
+
|
202
|
+
if respond_to?(:_type=) && self['_type'].blank?
|
203
|
+
self._type = self.class.name
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
if self.class.embeddable?
|
208
|
+
if read_attribute(:_id).blank?
|
209
|
+
write_attribute :_id, Mongo::ObjectID.new
|
210
|
+
@new_document = true
|
211
|
+
else
|
212
|
+
@new_document = false
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def new?
|
218
|
+
!!@new_document
|
219
|
+
end
|
220
|
+
|
221
|
+
def to_param
|
222
|
+
id.to_s
|
223
|
+
end
|
224
|
+
|
225
|
+
def attributes=(attrs)
|
226
|
+
return if attrs.blank?
|
227
|
+
attrs.each_pair do |name, value|
|
228
|
+
writer_method = "#{name}="
|
229
|
+
|
230
|
+
if respond_to?(writer_method)
|
231
|
+
self.send(writer_method, value)
|
232
|
+
else
|
233
|
+
self[name.to_s] = value
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def attributes
|
239
|
+
attrs = HashWithIndifferentAccess.new
|
240
|
+
|
241
|
+
embedded_keys.each do |key|
|
242
|
+
attrs[key.name] = read_attribute(key.name).try(:attributes)
|
243
|
+
end
|
244
|
+
|
245
|
+
non_embedded_keys.each do |key|
|
246
|
+
attrs[key.name] = read_attribute(key.name)
|
247
|
+
end
|
248
|
+
|
249
|
+
embedded_associations.each do |association|
|
250
|
+
documents = instance_variable_get(association.ivar)
|
251
|
+
next if documents.nil?
|
252
|
+
attrs[association.name] = documents.collect { |doc| doc.attributes }
|
253
|
+
end
|
254
|
+
|
255
|
+
attrs
|
256
|
+
end
|
257
|
+
|
258
|
+
def to_mongo
|
259
|
+
attrs = HashWithIndifferentAccess.new
|
260
|
+
|
261
|
+
_keys.each_pair do |name, key|
|
262
|
+
value = key.set(read_attribute(key.name))
|
263
|
+
attrs[name] = value unless value.nil?
|
264
|
+
end
|
265
|
+
|
266
|
+
embedded_associations.each do |association|
|
267
|
+
if documents = instance_variable_get(association.ivar)
|
268
|
+
attrs[association.name] = documents.map { |document| document.to_mongo }
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
attrs
|
273
|
+
end
|
274
|
+
|
275
|
+
def clone
|
276
|
+
clone_attributes = self.attributes
|
277
|
+
clone_attributes.delete("_id")
|
278
|
+
self.class.new(clone_attributes)
|
279
|
+
end
|
280
|
+
|
281
|
+
def [](name)
|
282
|
+
read_attribute(name)
|
283
|
+
end
|
284
|
+
|
285
|
+
def []=(name, value)
|
286
|
+
ensure_key_exists(name)
|
287
|
+
write_attribute(name, value)
|
288
|
+
end
|
289
|
+
|
290
|
+
def ==(other)
|
291
|
+
other.is_a?(self.class) && _id == other._id
|
292
|
+
end
|
293
|
+
|
294
|
+
def id
|
295
|
+
read_attribute(:_id)
|
296
|
+
end
|
297
|
+
|
298
|
+
def id=(value)
|
299
|
+
if self.class.using_object_id?
|
300
|
+
value = MongoMapper.normalize_object_id(value)
|
301
|
+
else
|
302
|
+
@using_custom_id = true
|
303
|
+
end
|
304
|
+
|
305
|
+
write_attribute :_id, value
|
306
|
+
end
|
307
|
+
|
308
|
+
def using_custom_id?
|
309
|
+
!!@using_custom_id
|
310
|
+
end
|
311
|
+
|
312
|
+
def inspect
|
313
|
+
attributes_as_nice_string = key_names.collect do |name|
|
314
|
+
"#{name}: #{read_attribute(name).inspect}"
|
315
|
+
end.join(", ")
|
316
|
+
"#<#{self.class} #{attributes_as_nice_string}>"
|
317
|
+
end
|
318
|
+
|
319
|
+
def save
|
320
|
+
if _root_document
|
321
|
+
_root_document.save
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def save!
|
326
|
+
if _root_document
|
327
|
+
_root_document.save!
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def update_attributes(attrs={})
|
332
|
+
self.attributes = attrs
|
333
|
+
save
|
334
|
+
end
|
335
|
+
|
336
|
+
private
|
337
|
+
def _keys
|
338
|
+
self.metaclass.keys
|
339
|
+
end
|
340
|
+
|
341
|
+
def key_names
|
342
|
+
_keys.keys
|
343
|
+
end
|
344
|
+
|
345
|
+
def non_embedded_keys
|
346
|
+
_keys.values.select { |key| !key.embeddable? }
|
347
|
+
end
|
348
|
+
|
349
|
+
def embedded_keys
|
350
|
+
_keys.values.select { |key| key.embeddable? }
|
351
|
+
end
|
352
|
+
|
353
|
+
def ensure_key_exists(name)
|
354
|
+
self.metaclass.key(name) unless respond_to?("#{name}=")
|
355
|
+
end
|
356
|
+
|
357
|
+
def read_attribute(name)
|
358
|
+
if key = _keys[name]
|
359
|
+
value = key.get(instance_variable_get("@#{name}"))
|
360
|
+
instance_variable_set "@#{name}", value if !frozen?
|
361
|
+
value
|
362
|
+
else
|
363
|
+
raise KeyNotFound, "Could not find key: #{name.inspect}"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def read_attribute_before_typecast(name)
|
368
|
+
instance_variable_get("@#{name}_before_typecast")
|
369
|
+
end
|
370
|
+
|
371
|
+
def write_attribute(name, value)
|
372
|
+
key = _keys[name]
|
373
|
+
instance_variable_set "@#{name}_before_typecast", value
|
374
|
+
instance_variable_set "@#{name}", key.set(value)
|
375
|
+
end
|
376
|
+
|
377
|
+
def embedded_associations
|
378
|
+
self.class.associations.select do |name, association|
|
379
|
+
association.embeddable?
|
380
|
+
end.map do |name, association|
|
381
|
+
association
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end # InstanceMethods
|
385
|
+
end # EmbeddedDocument
|
386
|
+
end # MongoMapper
|
@@ -0,0 +1,133 @@
|
|
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 FinderOptions
|
13
|
+
OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
|
14
|
+
|
15
|
+
def initialize(model, options)
|
16
|
+
raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
|
17
|
+
options = options.symbolize_keys
|
18
|
+
|
19
|
+
@model = model
|
20
|
+
@options = {}
|
21
|
+
@conditions = options.delete(:conditions) || {}
|
22
|
+
|
23
|
+
options.each_pair do |key, value|
|
24
|
+
if OptionKeys.include?(key)
|
25
|
+
@options[key] = value
|
26
|
+
else
|
27
|
+
@conditions[key] = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
add_sci_scope
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Hash] Mongo compatible criteria options
|
35
|
+
#
|
36
|
+
# @see FinderOptions#to_mongo_criteria
|
37
|
+
def criteria
|
38
|
+
to_mongo_criteria(@conditions)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash] Mongo compatible options
|
42
|
+
def options
|
43
|
+
fields = @options.delete(:fields) || @options.delete(:select)
|
44
|
+
skip = @options.delete(:skip) || @options.delete(:offset) || 0
|
45
|
+
limit = @options.delete(:limit) || 0
|
46
|
+
sort = @options.delete(:sort) || convert_order_to_sort(@options.delete(:order))
|
47
|
+
|
48
|
+
{:fields => to_mongo_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Array<Hash>] Mongo criteria and options enclosed in an Array
|
52
|
+
def to_a
|
53
|
+
[criteria, options]
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def to_mongo_criteria(conditions, parent_key=nil)
|
58
|
+
criteria = {}
|
59
|
+
|
60
|
+
conditions.each_pair do |field, value|
|
61
|
+
field = normalized_field(field)
|
62
|
+
|
63
|
+
if @model.object_id_key?(field) && value.is_a?(String)
|
64
|
+
value = Mongo::ObjectID.from_string(value)
|
65
|
+
end
|
66
|
+
|
67
|
+
if field.is_a?(FinderOperator)
|
68
|
+
criteria.merge!(field.to_criteria(value))
|
69
|
+
next
|
70
|
+
end
|
71
|
+
|
72
|
+
case value
|
73
|
+
when Array
|
74
|
+
criteria[field] = operator?(field) ? value : {'$in' => value}
|
75
|
+
when Hash
|
76
|
+
criteria[field] = to_mongo_criteria(value, field)
|
77
|
+
else
|
78
|
+
criteria[field] = value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
criteria
|
83
|
+
end
|
84
|
+
|
85
|
+
def operator?(field)
|
86
|
+
field.to_s =~ /^\$/
|
87
|
+
end
|
88
|
+
|
89
|
+
def normalized_field(field)
|
90
|
+
field.to_s == 'id' ? :_id : field
|
91
|
+
end
|
92
|
+
|
93
|
+
# adds _type single collection inheritance scope for models that need it
|
94
|
+
def add_sci_scope
|
95
|
+
if @model.single_collection_inherited?
|
96
|
+
@conditions[:_type] = @model.to_s
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_mongo_fields(fields)
|
101
|
+
return if fields.blank?
|
102
|
+
|
103
|
+
if fields.is_a?(String)
|
104
|
+
fields.split(',').map { |field| field.strip }
|
105
|
+
else
|
106
|
+
fields.flatten.compact
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def convert_order_to_sort(sort)
|
111
|
+
return if sort.blank?
|
112
|
+
pieces = sort.split(',')
|
113
|
+
pieces.map { |s| to_mongo_sort_piece(s) }
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_mongo_sort_piece(str)
|
117
|
+
field, direction = str.strip.split(' ')
|
118
|
+
direction ||= 'ASC'
|
119
|
+
direction = direction.upcase == 'ASC' ? 1 : -1
|
120
|
+
[field, direction]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class FinderOperator
|
125
|
+
def initialize(field, operator)
|
126
|
+
@field, @operator = field, operator
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_criteria(value)
|
130
|
+
{@field => {@operator => value}}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|