mongoo 0.4.4 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/mongoo/attribute_sanitizer.rb +4 -3
- data/lib/mongoo/modifiers.rb +95 -80
- data/lib/mongoo/persistence.rb +21 -11
- data/mongoo.gemspec +3 -3
- data/test/test_mongoo.rb +70 -8
- metadata +4 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.5
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module Mongoo
|
2
2
|
class InvalidAttributeValue < Exception; end
|
3
|
-
|
3
|
+
|
4
4
|
class AttributeSanitizer
|
5
5
|
class << self
|
6
6
|
def sanitize(field_type, val)
|
7
7
|
return val if val.nil? || field_type.nil?
|
8
|
-
|
8
|
+
|
9
9
|
case field_type.to_sym
|
10
10
|
when :string then
|
11
11
|
val.is_a?(String) ? val : val.to_s
|
@@ -23,7 +23,8 @@ module Mongoo
|
|
23
23
|
val.is_a?(Hash) ? val : raise(InvalidAttributeValue, val.inspect)
|
24
24
|
when :time then
|
25
25
|
Time.parse(val.to_s)
|
26
|
-
|
26
|
+
when :db_ref then
|
27
|
+
val.is_a?(BSON::DBRef) ? val : BSON::DBRef.new(val.collection.name, val.id)
|
27
28
|
when :bool then
|
28
29
|
if [true,false].include?(val)
|
29
30
|
val
|
data/lib/mongoo/modifiers.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Mongoo
|
2
2
|
class ModifierUpdateError < Exception; end
|
3
3
|
class UnknownAttributeError < Exception; end
|
4
|
-
|
4
|
+
|
5
5
|
class ModifierBuilder
|
6
6
|
def initialize(opts, doc)
|
7
7
|
@opts = opts
|
@@ -9,145 +9,160 @@ module Mongoo
|
|
9
9
|
@queue = {}
|
10
10
|
@key_prefix = opts[:key_prefix] || ""
|
11
11
|
end
|
12
|
-
|
13
|
-
def
|
14
|
-
|
15
|
-
raise UnknownAttributeError, "#{@key_prefix}#{k}"
|
16
|
-
end
|
12
|
+
|
13
|
+
def known_attribute?(k)
|
14
|
+
@doc.known_attribute?("#{@key_prefix}#{k}")
|
17
15
|
end
|
18
|
-
|
16
|
+
|
19
17
|
def sanitize_value(k,v)
|
20
18
|
k = "#{@key_prefix}#{k}"
|
21
|
-
|
22
|
-
|
19
|
+
if known_attribute?(k)
|
20
|
+
field_type = @doc.class.attributes[k][:type]
|
21
|
+
Mongoo::AttributeSanitizer.sanitize(field_type, v)
|
22
|
+
else
|
23
|
+
v
|
24
|
+
end
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
def inc(k, v=1)
|
26
|
-
ensure_valid_field!(k)
|
27
28
|
v = sanitize_value(k,v)
|
28
29
|
@queue["$inc"] ||= {}
|
29
30
|
@queue["$inc"]["#{@key_prefix}#{k}"] = v
|
30
31
|
end
|
31
|
-
|
32
|
+
|
32
33
|
def set(k,v)
|
33
|
-
ensure_valid_field!(k)
|
34
34
|
v = sanitize_value(k,v)
|
35
35
|
@queue["$set"] ||= {}
|
36
36
|
@queue["$set"]["#{@key_prefix}#{k}"] = v
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def unset(k)
|
40
|
-
ensure_valid_field!(k)
|
41
40
|
@queue["$unset"] ||= {}
|
42
41
|
@queue["$unset"]["#{@key_prefix}#{k}"] = 1
|
43
42
|
end
|
44
43
|
|
45
44
|
def push(k, v)
|
46
|
-
ensure_valid_field!(k)
|
47
45
|
@queue["$push"] ||= {}
|
48
46
|
@queue["$push"]["#{@key_prefix}#{k}"] = v
|
49
47
|
end
|
50
|
-
|
48
|
+
|
51
49
|
def push_all(k, v)
|
52
|
-
ensure_valid_field!(k)
|
53
50
|
@queue["$pushAll"] ||= {}
|
54
51
|
@queue["$pushAll"]["#{@key_prefix}#{k}"] = v
|
55
52
|
end
|
56
|
-
|
53
|
+
|
57
54
|
def add_to_set(k,v)
|
58
|
-
ensure_valid_field!(k)
|
59
55
|
@queue["$addToSet"] ||= {}
|
60
56
|
@queue["$addToSet"]["#{@key_prefix}#{k}"] = v
|
61
57
|
end
|
62
|
-
|
58
|
+
|
63
59
|
def pop(k)
|
64
|
-
ensure_valid_field!(k)
|
65
60
|
@queue["$pop"] ||= {}
|
66
61
|
@queue["$pop"]["#{@key_prefix}#{k}"] = 1
|
67
62
|
end
|
68
|
-
|
63
|
+
|
69
64
|
def pull(k, v)
|
70
|
-
ensure_valid_field!(k)
|
71
65
|
@queue["$pull"] ||= {}
|
72
66
|
@queue["$pull"]["#{@key_prefix}#{k}"] = v
|
73
67
|
end
|
74
|
-
|
68
|
+
|
75
69
|
def pull_all(k, v)
|
76
|
-
ensure_valid_field!(k)
|
77
70
|
@queue["$pullAll"] ||= {}
|
78
71
|
@queue["$pullAll"]["#{@key_prefix}#{k}"] = v
|
79
72
|
end
|
80
|
-
|
73
|
+
|
81
74
|
def run!
|
82
75
|
if @queue.blank?
|
83
76
|
raise ModifierUpdateError, "modifier update queue is empty"
|
84
77
|
end
|
85
|
-
|
86
|
-
|
78
|
+
|
79
|
+
update_query = { "_id" => @doc.id }
|
80
|
+
if @opts[:only_if_current] == true
|
87
81
|
@queue.each do |op, op_queue|
|
88
|
-
op_queue.each do |k,
|
89
|
-
|
90
|
-
when "$inc" then
|
91
|
-
new_val = @doc.persisted_mongohash.dot_get(k).to_i + v
|
92
|
-
@doc.mongohash.dot_set( k, new_val )
|
93
|
-
@doc.persisted_mongohash.dot_set( k, new_val )
|
94
|
-
when "$set" then
|
95
|
-
@doc.mongohash.dot_set( k, v )
|
96
|
-
@doc.persisted_mongohash.dot_set( k, v )
|
97
|
-
when "$unset" then
|
98
|
-
@doc.mongohash.dot_delete( k )
|
99
|
-
@doc.persisted_mongohash.dot_delete( k )
|
100
|
-
when "$push" then
|
101
|
-
new_val = (@doc.persisted_mongohash.dot_get(k) || []) + [v]
|
102
|
-
@doc.mongohash.dot_set( k, new_val )
|
103
|
-
@doc.persisted_mongohash.dot_set( k, new_val )
|
104
|
-
when "$pushAll" then
|
105
|
-
new_val = (@doc.persisted_mongohash.dot_get(k) || []) + v
|
106
|
-
@doc.mongohash.dot_set( k, new_val )
|
107
|
-
@doc.persisted_mongohash.dot_set( k, new_val )
|
108
|
-
when "$addToSet" then
|
109
|
-
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
110
|
-
new_val << v unless new_val.include?(v)
|
111
|
-
@doc.mongohash.dot_set(k, new_val)
|
112
|
-
@doc.persisted_mongohash.dot_set(k, new_val)
|
113
|
-
when "$pop" then
|
114
|
-
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
115
|
-
new_val.pop
|
116
|
-
@doc.mongohash.dot_set(k, new_val)
|
117
|
-
@doc.persisted_mongohash.dot_set(k, new_val)
|
118
|
-
when "$pull" then
|
119
|
-
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
120
|
-
new_val.delete(v)
|
121
|
-
@doc.mongohash.dot_set(k, new_val)
|
122
|
-
@doc.persisted_mongohash.dot_set(k, new_val)
|
123
|
-
when "$pullAll" then
|
124
|
-
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
125
|
-
v.each do |val|
|
126
|
-
new_val.delete(val)
|
127
|
-
end
|
128
|
-
@doc.mongohash.dot_set(k, new_val)
|
129
|
-
@doc.persisted_mongohash.dot_set(k, new_val)
|
130
|
-
end
|
82
|
+
op_queue.each do |k,v|
|
83
|
+
update_query[k] = @doc.persisted_mongohash.dot_get(k)
|
131
84
|
end
|
132
85
|
end
|
133
|
-
|
134
|
-
|
135
|
-
raise ModifierUpdateError, ret.inspect
|
86
|
+
@opts[:update_opts] ||= {}
|
87
|
+
@opts[:update_opts][:safe] = true
|
136
88
|
end
|
89
|
+
|
90
|
+
if @opts[:find_and_modify]
|
91
|
+
ret = @doc.collection.find_and_modify(query: update_query,
|
92
|
+
update: @queue,
|
93
|
+
new: true)
|
94
|
+
@doc.reload(ret)
|
95
|
+
else
|
96
|
+
update_opts = @opts.delete(:update_opts) || {}
|
97
|
+
ret = @doc.collection.update(update_query, @queue, update_opts)
|
98
|
+
if !ret.is_a?(Hash) || (ret["err"] == nil && ret["n"] == 1)
|
99
|
+
@queue.each do |op, op_queue|
|
100
|
+
op_queue.each do |k, v|
|
101
|
+
case op
|
102
|
+
when "$inc" then
|
103
|
+
new_val = @doc.persisted_mongohash.dot_get(k).to_i + v
|
104
|
+
@doc.mongohash.dot_set( k, new_val )
|
105
|
+
@doc.persisted_mongohash.dot_set( k, new_val )
|
106
|
+
when "$set" then
|
107
|
+
@doc.mongohash.dot_set( k, v )
|
108
|
+
@doc.persisted_mongohash.dot_set( k, v )
|
109
|
+
when "$unset" then
|
110
|
+
@doc.mongohash.dot_delete( k )
|
111
|
+
@doc.persisted_mongohash.dot_delete( k )
|
112
|
+
when "$push" then
|
113
|
+
new_val = (@doc.persisted_mongohash.dot_get(k) || []) + [v]
|
114
|
+
@doc.mongohash.dot_set( k, new_val )
|
115
|
+
@doc.persisted_mongohash.dot_set( k, new_val )
|
116
|
+
when "$pushAll" then
|
117
|
+
new_val = (@doc.persisted_mongohash.dot_get(k) || []) + v
|
118
|
+
@doc.mongohash.dot_set( k, new_val )
|
119
|
+
@doc.persisted_mongohash.dot_set( k, new_val )
|
120
|
+
when "$addToSet" then
|
121
|
+
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
122
|
+
new_val << v unless new_val.include?(v)
|
123
|
+
@doc.mongohash.dot_set(k, new_val)
|
124
|
+
@doc.persisted_mongohash.dot_set(k, new_val)
|
125
|
+
when "$pop" then
|
126
|
+
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
127
|
+
new_val.pop
|
128
|
+
@doc.mongohash.dot_set(k, new_val)
|
129
|
+
@doc.persisted_mongohash.dot_set(k, new_val)
|
130
|
+
when "$pull" then
|
131
|
+
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
132
|
+
new_val.delete(v)
|
133
|
+
@doc.mongohash.dot_set(k, new_val)
|
134
|
+
@doc.persisted_mongohash.dot_set(k, new_val)
|
135
|
+
when "$pullAll" then
|
136
|
+
new_val = (@doc.persisted_mongohash.dot_get(k) || [])
|
137
|
+
v.each do |val|
|
138
|
+
new_val.delete(val)
|
139
|
+
end
|
140
|
+
@doc.mongohash.dot_set(k, new_val)
|
141
|
+
@doc.persisted_mongohash.dot_set(k, new_val)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end # @queue.each
|
145
|
+
true
|
146
|
+
else
|
147
|
+
raise ModifierUpdateError, ret.inspect
|
148
|
+
end
|
149
|
+
end # if opts[:find_any_modify]
|
137
150
|
end
|
138
151
|
end
|
139
|
-
|
152
|
+
|
140
153
|
module Modifiers
|
141
|
-
|
154
|
+
|
142
155
|
def mod(opts={}, &block)
|
143
156
|
builder = ModifierBuilder.new(opts, self)
|
144
157
|
block.call(builder)
|
145
158
|
builder.run!
|
146
159
|
end
|
147
|
-
|
160
|
+
|
148
161
|
def mod!(opts={}, &block)
|
149
|
-
|
162
|
+
opts[:update_opts] ||= {}
|
163
|
+
opts[:update_opts][:safe] = true
|
164
|
+
mod(opts, &block)
|
150
165
|
end
|
151
|
-
|
166
|
+
|
152
167
|
end
|
153
168
|
end
|
data/lib/mongoo/persistence.rb
CHANGED
@@ -205,21 +205,30 @@ module Mongoo
|
|
205
205
|
update_hash = build_update_hash(self.changelog)
|
206
206
|
return true if update_hash.empty?
|
207
207
|
update_query_hash = build_update_query_hash(persisted_mongohash.to_key_value, self.changelog)
|
208
|
+
|
208
209
|
if Mongoo.verbose_debug
|
209
210
|
puts "\n* update_query_hash: #{update_query_hash.inspect}\n update_hash: #{update_hash.inspect}\n opts: #{opts.inspect}\n"
|
210
211
|
end
|
211
|
-
|
212
|
-
if
|
213
|
-
|
214
|
-
|
215
|
-
|
212
|
+
|
213
|
+
if opts.delete(:find_and_modify) == true
|
214
|
+
ret = self.collection.find_and_modify(query: update_query_hash,
|
215
|
+
update: update_hash,
|
216
|
+
new: true)
|
217
|
+
reload(ret)
|
216
218
|
else
|
217
|
-
|
218
|
-
|
219
|
+
ret = self.collection.update(update_query_hash, update_hash, opts)
|
220
|
+
if !ret.is_a?(Hash) || (ret["updatedExisting"] && ret["n"] == 1)
|
221
|
+
set_persisted_mongohash(mongohash)
|
222
|
+
@persisted = true
|
223
|
+
true
|
219
224
|
else
|
220
|
-
|
225
|
+
if opts[:only_if_current]
|
226
|
+
raise StaleUpdateError, ret.inspect
|
227
|
+
else
|
228
|
+
raise UpdateError, ret.inspect
|
229
|
+
end
|
221
230
|
end
|
222
|
-
end
|
231
|
+
end # if opts.delete(:find_and_modify)
|
223
232
|
end
|
224
233
|
end
|
225
234
|
|
@@ -255,8 +264,9 @@ module Mongoo
|
|
255
264
|
remove(opts.merge(:safe => true))
|
256
265
|
end
|
257
266
|
|
258
|
-
def reload
|
259
|
-
|
267
|
+
def reload(new_doc=nil)
|
268
|
+
new_doc ||= collection.find_one(get("_id"))
|
269
|
+
init_from_hash(new_doc)
|
260
270
|
@persisted = true
|
261
271
|
set_persisted_mongohash(mongohash)
|
262
272
|
true
|
data/mongoo.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mongoo}
|
8
|
-
s.version = "0.4.
|
8
|
+
s.version = "0.4.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ben Myles"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-06-06}
|
13
13
|
s.description = %q{Simple object mapper for MongoDB}
|
14
14
|
s.email = %q{ben.myles@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -47,7 +47,7 @@ Gem::Specification.new do |s|
|
|
47
47
|
s.homepage = %q{http://github.com/benmyles/mongoo}
|
48
48
|
s.licenses = ["MIT"]
|
49
49
|
s.require_paths = ["lib"]
|
50
|
-
s.rubygems_version = %q{1.6.
|
50
|
+
s.rubygems_version = %q{1.6.1}
|
51
51
|
s.summary = %q{Object mapper for MongoDB}
|
52
52
|
s.test_files = [
|
53
53
|
"test/helper.rb",
|
data/test/test_mongoo.rb
CHANGED
@@ -229,14 +229,6 @@ class TestMongoo < Test::Unit::TestCase
|
|
229
229
|
assert_equal ["snowboarding", "travelling"], p.interests
|
230
230
|
end
|
231
231
|
|
232
|
-
should "not be able to modify fields that don't exist" do
|
233
|
-
p = Person.new("name" => "Ben", "visits" => 0)
|
234
|
-
p.insert!
|
235
|
-
assert_raise(Mongoo::UnknownAttributeError) do
|
236
|
-
p.mod! { |mod| mod.push("idontexist", "foobar") }
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
232
|
should "be able to access a hash type directly" do
|
241
233
|
p = Person.new("name" => "Ben")
|
242
234
|
p.insert!
|
@@ -351,5 +343,75 @@ class TestMongoo < Test::Unit::TestCase
|
|
351
343
|
p.reload
|
352
344
|
assert_equal ["skydiving", "coding", "swimming"], p.interests
|
353
345
|
end
|
346
|
+
|
347
|
+
should "still do set/unset updates on granular attributes on a hash field" do
|
348
|
+
p = Person.new(name: "Ben", interests: ["skydiving", "coding"], misc: { foo: { bar: 1, zar: 2 } })
|
349
|
+
p.insert!
|
350
|
+
p.misc['foo']['zar'] = 3
|
351
|
+
assert_equal [[:set, "misc.foo.zar", 3]], p.changelog
|
352
|
+
end
|
353
|
+
|
354
|
+
should "be able to apply modifiers on hash attribute values" do
|
355
|
+
p = Person.new(name: "Ben", interests: ["skydiving", "coding"], misc: { foo: { bar: 1, zar: 2 } })
|
356
|
+
p.insert!
|
357
|
+
p.mod { |m| m.inc 'misc.foo.zar', 2 }
|
358
|
+
assert_equal 4, p.misc['foo']['zar']
|
359
|
+
p.reload
|
360
|
+
assert_equal 4, p.misc['foo']['zar']
|
361
|
+
end
|
362
|
+
|
363
|
+
should "be able to abort modifier update if there are stale values" do
|
364
|
+
p = Person.new(name: "Ben", interests: ["skydiving", "coding"], misc: { foo: { bar: 1, zar: 2 } })
|
365
|
+
p.insert!
|
366
|
+
p.mod { |m| m.inc 'misc.foo.zar', 2 }
|
367
|
+
|
368
|
+
p2 = Person.find_one(p.id)
|
369
|
+
p2.mod { |m| m.inc 'misc.foo.zar', 2 }
|
370
|
+
assert_equal 6, p2.misc['foo']['zar']
|
371
|
+
|
372
|
+
assert_raise(Mongoo::ModifierUpdateError) do
|
373
|
+
p.mod(only_if_current: true) { |m| m.inc 'misc.foo.zar', 1 }
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
should "be able to do a find_and_modify when using modifiers" do
|
378
|
+
p = Person.new(name: "Ben", interests: ["skydiving", "coding"], misc: { foo: { bar: 1, zar: 2 } })
|
379
|
+
p.insert!
|
380
|
+
p.mod { |m| m.inc 'misc.foo.zar', 2 }
|
381
|
+
|
382
|
+
p2 = Person.find_one(p.id)
|
383
|
+
p2.mod { |m| m.inc 'misc.foo.zar', 2 }
|
384
|
+
assert_equal 6, p2.misc['foo']['zar']
|
385
|
+
|
386
|
+
p.mod { |m| m.inc 'misc.foo.zar', 1 }
|
387
|
+
assert_equal 5, p.misc['foo']['zar']
|
388
|
+
|
389
|
+
# but if we do a find_and_modify ....
|
390
|
+
|
391
|
+
p = Person.new(name: "Ben", interests: ["skydiving", "coding"], misc: { foo: { bar: 1, zar: 2 } })
|
392
|
+
p.insert!
|
393
|
+
p.mod { |m| m.inc 'misc.foo.zar', 2 }
|
394
|
+
|
395
|
+
p2 = Person.find_one(p.id)
|
396
|
+
p2.mod { |m| m.inc 'misc.foo.zar', 2 }
|
397
|
+
assert_equal 6, p2.misc['foo']['zar']
|
398
|
+
|
399
|
+
p.mod(find_and_modify: true) { |m| m.inc 'misc.foo.zar', 1 }
|
400
|
+
assert_equal 7, p.misc['foo']['zar']
|
401
|
+
end
|
402
|
+
|
403
|
+
should "be able to do an update using find_and_modify" do
|
404
|
+
p = Person.new(name: "Ben", interests: ["skydiving", "coding"])
|
405
|
+
p.insert!
|
406
|
+
|
407
|
+
p2 = Person.find_one(p.id)
|
408
|
+
p2.mod! { |m| m.push "interests", "swimming" }
|
409
|
+
|
410
|
+
assert_equal ["skydiving","coding"], p.interests
|
411
|
+
p.name = "Ben Myles"
|
412
|
+
p.update!(find_and_modify: true)
|
413
|
+
assert_equal "Ben Myles", p.name
|
414
|
+
assert_equal ["skydiving", "coding", "swimming"], p.interests
|
415
|
+
end
|
354
416
|
end
|
355
417
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: mongoo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.4.
|
5
|
+
version: 0.4.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ben Myles
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-06-06 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -184,7 +184,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
184
184
|
requirements:
|
185
185
|
- - ">="
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
hash:
|
187
|
+
hash: 728381716729290434
|
188
188
|
segments:
|
189
189
|
- 0
|
190
190
|
version: "0"
|
@@ -197,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
197
197
|
requirements: []
|
198
198
|
|
199
199
|
rubyforge_project:
|
200
|
-
rubygems_version: 1.6.
|
200
|
+
rubygems_version: 1.6.1
|
201
201
|
signing_key:
|
202
202
|
specification_version: 3
|
203
203
|
summary: Object mapper for MongoDB
|