mongo_mapper 0.7.5 → 0.7.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mongo_mapper.rb +3 -5
- data/lib/mongo_mapper/document.rb +23 -53
- data/lib/mongo_mapper/plugins/associations.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/base.rb +4 -4
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +1 -1
- data/lib/mongo_mapper/plugins/equality.rb +3 -3
- data/lib/mongo_mapper/plugins/identity_map.rb +8 -7
- data/lib/mongo_mapper/plugins/keys.rb +49 -73
- data/lib/mongo_mapper/plugins/keys/key.rb +44 -0
- data/lib/mongo_mapper/plugins/modifiers.rb +9 -5
- data/lib/mongo_mapper/plugins/pagination/proxy.rb +3 -3
- data/lib/mongo_mapper/plugins/serialization.rb +3 -3
- data/lib/mongo_mapper/plugins/timestamps.rb +1 -1
- data/lib/mongo_mapper/plugins/validations.rb +2 -2
- data/lib/mongo_mapper/query.rb +9 -129
- data/lib/mongo_mapper/support.rb +17 -39
- data/lib/mongo_mapper/version.rb +1 -1
- metadata +54 -140
- data/.gitignore +0 -10
- data/Rakefile +0 -37
- data/mongo_mapper.gemspec +0 -214
- data/performance/read_write.rb +0 -52
- data/specs.watchr +0 -51
- data/test/NOTE_ON_TESTING +0 -1
- data/test/active_model_lint_test.rb +0 -13
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +0 -63
- data/test/functional/associations/test_belongs_to_proxy.rb +0 -101
- data/test/functional/associations/test_in_array_proxy.rb +0 -325
- data/test/functional/associations/test_many_documents_as_proxy.rb +0 -229
- data/test/functional/associations/test_many_documents_proxy.rb +0 -536
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +0 -176
- data/test/functional/associations/test_many_embedded_proxy.rb +0 -256
- data/test/functional/associations/test_many_polymorphic_proxy.rb +0 -302
- data/test/functional/associations/test_one_embedded_proxy.rb +0 -68
- data/test/functional/associations/test_one_proxy.rb +0 -196
- data/test/functional/test_associations.rb +0 -44
- data/test/functional/test_binary.rb +0 -27
- data/test/functional/test_callbacks.rb +0 -151
- data/test/functional/test_dirty.rb +0 -163
- data/test/functional/test_document.rb +0 -1219
- data/test/functional/test_embedded_document.rb +0 -210
- data/test/functional/test_identity_map.rb +0 -507
- data/test/functional/test_indexing.rb +0 -44
- data/test/functional/test_logger.rb +0 -20
- data/test/functional/test_modifiers.rb +0 -394
- data/test/functional/test_pagination.rb +0 -93
- data/test/functional/test_protected.rb +0 -163
- data/test/functional/test_string_id_compatibility.rb +0 -67
- data/test/functional/test_timestamps.rb +0 -64
- data/test/functional/test_userstamps.rb +0 -28
- data/test/functional/test_validations.rb +0 -342
- data/test/models.rb +0 -227
- data/test/support/custom_matchers.rb +0 -37
- data/test/support/timing.rb +0 -16
- data/test/test_helper.rb +0 -64
- data/test/unit/associations/test_base.rb +0 -212
- data/test/unit/associations/test_proxy.rb +0 -105
- data/test/unit/serializers/test_json_serializer.rb +0 -202
- data/test/unit/test_descendant_appends.rb +0 -71
- data/test/unit/test_document.rb +0 -225
- data/test/unit/test_dynamic_finder.rb +0 -123
- data/test/unit/test_embedded_document.rb +0 -657
- data/test/unit/test_keys.rb +0 -185
- data/test/unit/test_mongo_mapper.rb +0 -118
- data/test/unit/test_pagination.rb +0 -160
- data/test/unit/test_plugins.rb +0 -50
- data/test/unit/test_query.rb +0 -374
- data/test/unit/test_rails.rb +0 -181
- data/test/unit/test_rails_compatibility.rb +0 -52
- data/test/unit/test_serialization.rb +0 -51
- data/test/unit/test_support.rb +0 -382
- data/test/unit/test_time_zones.rb +0 -39
- data/test/unit/test_validations.rb +0 -544
data/lib/mongo_mapper.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
|
-
# Make sure you have the
|
2
|
-
# gem 'activesupport', '>= 2.3.4'
|
3
|
-
# gem 'mongo', '1.0'
|
4
|
-
# gem 'jnunemaker-validatable', '1.8.4'
|
1
|
+
# Make sure you have the correct versions of the gems (see gemspec) in your load path.
|
5
2
|
require 'set'
|
6
3
|
require 'uri'
|
7
4
|
require 'mongo'
|
5
|
+
require 'plucky'
|
8
6
|
require 'validatable'
|
9
7
|
require 'active_support/all'
|
10
8
|
|
@@ -71,7 +69,7 @@ module MongoMapper
|
|
71
69
|
def self.config_for_environment(environment)
|
72
70
|
env = config[environment]
|
73
71
|
return env if env['uri'].blank?
|
74
|
-
|
72
|
+
|
75
73
|
uri = URI.parse(env['uri'])
|
76
74
|
raise InvalidScheme.new('must be mongodb') unless uri.scheme == 'mongodb'
|
77
75
|
{
|
@@ -45,34 +45,31 @@ module MongoMapper
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def find(*args)
|
48
|
-
assert_no_first_last_or_all(args)
|
49
48
|
options = args.extract_options!
|
50
49
|
return nil if args.size == 0
|
51
50
|
|
52
51
|
if args.first.is_a?(Array) || args.size > 1
|
53
52
|
find_some(args, options)
|
54
53
|
else
|
55
|
-
|
54
|
+
query = query(options).update(:_id => args[0])
|
55
|
+
find_one(query.to_hash)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def find!(*args)
|
60
|
-
assert_no_first_last_or_all(args)
|
61
60
|
options = args.extract_options!
|
62
61
|
raise DocumentNotFound, "Couldn't find without an ID" if args.size == 0
|
63
62
|
|
64
63
|
if args.first.is_a?(Array) || args.size > 1
|
65
64
|
find_some!(args, options)
|
66
65
|
else
|
67
|
-
|
66
|
+
query = query(options).update(:_id => args[0])
|
67
|
+
find_one(query.to_hash) || raise(DocumentNotFound, "Document match #{options.inspect} does not exist in #{collection.name} collection")
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
71
|
def find_each(options={})
|
72
|
-
|
73
|
-
collection.find(criteria, options).each do |doc|
|
74
|
-
yield load(doc)
|
75
|
-
end
|
72
|
+
query(options).find().each { |doc| yield load(doc) }
|
76
73
|
end
|
77
74
|
|
78
75
|
def find_by_id(id)
|
@@ -93,7 +90,7 @@ module MongoMapper
|
|
93
90
|
|
94
91
|
def last(options={})
|
95
92
|
raise ':order option must be provided when using last' if options[:order].blank?
|
96
|
-
find_one(
|
93
|
+
find_one(query(options).reverse.to_hash)
|
97
94
|
end
|
98
95
|
|
99
96
|
def all(options={})
|
@@ -101,7 +98,7 @@ module MongoMapper
|
|
101
98
|
end
|
102
99
|
|
103
100
|
def count(options={})
|
104
|
-
|
101
|
+
query(options).count
|
105
102
|
end
|
106
103
|
|
107
104
|
def exists?(options={})
|
@@ -126,11 +123,11 @@ module MongoMapper
|
|
126
123
|
end
|
127
124
|
|
128
125
|
def delete(*ids)
|
129
|
-
|
126
|
+
query(:_id => ids.flatten).remove
|
130
127
|
end
|
131
128
|
|
132
129
|
def delete_all(options={})
|
133
|
-
|
130
|
+
query(options).remove
|
134
131
|
end
|
135
132
|
|
136
133
|
def destroy(*ids)
|
@@ -153,6 +150,11 @@ module MongoMapper
|
|
153
150
|
superclass.respond_to?(:keys) && superclass.keys.key?(:_type)
|
154
151
|
end
|
155
152
|
|
153
|
+
# @api private for now
|
154
|
+
def query(options={})
|
155
|
+
Query.new(self, options)
|
156
|
+
end
|
157
|
+
|
156
158
|
private
|
157
159
|
def initialize_each(*docs)
|
158
160
|
instances = []
|
@@ -165,15 +167,9 @@ module MongoMapper
|
|
165
167
|
instances.size == 1 ? instances[0] : instances
|
166
168
|
end
|
167
169
|
|
168
|
-
def assert_no_first_last_or_all(args)
|
169
|
-
if args[0] == :first || args[0] == :last || args[0] == :all
|
170
|
-
raise ArgumentError, "#{self}.find(:#{args}) is no longer supported, use #{self}.#{args} instead."
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
170
|
def find_some(ids, options={})
|
175
|
-
|
176
|
-
find_many(
|
171
|
+
query = query(options).update(:_id => ids.flatten.compact.uniq)
|
172
|
+
find_many(query.to_hash).compact
|
177
173
|
end
|
178
174
|
|
179
175
|
def find_some!(ids, options={})
|
@@ -189,30 +185,12 @@ module MongoMapper
|
|
189
185
|
|
190
186
|
# All query methods that load documents pass through find_one or find_many
|
191
187
|
def find_one(options={})
|
192
|
-
|
193
|
-
if doc = collection.find_one(criteria, options)
|
194
|
-
load(doc)
|
195
|
-
end
|
188
|
+
load(query(options).first)
|
196
189
|
end
|
197
190
|
|
198
191
|
# All query methods that load documents pass through find_one or find_many
|
199
192
|
def find_many(options)
|
200
|
-
|
201
|
-
collection.find(criteria, options).to_a.map do |doc|
|
202
|
-
load(doc)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def invert_order_clause(order)
|
207
|
-
order.split(',').map do |order_segment|
|
208
|
-
if order_segment =~ /\sasc/i
|
209
|
-
order_segment.sub /\sasc/i, ' desc'
|
210
|
-
elsif order_segment =~ /\sdesc/i
|
211
|
-
order_segment.sub /\sdesc/i, ' asc'
|
212
|
-
else
|
213
|
-
"#{order_segment.strip} desc"
|
214
|
-
end
|
215
|
-
end.join(',')
|
193
|
+
query(options).all().map { |doc| load(doc) }
|
216
194
|
end
|
217
195
|
|
218
196
|
def update_single(id, attrs)
|
@@ -220,9 +198,9 @@ module MongoMapper
|
|
220
198
|
raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
|
221
199
|
end
|
222
200
|
|
223
|
-
|
224
|
-
|
225
|
-
|
201
|
+
find(id).tap do |doc|
|
202
|
+
doc.update_attributes(attrs)
|
203
|
+
end
|
226
204
|
end
|
227
205
|
|
228
206
|
def update_multiple(docs)
|
@@ -234,14 +212,6 @@ module MongoMapper
|
|
234
212
|
docs.each_pair { |id, attrs| instances << update(id, attrs) }
|
235
213
|
instances
|
236
214
|
end
|
237
|
-
|
238
|
-
def to_criteria(options={})
|
239
|
-
Query.new(self, options).criteria
|
240
|
-
end
|
241
|
-
|
242
|
-
def to_query(options={})
|
243
|
-
Query.new(self, options).to_a
|
244
|
-
end
|
245
215
|
end
|
246
216
|
|
247
217
|
module InstanceMethods
|
@@ -274,9 +244,9 @@ module MongoMapper
|
|
274
244
|
end
|
275
245
|
|
276
246
|
def reload
|
277
|
-
if
|
247
|
+
if doc = self.class.query(:_id => id).first
|
278
248
|
self.class.associations.each { |name, assoc| send(name).reset if respond_to?(name) }
|
279
|
-
self.attributes =
|
249
|
+
self.attributes = doc
|
280
250
|
self
|
281
251
|
else
|
282
252
|
raise DocumentNotFound, "Document match #{_id.inspect} does not exist in #{collection.name} collection"
|
@@ -16,8 +16,8 @@ module MongoMapper
|
|
16
16
|
|
17
17
|
def class_name
|
18
18
|
return @class_name if defined?(@class_name)
|
19
|
-
|
20
|
-
@class_name =
|
19
|
+
|
20
|
+
@class_name =
|
21
21
|
if cn = options[:class_name]
|
22
22
|
cn
|
23
23
|
elsif many?
|
@@ -78,8 +78,8 @@ module MongoMapper
|
|
78
78
|
# hate this, need to revisit
|
79
79
|
def proxy_class
|
80
80
|
return @proxy_class if defined?(@proxy_class)
|
81
|
-
|
82
|
-
@proxy_class =
|
81
|
+
|
82
|
+
@proxy_class =
|
83
83
|
if many?
|
84
84
|
if klass.embeddable?
|
85
85
|
polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
|
@@ -8,7 +8,7 @@ module MongoMapper
|
|
8
8
|
def self.clear
|
9
9
|
models.each { |m| m.identity_map.clear }
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def self.configure(model)
|
13
13
|
IdentityMap.models << model
|
14
14
|
end
|
@@ -28,27 +28,28 @@ module MongoMapper
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def find_one(options={})
|
31
|
-
|
32
|
-
|
31
|
+
query = query(options)
|
32
|
+
criteria = query.criteria.to_hash
|
33
|
+
|
33
34
|
if simple_find?(criteria) && identity_map.key?(criteria[:_id])
|
34
35
|
identity_map[criteria[:_id]]
|
35
36
|
else
|
36
37
|
super.tap do |document|
|
37
|
-
remove_documents_from_map(document) if selecting_fields?(
|
38
|
+
remove_documents_from_map(document) if selecting_fields?(query.options)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
43
|
def find_many(options)
|
43
|
-
criteria, query_options = to_query(options)
|
44
44
|
super.tap do |documents|
|
45
|
-
remove_documents_from_map(documents) if selecting_fields?(
|
45
|
+
remove_documents_from_map(documents) if selecting_fields?(query(options).options)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def load(attrs)
|
50
|
+
return nil if attrs.nil?
|
50
51
|
document = identity_map[attrs['_id']]
|
51
|
-
|
52
|
+
|
52
53
|
if document.nil? || identity_map_off?
|
53
54
|
document = super
|
54
55
|
identity_map[document._id] = document if identity_map_on?
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module MongoMapper
|
2
2
|
module Plugins
|
3
3
|
module Keys
|
4
|
+
autoload :Key, 'mongo_mapper/plugins/keys/key'
|
5
|
+
|
4
6
|
def self.configure(model)
|
5
7
|
model.key :_id, ObjectId
|
6
8
|
end
|
@@ -17,15 +19,13 @@ module MongoMapper
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def key(*args)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
key
|
22
|
+
Key.new(*args).tap do |key|
|
23
|
+
keys[key.name] = key
|
24
|
+
create_accessors_for(key)
|
25
|
+
create_key_in_descendants(*args)
|
26
|
+
create_indexes_for(key)
|
27
|
+
create_validations_for(key)
|
28
|
+
end
|
29
29
|
end
|
30
30
|
|
31
31
|
def key?(key)
|
@@ -36,9 +36,12 @@ module MongoMapper
|
|
36
36
|
object_id_key?(:_id)
|
37
37
|
end
|
38
38
|
|
39
|
+
def object_id_keys
|
40
|
+
keys.keys.select { |key| keys[key].type == ObjectId }.map(&:to_sym)
|
41
|
+
end
|
42
|
+
|
39
43
|
def object_id_key?(name)
|
40
|
-
|
41
|
-
key && key.type == ObjectId
|
44
|
+
object_id_keys.include?(name.to_sym)
|
42
45
|
end
|
43
46
|
|
44
47
|
def to_mongo(instance)
|
@@ -53,6 +56,7 @@ module MongoMapper
|
|
53
56
|
|
54
57
|
# load is overridden in identity map to ensure same objects are loaded
|
55
58
|
def load(attrs)
|
59
|
+
return nil if attrs.nil?
|
56
60
|
begin
|
57
61
|
klass = attrs['_type'].present? ? attrs['_type'].constantize : self
|
58
62
|
klass.new(attrs, true)
|
@@ -62,14 +66,16 @@ module MongoMapper
|
|
62
66
|
end
|
63
67
|
|
64
68
|
private
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
def key_accessors_module_defined?
|
70
|
+
if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
|
71
|
+
const_defined?('MongoMapperKeys')
|
72
|
+
else
|
73
|
+
const_defined?('MongoMapperKeys', false)
|
74
|
+
end
|
75
|
+
end
|
71
76
|
|
72
|
-
|
77
|
+
def accessors_module
|
78
|
+
if key_accessors_module_defined?
|
73
79
|
const_get 'MongoMapperKeys'
|
74
80
|
else
|
75
81
|
const_set 'MongoMapperKeys', Module.new
|
@@ -154,7 +160,7 @@ module MongoMapper
|
|
154
160
|
|
155
161
|
if from_database
|
156
162
|
@new = false
|
157
|
-
|
163
|
+
load_from_database(attrs)
|
158
164
|
else
|
159
165
|
@new = true
|
160
166
|
assign(attrs)
|
@@ -170,13 +176,11 @@ module MongoMapper
|
|
170
176
|
def attributes=(attrs)
|
171
177
|
return if attrs.blank?
|
172
178
|
|
173
|
-
attrs.each_pair do |
|
174
|
-
|
175
|
-
|
176
|
-
if respond_to?(writer_method)
|
177
|
-
self.send(writer_method, value)
|
179
|
+
attrs.each_pair do |key, value|
|
180
|
+
if respond_to?(:"#{key}=")
|
181
|
+
self.send(:"#{key}=", value)
|
178
182
|
else
|
179
|
-
self[
|
183
|
+
self[key] = value
|
180
184
|
end
|
181
185
|
end
|
182
186
|
end
|
@@ -255,6 +259,17 @@ module MongoMapper
|
|
255
259
|
end
|
256
260
|
|
257
261
|
private
|
262
|
+
def load_from_database(attrs)
|
263
|
+
return if attrs.blank?
|
264
|
+
attrs.each do |key, value|
|
265
|
+
if respond_to?(:"#{key}=") && !self.class.key?(key)
|
266
|
+
self.send(:"#{key}=", value)
|
267
|
+
else
|
268
|
+
self[key] = value
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
258
273
|
def default_id_value(attrs)
|
259
274
|
unless attrs.nil?
|
260
275
|
provided_keys = attrs.keys.map { |k| k.to_s }
|
@@ -278,68 +293,29 @@ module MongoMapper
|
|
278
293
|
end
|
279
294
|
end
|
280
295
|
|
281
|
-
def read_key(
|
282
|
-
if key = keys[
|
283
|
-
|
284
|
-
value = key.get(instance_variable_get(var_name))
|
296
|
+
def read_key(key_name)
|
297
|
+
if key = keys[key_name]
|
298
|
+
value = key.get(instance_variable_get(:"@#{key_name}"))
|
285
299
|
set_parent_document(key, value)
|
286
|
-
instance_variable_set(
|
300
|
+
instance_variable_set(:"@#{key_name}", value)
|
287
301
|
else
|
288
|
-
raise KeyNotFound, "Could not find key: #{
|
302
|
+
raise KeyNotFound, "Could not find key: #{key_name.inspect}"
|
289
303
|
end
|
290
304
|
end
|
291
305
|
|
292
306
|
def read_key_before_typecast(name)
|
293
|
-
instance_variable_get("@#{name}_before_typecast")
|
307
|
+
instance_variable_get(:"@#{name}_before_typecast")
|
294
308
|
end
|
295
309
|
|
296
310
|
def write_key(name, value)
|
297
|
-
key = keys[name]
|
298
|
-
|
311
|
+
key = keys[name.to_s]
|
299
312
|
set_parent_document(key, value)
|
300
|
-
instance_variable_set "@#{name}_before_typecast", value
|
301
|
-
instance_variable_set "@#{name}", key.set(value)
|
313
|
+
instance_variable_set :"@#{name}_before_typecast", value
|
314
|
+
instance_variable_set :"@#{name}", key.set(value)
|
302
315
|
end
|
303
316
|
end
|
304
317
|
|
305
|
-
class Key
|
306
|
-
attr_accessor :name, :type, :options, :default_value
|
307
|
-
|
308
|
-
def initialize(*args)
|
309
|
-
options = args.extract_options!
|
310
|
-
@name, @type = args.shift.to_s, args.shift
|
311
|
-
self.options = (options || {}).symbolize_keys
|
312
|
-
self.default_value = self.options.delete(:default)
|
313
|
-
end
|
314
|
-
|
315
|
-
def ==(other)
|
316
|
-
@name == other.name && @type == other.type
|
317
|
-
end
|
318
|
-
|
319
|
-
def embeddable?
|
320
|
-
type.respond_to?(:embeddable?) && type.embeddable? ? true : false
|
321
|
-
end
|
322
|
-
|
323
|
-
def number?
|
324
|
-
[Integer, Float].include?(type)
|
325
|
-
end
|
326
318
|
|
327
|
-
def get(value)
|
328
|
-
if value.nil? && !default_value.nil?
|
329
|
-
if default_value.respond_to?(:call)
|
330
|
-
return default_value.call
|
331
|
-
else
|
332
|
-
return default_value
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
type.from_mongo(value)
|
337
|
-
end
|
338
|
-
|
339
|
-
def set(value)
|
340
|
-
type.to_mongo(value)
|
341
|
-
end
|
342
|
-
end
|
343
319
|
end
|
344
320
|
end
|
345
321
|
end
|