mongo_mapper_ign 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/LICENSE +20 -0
- data/README.rdoc +35 -0
- data/Rakefile +37 -0
- data/bin/mmconsole +60 -0
- data/lib/mongo_mapper.rb +116 -0
- data/lib/mongo_mapper/document.rb +313 -0
- data/lib/mongo_mapper/embedded_document.rb +70 -0
- data/lib/mongo_mapper/plugins.rb +35 -0
- data/lib/mongo_mapper/plugins/associations.rb +114 -0
- data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
- data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
- data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +39 -0
- data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +144 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +129 -0
- data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
- data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
- data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
- data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +41 -0
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +69 -0
- data/lib/mongo_mapper/plugins/associations/proxy.rb +124 -0
- data/lib/mongo_mapper/plugins/callbacks.rb +240 -0
- data/lib/mongo_mapper/plugins/clone.rb +13 -0
- data/lib/mongo_mapper/plugins/descendants.rb +16 -0
- data/lib/mongo_mapper/plugins/dirty.rb +119 -0
- data/lib/mongo_mapper/plugins/equality.rb +23 -0
- data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
- data/lib/mongo_mapper/plugins/inspect.rb +14 -0
- data/lib/mongo_mapper/plugins/keys.rb +345 -0
- data/lib/mongo_mapper/plugins/logger.rb +17 -0
- data/lib/mongo_mapper/plugins/modifiers.rb +107 -0
- data/lib/mongo_mapper/plugins/pagination.rb +24 -0
- data/lib/mongo_mapper/plugins/pagination/proxy.rb +72 -0
- data/lib/mongo_mapper/plugins/persistence.rb +68 -0
- data/lib/mongo_mapper/plugins/protected.rb +45 -0
- data/lib/mongo_mapper/plugins/rails.rb +57 -0
- data/lib/mongo_mapper/plugins/serialization.rb +91 -0
- data/lib/mongo_mapper/plugins/serialization/array.rb +56 -0
- data/lib/mongo_mapper/plugins/serialization/xml_serializer.rb +240 -0
- data/lib/mongo_mapper/plugins/timestamps.rb +21 -0
- data/lib/mongo_mapper/plugins/userstamps.rb +14 -0
- data/lib/mongo_mapper/plugins/validations.rb +46 -0
- data/lib/mongo_mapper/query.rb +143 -0
- data/lib/mongo_mapper/support.rb +218 -0
- data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
- data/lib/mongo_mapper/support/find.rb +77 -0
- data/lib/mongo_mapper/version.rb +3 -0
- data/mongo_mapper.gemspec +214 -0
- data/mongo_mapper_ign.gemspec +217 -0
- data/performance/read_write.rb +52 -0
- data/specs.watchr +51 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/active_model_lint_test.rb +13 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +101 -0
- data/test/functional/associations/test_in_array_proxy.rb +325 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
- data/test/functional/associations/test_many_documents_proxy.rb +536 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
- data/test/functional/associations/test_one_embedded_proxy.rb +68 -0
- data/test/functional/associations/test_one_proxy.rb +196 -0
- data/test/functional/test_associations.rb +44 -0
- data/test/functional/test_binary.rb +27 -0
- data/test/functional/test_callbacks.rb +151 -0
- data/test/functional/test_dirty.rb +163 -0
- data/test/functional/test_document.rb +1219 -0
- data/test/functional/test_embedded_document.rb +210 -0
- data/test/functional/test_identity_map.rb +507 -0
- data/test/functional/test_indexing.rb +44 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_modifiers.rb +394 -0
- data/test/functional/test_pagination.rb +93 -0
- data/test/functional/test_protected.rb +163 -0
- data/test/functional/test_string_id_compatibility.rb +67 -0
- data/test/functional/test_timestamps.rb +64 -0
- data/test/functional/test_userstamps.rb +28 -0
- data/test/functional/test_validations.rb +342 -0
- data/test/models.rb +227 -0
- data/test/support/custom_matchers.rb +37 -0
- data/test/support/timing.rb +16 -0
- data/test/test_helper.rb +64 -0
- data/test/unit/associations/test_base.rb +212 -0
- data/test/unit/associations/test_proxy.rb +105 -0
- data/test/unit/serializers/test_json_serializer.rb +202 -0
- data/test/unit/test_descendant_appends.rb +71 -0
- data/test/unit/test_document.rb +225 -0
- data/test/unit/test_dynamic_finder.rb +123 -0
- data/test/unit/test_embedded_document.rb +657 -0
- data/test/unit/test_keys.rb +185 -0
- data/test/unit/test_mongo_mapper.rb +118 -0
- data/test/unit/test_pagination.rb +160 -0
- data/test/unit/test_plugins.rb +50 -0
- data/test/unit/test_query.rb +374 -0
- data/test/unit/test_rails.rb +181 -0
- data/test/unit/test_rails_compatibility.rb +52 -0
- data/test/unit/test_serialization.rb +51 -0
- data/test/unit/test_support.rb +382 -0
- data/test/unit/test_time_zones.rb +39 -0
- data/test/unit/test_validations.rb +544 -0
- metadata +327 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# Credit: http://github.com/tsxn26/array-xml_serialization
|
2
|
+
|
3
|
+
# Extends the XML serialization support in activesupport to allow for
|
4
|
+
# arrays containing strings, symbols, and integers.
|
5
|
+
|
6
|
+
# Forces elements in arrays to be output using to_xml on each element if the
|
7
|
+
# element responds to to_xml. If an element does not respond to to_xml then a
|
8
|
+
# nested XML tag is created with the element's to_s value and the singlarized name
|
9
|
+
# of the array as the tag name.
|
10
|
+
|
11
|
+
require 'rubygems'
|
12
|
+
require 'activesupport'
|
13
|
+
require 'builder'
|
14
|
+
|
15
|
+
class Array
|
16
|
+
def to_xml(options = {})
|
17
|
+
#raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
|
18
|
+
require 'builder' unless defined?(Builder)
|
19
|
+
|
20
|
+
options = options.dup
|
21
|
+
options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records"
|
22
|
+
options[:children] ||= options[:root].singularize
|
23
|
+
options[:indent] ||= 2
|
24
|
+
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
|
25
|
+
|
26
|
+
root = options.delete(:root).to_s
|
27
|
+
children = options.delete(:children)
|
28
|
+
|
29
|
+
if !options.has_key?(:dasherize) || options[:dasherize]
|
30
|
+
root = root.dasherize
|
31
|
+
end
|
32
|
+
|
33
|
+
options[:builder].instruct! unless options.delete(:skip_instruct)
|
34
|
+
|
35
|
+
opts = options.merge({ :root => children })
|
36
|
+
|
37
|
+
root = root.pluralize
|
38
|
+
|
39
|
+
xml = options[:builder]
|
40
|
+
if empty?
|
41
|
+
xml.tag!(root, options[:skip_types] ? {} : {:type => "array"})
|
42
|
+
else
|
43
|
+
xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) do
|
44
|
+
yield xml if block_given?
|
45
|
+
each do |e|
|
46
|
+
if e.respond_to? :to_xml
|
47
|
+
e.to_xml(opts.merge({ :skip_instruct => true }))
|
48
|
+
else
|
49
|
+
xml.tag!(root.singularize, e.to_s)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,240 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module Serialization
|
4
|
+
|
5
|
+
class XmlSerializer #:nodoc:
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(record, options = {})
|
9
|
+
@record, @options = record, options.dup
|
10
|
+
end
|
11
|
+
|
12
|
+
def builder
|
13
|
+
@builder ||= begin
|
14
|
+
options[:indent] ||= 2
|
15
|
+
builder = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
|
16
|
+
|
17
|
+
unless options[:skip_instruct]
|
18
|
+
builder.instruct!
|
19
|
+
options[:skip_instruct] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
builder
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def root
|
27
|
+
root = (options[:root] || @record.class.to_s.underscore).to_s
|
28
|
+
dasherize? ? root.dasherize : root
|
29
|
+
end
|
30
|
+
|
31
|
+
def dasherize?
|
32
|
+
!options.has_key?(:dasherize) || options[:dasherize]
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# To replicate the behavior in ActiveRecord#attributes,
|
37
|
+
# :except takes precedence over :only. If :only is not set
|
38
|
+
# for a N level model but is set for the N+1 level models,
|
39
|
+
# then because :except is set to a default value, the second
|
40
|
+
# level model can have both :except and :only set. So if
|
41
|
+
# :only is set, always delete :except.
|
42
|
+
def serializable_attributes
|
43
|
+
#attribute_names = @record.attributes.keys # This includes all attributes including associations
|
44
|
+
attribute_names = @record.class.keys.keys # This includes just keys
|
45
|
+
idex = attribute_names.index("_id")
|
46
|
+
attribute_names[idex] = "id" if idex
|
47
|
+
|
48
|
+
if options[:only]
|
49
|
+
options.delete(:except)
|
50
|
+
attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
|
51
|
+
else
|
52
|
+
options[:except] = Array(options[:except])
|
53
|
+
attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
|
54
|
+
end
|
55
|
+
|
56
|
+
attribute_names.collect { |name| Attribute.new(name, @record) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def serializable_method_attributes
|
60
|
+
Array(options[:methods]).collect { |name| MethodAttribute.new(name.to_s, @record) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_attributes
|
64
|
+
(serializable_attributes + serializable_method_attributes).each do |attribute|
|
65
|
+
add_tag(attribute)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_includes
|
70
|
+
if include_associations = options.delete(:include)
|
71
|
+
root_only_or_except = { :except => options[:except],
|
72
|
+
:only => options[:only] }
|
73
|
+
|
74
|
+
include_has_options = include_associations.is_a?(Hash)
|
75
|
+
|
76
|
+
for association in include_has_options ? include_associations.keys : Array(include_associations)
|
77
|
+
association_options = include_has_options ? include_associations[association] : root_only_or_except
|
78
|
+
|
79
|
+
opts = options.merge(association_options)
|
80
|
+
|
81
|
+
case @record.class.associations[association].type
|
82
|
+
when :many, :has_and_belongs_to_many
|
83
|
+
records = @record.send(association).to_a
|
84
|
+
unless records.empty?
|
85
|
+
tag = association.to_s
|
86
|
+
tag = tag.dasherize if dasherize?
|
87
|
+
|
88
|
+
builder.tag!(tag) do
|
89
|
+
records.each { |r| r.to_xml(opts.merge(:root=>r.class.to_s.underscore)) }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
when :has_one, :belongs_to
|
93
|
+
if record = @record.send(association)
|
94
|
+
record.to_xml(opts.merge(:root => association))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
options[:include] = include_associations
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def add_procs
|
104
|
+
if procs = options.delete(:procs)
|
105
|
+
[ *procs ].each do |proc|
|
106
|
+
proc.call(options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def add_tag(attribute)
|
113
|
+
if attribute.type == :array
|
114
|
+
builder.tag!(
|
115
|
+
dasherize? ? attribute.name.dasherize.pluralize : attribute.name.pluralize,
|
116
|
+
attribute.decorations(!options[:skip_types])
|
117
|
+
) do |x|
|
118
|
+
attribute.value.each do |val|
|
119
|
+
if val.respond_to? :to_xml
|
120
|
+
x << val.to_xml(:skip_instruct => true, :root => attribute.name.dasherize.singularize)
|
121
|
+
else
|
122
|
+
x.tag!(
|
123
|
+
dasherize? ? attribute.name.dasherize.singularize : attribute.name.singularize,
|
124
|
+
val.to_s
|
125
|
+
)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
else
|
130
|
+
builder.tag!(
|
131
|
+
dasherize? ? attribute.name.dasherize : attribute.name,
|
132
|
+
attribute.value.to_s,
|
133
|
+
attribute.decorations(!options[:skip_types])
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def serialize
|
139
|
+
args = [root]
|
140
|
+
if options[:namespace]
|
141
|
+
args << {:xmlns=>options[:namespace]}
|
142
|
+
end
|
143
|
+
|
144
|
+
builder.tag!(*args) do
|
145
|
+
add_attributes
|
146
|
+
add_includes
|
147
|
+
add_procs
|
148
|
+
yield builder if block_given?
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
alias_method :to_s, :serialize
|
153
|
+
|
154
|
+
class Attribute #:nodoc:
|
155
|
+
attr_reader :name, :value, :type
|
156
|
+
|
157
|
+
def initialize(name, record)
|
158
|
+
@name, @record = name, record
|
159
|
+
|
160
|
+
@type = compute_type
|
161
|
+
@value = compute_value
|
162
|
+
end
|
163
|
+
|
164
|
+
# There is a significant speed improvement if the value
|
165
|
+
# does not need to be escaped, as #tag! escapes all values
|
166
|
+
# to ensure that valid XML is generated. For known binary
|
167
|
+
# values, it is at least an order of magnitude faster to
|
168
|
+
# Base64 encode binary values and directly put them in the
|
169
|
+
# output XML than to pass the original value or the Base64
|
170
|
+
# encoded value to the #tag! method. It definitely makes
|
171
|
+
# no sense to Base64 encode the value and then give it to
|
172
|
+
# #tag!, since that just adds additional overhead.
|
173
|
+
def needs_encoding?
|
174
|
+
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
|
175
|
+
end
|
176
|
+
|
177
|
+
def decorations(include_types = true)
|
178
|
+
decorations = {}
|
179
|
+
|
180
|
+
if type == :binary
|
181
|
+
decorations[:encoding] = 'base64'
|
182
|
+
end
|
183
|
+
|
184
|
+
if include_types && type != :string
|
185
|
+
decorations[:type] = type
|
186
|
+
end
|
187
|
+
|
188
|
+
decorations
|
189
|
+
end
|
190
|
+
|
191
|
+
protected
|
192
|
+
def compute_type
|
193
|
+
if name == "id"
|
194
|
+
return :object_id
|
195
|
+
end
|
196
|
+
|
197
|
+
#type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type
|
198
|
+
v = @record.class.keys[name]
|
199
|
+
#puts "Value type is........... #{v.type.to_s} #{v.type.to_s.blank?}"
|
200
|
+
|
201
|
+
#type = @record.send(name).class
|
202
|
+
|
203
|
+
type = v.nil? ? :yaml : (v.type.to_s.blank? ? :key : v.type.to_s.underscore.to_sym)
|
204
|
+
|
205
|
+
case type
|
206
|
+
when :text
|
207
|
+
:string
|
208
|
+
when :time
|
209
|
+
:datetime
|
210
|
+
# when :array
|
211
|
+
# :yaml
|
212
|
+
else
|
213
|
+
type
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def compute_value
|
218
|
+
n = name == "id" ? "_id" : name
|
219
|
+
|
220
|
+
value = @record.send(n)
|
221
|
+
|
222
|
+
if formatter = Hash::XML_FORMATTING[type.to_s]
|
223
|
+
value ? formatter.call(value) : nil
|
224
|
+
else
|
225
|
+
value
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
class MethodAttribute < Attribute #:nodoc:
|
231
|
+
protected
|
232
|
+
def compute_type
|
233
|
+
Hash::XML_TYPE_NAMES[@record.send(name).class.name] || :string
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module Timestamps
|
4
|
+
module ClassMethods
|
5
|
+
def timestamps!
|
6
|
+
key :created_at, Time
|
7
|
+
key :updated_at, Time
|
8
|
+
class_eval { before_save :update_timestamps }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def update_timestamps
|
14
|
+
now = Time.now.utc
|
15
|
+
self[:created_at] = now if new? && !created_at?
|
16
|
+
self[:updated_at] = now
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module Userstamps
|
4
|
+
module ClassMethods
|
5
|
+
def userstamps!
|
6
|
+
key :creator_id, ObjectId
|
7
|
+
key :updater_id, ObjectId
|
8
|
+
belongs_to :creator, :class_name => 'User'
|
9
|
+
belongs_to :updater, :class_name => 'User'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module Validations
|
4
|
+
def self.configure(model)
|
5
|
+
model.class_eval { include Validatable }
|
6
|
+
end
|
7
|
+
|
8
|
+
module DocumentMacros
|
9
|
+
def validates_uniqueness_of(*args)
|
10
|
+
add_validations(args, MongoMapper::Plugins::Validations::ValidatesUniquenessOf)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ValidatesUniquenessOf < Validatable::ValidationBase
|
15
|
+
option :scope, :case_sensitive
|
16
|
+
default :case_sensitive => true
|
17
|
+
|
18
|
+
def valid?(instance)
|
19
|
+
value = instance[attribute]
|
20
|
+
return true if allow_blank && value.blank?
|
21
|
+
return true if allow_nil && value.nil?
|
22
|
+
base_conditions = case_sensitive ? {self.attribute => value} : {}
|
23
|
+
doc = instance.class.first(base_conditions.merge(scope_conditions(instance)).merge(where_conditions(instance)))
|
24
|
+
doc.nil? || instance._id == doc._id
|
25
|
+
end
|
26
|
+
|
27
|
+
def message(instance)
|
28
|
+
super || "has already been taken"
|
29
|
+
end
|
30
|
+
|
31
|
+
def scope_conditions(instance)
|
32
|
+
return {} unless scope
|
33
|
+
Array(scope).inject({}) do |conditions, key|
|
34
|
+
conditions.merge(key => instance[key])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def where_conditions(instance)
|
39
|
+
conditions = {}
|
40
|
+
conditions[attribute] = /^#{Regexp.escape(instance[attribute].to_s)}$/i unless case_sensitive
|
41
|
+
conditions
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
# IMPORTANT
|
3
|
+
# This class is private to MongoMapper and should not be considered part of MongoMapper's public API.
|
4
|
+
#
|
5
|
+
class Query
|
6
|
+
OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
|
7
|
+
|
8
|
+
attr_reader :model
|
9
|
+
|
10
|
+
def initialize(model, options)
|
11
|
+
raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
|
12
|
+
@model, @options, @conditions, @original_options = model, {}, {}, options
|
13
|
+
separate_options_and_conditions
|
14
|
+
add_sci_condition
|
15
|
+
end
|
16
|
+
|
17
|
+
def criteria
|
18
|
+
to_criteria(@conditions)
|
19
|
+
end
|
20
|
+
|
21
|
+
def options
|
22
|
+
fields = @options[:fields] || @options[:select]
|
23
|
+
skip = @options[:skip] || @options[:offset] || 0
|
24
|
+
limit = @options[:limit] || 0
|
25
|
+
sort = @options[:sort] || normalized_sort(@options[:order])
|
26
|
+
|
27
|
+
{:fields => to_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_a
|
31
|
+
[criteria, options]
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def separate_options_and_conditions
|
36
|
+
@original_options.each_pair do |key, value|
|
37
|
+
key = key.respond_to?(:to_sym) ? key.to_sym : key
|
38
|
+
|
39
|
+
if OptionKeys.include?(key)
|
40
|
+
@options[key] = value
|
41
|
+
elsif key == :conditions
|
42
|
+
@conditions.update(value)
|
43
|
+
else
|
44
|
+
@conditions[key] = value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# adds _type single collection inheritance scope for models that need it
|
50
|
+
def add_sci_condition
|
51
|
+
@conditions[:_type] = model.to_s if model.single_collection_inherited?
|
52
|
+
end
|
53
|
+
|
54
|
+
def modifier?(field)
|
55
|
+
field.to_s =~ /^\$/
|
56
|
+
end
|
57
|
+
|
58
|
+
def symbol_operator?(object)
|
59
|
+
object.respond_to?(:field, :operator)
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_criteria(conditions, parent_key=nil)
|
63
|
+
criteria = {}
|
64
|
+
|
65
|
+
conditions.each_pair do |key, value|
|
66
|
+
key = normalized_key(key)
|
67
|
+
|
68
|
+
if model.object_id_key?(key)
|
69
|
+
case value
|
70
|
+
when String
|
71
|
+
value = ObjectId.to_mongo(value)
|
72
|
+
when Array
|
73
|
+
value.map! { |id| ObjectId.to_mongo(id) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if symbol_operator?(key)
|
78
|
+
key, value = normalized_key(key.field), {"$#{key.operator}" => value}
|
79
|
+
end
|
80
|
+
|
81
|
+
criteria[key] = normalized_value(criteria, key, value)
|
82
|
+
end
|
83
|
+
|
84
|
+
criteria
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_fields(keys)
|
88
|
+
return keys if keys.is_a?(Hash)
|
89
|
+
return nil if keys.blank?
|
90
|
+
|
91
|
+
if keys.respond_to?(:flatten, :compact)
|
92
|
+
keys.flatten.compact
|
93
|
+
else
|
94
|
+
keys.split(',').map { |key| key.strip }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_order(key, direction=nil)
|
99
|
+
[normalized_key(key).to_s, normalized_direction(direction)]
|
100
|
+
end
|
101
|
+
|
102
|
+
def normalized_key(key)
|
103
|
+
key.to_s == 'id' ? :_id : key
|
104
|
+
end
|
105
|
+
|
106
|
+
# TODO: this is getting heavy enough to move to a class
|
107
|
+
def normalized_value(criteria, key, value)
|
108
|
+
case value
|
109
|
+
when Array, Set
|
110
|
+
modifier?(key) ? value.to_a : {'$in' => value.to_a}
|
111
|
+
when Hash
|
112
|
+
if criteria[key].kind_of?(Hash)
|
113
|
+
criteria[key].dup.merge(to_criteria(value, key))
|
114
|
+
else
|
115
|
+
to_criteria(value, key)
|
116
|
+
end
|
117
|
+
when Time
|
118
|
+
value.utc
|
119
|
+
else
|
120
|
+
value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def normalized_direction(direction)
|
125
|
+
direction ||= 'asc'
|
126
|
+
direction.downcase == 'asc' ? Mongo::ASCENDING : Mongo::DESCENDING
|
127
|
+
end
|
128
|
+
|
129
|
+
def normalized_sort(sort)
|
130
|
+
return if sort.blank?
|
131
|
+
|
132
|
+
if sort.respond_to?(:all?) && sort.all? { |s| symbol_operator?(s) }
|
133
|
+
sort.map { |s| to_order(s.field, s.operator) }
|
134
|
+
elsif symbol_operator?(sort)
|
135
|
+
[to_order(sort.field, sort.operator)]
|
136
|
+
else
|
137
|
+
sort.split(',').map do |str|
|
138
|
+
to_order(*str.strip.split(' '))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|