ohm 1.0.0 → 1.0.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/lib/ohm.rb +82 -75
- data/test/issue-52.rb +34 -0
- metadata +11 -11
data/lib/ohm.rb
CHANGED
@@ -129,8 +129,29 @@ module Ohm
|
|
129
129
|
redis.flushdb
|
130
130
|
end
|
131
131
|
|
132
|
+
# Wraps the whole pipelining functionality.
|
133
|
+
module PipelinedFetch
|
134
|
+
private
|
135
|
+
def fetch(ids)
|
136
|
+
arr = db.pipelined do
|
137
|
+
ids.each { |id| db.hgetall(namespace[id]) }
|
138
|
+
end
|
139
|
+
|
140
|
+
res = []
|
141
|
+
|
142
|
+
return res if arr.nil?
|
143
|
+
|
144
|
+
arr.each_with_index do |atts, idx|
|
145
|
+
res << model.new(Hash[*atts].update(:id => ids[idx]))
|
146
|
+
end
|
147
|
+
|
148
|
+
res
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
132
152
|
# Defines most of the methods used by `Set` and `MultiSet`.
|
133
153
|
module Collection
|
154
|
+
include PipelinedFetch
|
134
155
|
include Enumerable
|
135
156
|
|
136
157
|
# Fetch the data from Redis in one go.
|
@@ -192,10 +213,10 @@ module Ohm
|
|
192
213
|
def sort(options = {})
|
193
214
|
if options.has_key?(:get)
|
194
215
|
options[:get] = namespace["*->%s" % options[:get]]
|
195
|
-
return execute { |key|
|
216
|
+
return execute { |key| db.sort(key, options) }
|
196
217
|
end
|
197
218
|
|
198
|
-
fetch(execute { |key|
|
219
|
+
fetch(execute { |key| db.sort(key, options) })
|
199
220
|
end
|
200
221
|
|
201
222
|
# Check if a model is included in this set.
|
@@ -216,7 +237,7 @@ module Ohm
|
|
216
237
|
|
217
238
|
# Returns the total size of the set using SCARD.
|
218
239
|
def size
|
219
|
-
execute { |key|
|
240
|
+
execute { |key| db.scard(key) }
|
220
241
|
end
|
221
242
|
|
222
243
|
# Syntactic sugar for `sort_by` or `sort` when you only need the
|
@@ -243,7 +264,7 @@ module Ohm
|
|
243
264
|
|
244
265
|
# Grab all the elements of this set using SMEMBERS.
|
245
266
|
def ids
|
246
|
-
execute { |key|
|
267
|
+
execute { |key| db.smembers(key) }
|
247
268
|
end
|
248
269
|
|
249
270
|
# Retrieve a specific element using an ID from this set.
|
@@ -262,42 +283,27 @@ module Ohm
|
|
262
283
|
|
263
284
|
private
|
264
285
|
def exists?(id)
|
265
|
-
execute { |key|
|
266
|
-
end
|
267
|
-
|
268
|
-
def fetch(ids)
|
269
|
-
arr = model.db.pipelined do
|
270
|
-
ids.each { |id| model.db.hgetall(namespace[id]) }
|
271
|
-
end
|
272
|
-
|
273
|
-
res = []
|
274
|
-
|
275
|
-
return res if arr.nil?
|
276
|
-
|
277
|
-
arr.each_with_index do |atts, idx|
|
278
|
-
res << model.new(Hash[*atts].update(:id => ids[idx]))
|
279
|
-
end
|
280
|
-
|
281
|
-
res
|
286
|
+
execute { |key| db.sismember(key, id) }
|
282
287
|
end
|
283
288
|
end
|
284
289
|
|
285
290
|
class List < Struct.new(:key, :namespace, :model)
|
291
|
+
include PipelinedFetch
|
286
292
|
include Enumerable
|
287
293
|
|
288
294
|
# Returns the total size of the list using LLEN.
|
289
295
|
def size
|
290
|
-
|
296
|
+
db.llen(key)
|
291
297
|
end
|
292
298
|
|
293
299
|
# Returns the first element of the list using LINDEX.
|
294
300
|
def first
|
295
|
-
model[
|
301
|
+
model[db.lindex(key, 0)]
|
296
302
|
end
|
297
303
|
|
298
304
|
# Returns the last element of the list using LINDEX.
|
299
305
|
def last
|
300
|
-
model[
|
306
|
+
model[db.lindex(key, -1)]
|
301
307
|
end
|
302
308
|
|
303
309
|
# Checks if the model is part of this List.
|
@@ -331,8 +337,8 @@ module Ohm
|
|
331
337
|
ids = models.map { |model| model.id }
|
332
338
|
|
333
339
|
model.db.multi do
|
334
|
-
|
335
|
-
ids.each { |id|
|
340
|
+
db.del(key)
|
341
|
+
ids.each { |id| db.rpush(key, id) }
|
336
342
|
end
|
337
343
|
end
|
338
344
|
|
@@ -351,12 +357,12 @@ module Ohm
|
|
351
357
|
|
352
358
|
# Pushes the model to the _end_ of the list using RPUSH.
|
353
359
|
def push(model)
|
354
|
-
|
360
|
+
db.rpush(key, model.id)
|
355
361
|
end
|
356
362
|
|
357
363
|
# Pushes the model to the _beginning_ of the list using LPUSH.
|
358
364
|
def unshift(model)
|
359
|
-
|
365
|
+
db.lpush(key, model.id)
|
360
366
|
end
|
361
367
|
|
362
368
|
# Delete a model from the list.
|
@@ -387,28 +393,16 @@ module Ohm
|
|
387
393
|
def delete(model)
|
388
394
|
# LREM key 0 <id> means remove all elements matching <id>
|
389
395
|
# @see http://redis.io/commands/lrem
|
390
|
-
|
396
|
+
db.lrem(key, 0, model.id)
|
391
397
|
end
|
392
398
|
|
393
399
|
private
|
394
400
|
def ids
|
395
|
-
|
401
|
+
db.lrange(key, 0, -1)
|
396
402
|
end
|
397
403
|
|
398
|
-
def
|
399
|
-
|
400
|
-
ids.each { |id| model.db.hgetall(namespace[id]) }
|
401
|
-
end
|
402
|
-
|
403
|
-
res = []
|
404
|
-
|
405
|
-
return res if arr.nil?
|
406
|
-
|
407
|
-
arr.each_with_index do |atts, idx|
|
408
|
-
res << model.new(Hash[*atts].update(:id => ids[idx]))
|
409
|
-
end
|
410
|
-
|
411
|
-
res
|
404
|
+
def db
|
405
|
+
model.db
|
412
406
|
end
|
413
407
|
end
|
414
408
|
|
@@ -460,6 +454,10 @@ module Ohm
|
|
460
454
|
def execute
|
461
455
|
yield key
|
462
456
|
end
|
457
|
+
|
458
|
+
def db
|
459
|
+
model.db
|
460
|
+
end
|
463
461
|
end
|
464
462
|
|
465
463
|
class MutableSet < Set
|
@@ -473,7 +471,7 @@ module Ohm
|
|
473
471
|
# user.posts.add(post)
|
474
472
|
#
|
475
473
|
def add(model)
|
476
|
-
|
474
|
+
db.sadd(key, model.id)
|
477
475
|
end
|
478
476
|
|
479
477
|
# Remove a model directly from the set.
|
@@ -486,7 +484,7 @@ module Ohm
|
|
486
484
|
# user.posts.delete(post)
|
487
485
|
#
|
488
486
|
def delete(model)
|
489
|
-
|
487
|
+
db.srem(key, model.id)
|
490
488
|
end
|
491
489
|
|
492
490
|
# Replace all the existing elements of a set with a different
|
@@ -509,8 +507,8 @@ module Ohm
|
|
509
507
|
ids = models.map { |model| model.id }
|
510
508
|
|
511
509
|
key.redis.multi do
|
512
|
-
|
513
|
-
ids.each { |id|
|
510
|
+
db.del(key)
|
511
|
+
ids.each { |id| db.sadd(key, id) }
|
514
512
|
end
|
515
513
|
end
|
516
514
|
end
|
@@ -586,6 +584,10 @@ module Ohm
|
|
586
584
|
end
|
587
585
|
|
588
586
|
private
|
587
|
+
def db
|
588
|
+
model.db
|
589
|
+
end
|
590
|
+
|
589
591
|
def filters
|
590
592
|
@filters ||= []
|
591
593
|
end
|
@@ -595,7 +597,7 @@ module Ohm
|
|
595
597
|
end
|
596
598
|
|
597
599
|
def clean_temp_keys
|
598
|
-
|
600
|
+
db.del(*temp_keys)
|
599
601
|
temp_keys.clear
|
600
602
|
end
|
601
603
|
|
@@ -617,7 +619,7 @@ module Ohm
|
|
617
619
|
# one intersected set, hence we need to `sinterstore` all
|
618
620
|
# the filters in a temporary set.
|
619
621
|
temp = generate_temp_key
|
620
|
-
|
622
|
+
db.sinterstore(temp, *list)
|
621
623
|
|
622
624
|
# If this is the first set, we simply assign the generated
|
623
625
|
# set to main, which could possibly be the return value
|
@@ -629,7 +631,12 @@ module Ohm
|
|
629
631
|
# Append the generated temporary set using the operation.
|
630
632
|
# i.e. if we have (mood=happy & book=1) and we have an
|
631
633
|
# `sunionstore`, we do (mood=happy & book=1) | (mood=sad & book=1)
|
632
|
-
|
634
|
+
#
|
635
|
+
# Here we dynamically call the stored command, e.g.
|
636
|
+
#
|
637
|
+
# SUNIONSTORE main main temp
|
638
|
+
#
|
639
|
+
db.send(operation, main, main, temp)
|
633
640
|
end
|
634
641
|
end
|
635
642
|
|
@@ -769,7 +776,7 @@ module Ohm
|
|
769
776
|
|
770
777
|
# Check if the ID exists within <Model>:all.
|
771
778
|
def self.exists?(id)
|
772
|
-
key[:all]
|
779
|
+
db.sismember(key[:all], id)
|
773
780
|
end
|
774
781
|
|
775
782
|
# Find values in `unique` indices.
|
@@ -785,7 +792,7 @@ module Ohm
|
|
785
792
|
# # => true
|
786
793
|
#
|
787
794
|
def self.with(att, val)
|
788
|
-
id = key[:uniques][att]
|
795
|
+
id = db.hget(key[:uniques][att], val)
|
789
796
|
id && self[id]
|
790
797
|
end
|
791
798
|
|
@@ -814,8 +821,8 @@ module Ohm
|
|
814
821
|
# end
|
815
822
|
# end
|
816
823
|
#
|
817
|
-
# u = User.create(:
|
818
|
-
# User.find(:
|
824
|
+
# u = User.create(name: "John", status: "pending", email: "foo@me.com")
|
825
|
+
# User.find(provider: "me", name: "John", status: "pending").include?(u)
|
819
826
|
# # => true
|
820
827
|
#
|
821
828
|
# User.find(:tag => "ruby").include?(u)
|
@@ -1054,7 +1061,7 @@ module Ohm
|
|
1054
1061
|
define_method(name) do
|
1055
1062
|
return 0 if new?
|
1056
1063
|
|
1057
|
-
key[:counters]
|
1064
|
+
db.hget(key[:counters], name).to_i
|
1058
1065
|
end
|
1059
1066
|
end
|
1060
1067
|
|
@@ -1132,7 +1139,7 @@ module Ohm
|
|
1132
1139
|
# Preload all the attributes of this model from Redis. Used
|
1133
1140
|
# internally by `Model::[]`.
|
1134
1141
|
def load!
|
1135
|
-
update_attributes(
|
1142
|
+
update_attributes(db.hgetall(key)) unless new?
|
1136
1143
|
return self
|
1137
1144
|
end
|
1138
1145
|
|
@@ -1153,7 +1160,7 @@ module Ohm
|
|
1153
1160
|
# | u.get(:name) == "B"
|
1154
1161
|
#
|
1155
1162
|
def get(att)
|
1156
|
-
@attributes[att] =
|
1163
|
+
@attributes[att] = db.hget(key, att)
|
1157
1164
|
end
|
1158
1165
|
|
1159
1166
|
# Update an attribute value atomically. The best usecase for this
|
@@ -1163,7 +1170,7 @@ module Ohm
|
|
1163
1170
|
# and uniques. Use it wisely. The safe equivalent is `update`.
|
1164
1171
|
#
|
1165
1172
|
def set(att, val)
|
1166
|
-
val.to_s.empty? ?
|
1173
|
+
val.to_s.empty? ? db.hdel(key, att) : db.hset(key, att, val)
|
1167
1174
|
@attributes[att] = val
|
1168
1175
|
end
|
1169
1176
|
|
@@ -1173,7 +1180,7 @@ module Ohm
|
|
1173
1180
|
|
1174
1181
|
# Increment a counter atomically. Internally uses HINCRBY.
|
1175
1182
|
def incr(att, count = 1)
|
1176
|
-
key[:counters]
|
1183
|
+
db.hincrby(key[:counters], att, count)
|
1177
1184
|
end
|
1178
1185
|
|
1179
1186
|
# Decrement a counter atomically. Internally uses HINCRBY.
|
@@ -1291,13 +1298,13 @@ module Ohm
|
|
1291
1298
|
|
1292
1299
|
t.read do
|
1293
1300
|
_verify_uniques
|
1294
|
-
existing =
|
1301
|
+
existing = db.hgetall(key)
|
1295
1302
|
uniques = _read_index_type(:uniques)
|
1296
1303
|
indices = _read_index_type(:indices)
|
1297
1304
|
end
|
1298
1305
|
|
1299
1306
|
t.write do
|
1300
|
-
model.key[:all]
|
1307
|
+
db.sadd(model.key[:all], id)
|
1301
1308
|
_delete_uniques(existing)
|
1302
1309
|
_delete_indices(existing)
|
1303
1310
|
_save
|
@@ -1318,16 +1325,16 @@ module Ohm
|
|
1318
1325
|
def delete
|
1319
1326
|
transaction do |t|
|
1320
1327
|
t.read do |store|
|
1321
|
-
store[:existing] =
|
1328
|
+
store[:existing] = db.hgetall(key)
|
1322
1329
|
end
|
1323
1330
|
|
1324
1331
|
t.write do |store|
|
1325
1332
|
_delete_uniques(store[:existing])
|
1326
1333
|
_delete_indices(store[:existing])
|
1327
|
-
model.collections.each { |e| key[e]
|
1328
|
-
model.key[:all]
|
1329
|
-
key[:counters]
|
1330
|
-
|
1334
|
+
model.collections.each { |e| db.del(key[e]) }
|
1335
|
+
db.srem(model.key[:all], id)
|
1336
|
+
db.del(key[:counters])
|
1337
|
+
db.del(key)
|
1331
1338
|
end
|
1332
1339
|
|
1333
1340
|
yield t if block_given?
|
@@ -1397,7 +1404,7 @@ module Ohm
|
|
1397
1404
|
end
|
1398
1405
|
|
1399
1406
|
def self.new_id
|
1400
|
-
key[:id]
|
1407
|
+
db.incr(key[:id])
|
1401
1408
|
end
|
1402
1409
|
|
1403
1410
|
attr_writer :id
|
@@ -1435,8 +1442,8 @@ module Ohm
|
|
1435
1442
|
|
1436
1443
|
def _save
|
1437
1444
|
catch :empty do
|
1438
|
-
|
1439
|
-
|
1445
|
+
db.del(key)
|
1446
|
+
db.hmset(key, *_skip_empty(attributes).to_a.flatten)
|
1440
1447
|
end
|
1441
1448
|
end
|
1442
1449
|
|
@@ -1448,7 +1455,7 @@ module Ohm
|
|
1448
1455
|
|
1449
1456
|
def _detect_duplicate
|
1450
1457
|
model.uniques.detect do |att|
|
1451
|
-
id = model.key[:uniques][att]
|
1458
|
+
id = db.hget(model.key[:uniques][att], send(att))
|
1452
1459
|
id && id != self.id.to_s
|
1453
1460
|
end
|
1454
1461
|
end
|
@@ -1463,13 +1470,13 @@ module Ohm
|
|
1463
1470
|
|
1464
1471
|
def _save_uniques(uniques)
|
1465
1472
|
uniques.each do |att, val|
|
1466
|
-
model.key[:uniques][att]
|
1473
|
+
db.hset(model.key[:uniques][att], val, id)
|
1467
1474
|
end
|
1468
1475
|
end
|
1469
1476
|
|
1470
1477
|
def _delete_uniques(atts)
|
1471
1478
|
model.uniques.each do |att|
|
1472
|
-
model.key[:uniques][att]
|
1479
|
+
db.hdel(model.key[:uniques][att], atts[att.to_s])
|
1473
1480
|
end
|
1474
1481
|
end
|
1475
1482
|
|
@@ -1477,14 +1484,14 @@ module Ohm
|
|
1477
1484
|
model.indices.each do |att|
|
1478
1485
|
val = atts[att.to_s]
|
1479
1486
|
|
1480
|
-
model.key[:indices][att][val]
|
1487
|
+
db.srem(model.key[:indices][att][val], id)
|
1481
1488
|
end
|
1482
1489
|
end
|
1483
1490
|
|
1484
1491
|
def _save_indices(indices)
|
1485
1492
|
indices.each do |att, val|
|
1486
1493
|
model.toindices(att, val).each do |index|
|
1487
|
-
|
1494
|
+
db.sadd(index, id)
|
1488
1495
|
end
|
1489
1496
|
end
|
1490
1497
|
end
|
data/test/issue-52.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
class Model < Ohm::Model
|
4
|
+
attribute :hash
|
5
|
+
index :hash
|
6
|
+
|
7
|
+
attribute :data
|
8
|
+
end
|
9
|
+
|
10
|
+
test do
|
11
|
+
50.times do |i|
|
12
|
+
Ohm.flush
|
13
|
+
|
14
|
+
Model.create(:hash => "123")
|
15
|
+
|
16
|
+
assert_equal 1, Ohm.redis.scard("Model:all")
|
17
|
+
|
18
|
+
Thread.new do
|
19
|
+
a = Model.find(:hash => "123").first
|
20
|
+
a.update(:data => "2")
|
21
|
+
end
|
22
|
+
|
23
|
+
sleep 0.01
|
24
|
+
|
25
|
+
b = Model.find(:hash => "123").first
|
26
|
+
|
27
|
+
if Ohm.redis.scard("Model:indices:hash:123") != 1
|
28
|
+
flunk("Failed at iteration %d" % i)
|
29
|
+
end
|
30
|
+
|
31
|
+
assert ! b.nil?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ohm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,11 +11,11 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-05-03 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: redis
|
18
|
-
requirement: &
|
18
|
+
requirement: &2155998060 !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
21
21
|
- - ~>
|
@@ -23,10 +23,10 @@ dependencies:
|
|
23
23
|
version: '2.2'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
|
-
version_requirements: *
|
26
|
+
version_requirements: *2155998060
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nest
|
29
|
-
requirement: &
|
29
|
+
requirement: &2155997480 !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
31
31
|
requirements:
|
32
32
|
- - ~>
|
@@ -34,10 +34,10 @@ dependencies:
|
|
34
34
|
version: '1.0'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
|
-
version_requirements: *
|
37
|
+
version_requirements: *2155997480
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: scrivener
|
40
|
-
requirement: &
|
40
|
+
requirement: &2155996820 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
@@ -45,10 +45,10 @@ dependencies:
|
|
45
45
|
version: 0.0.3
|
46
46
|
type: :runtime
|
47
47
|
prerelease: false
|
48
|
-
version_requirements: *
|
48
|
+
version_requirements: *2155996820
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: cutest
|
51
|
-
requirement: &
|
51
|
+
requirement: &2155996160 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
53
53
|
requirements:
|
54
54
|
- - ~>
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
version: '0.1'
|
57
57
|
type: :development
|
58
58
|
prerelease: false
|
59
|
-
version_requirements: *
|
59
|
+
version_requirements: *2155996160
|
60
60
|
description: Ohm is a library that allows to store an object in Redis, a persistent
|
61
61
|
key-value database. It includes an extensible list of validations and has very good
|
62
62
|
performance.
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- test/hash_key.rb
|
85
85
|
- test/helper.rb
|
86
86
|
- test/indices.rb
|
87
|
+
- test/issue-52.rb
|
87
88
|
- test/json.rb
|
88
89
|
- test/list.rb
|
89
90
|
- test/lua-save.rb
|
@@ -119,4 +120,3 @@ signing_key:
|
|
119
120
|
specification_version: 3
|
120
121
|
summary: Object-hash mapping library for Redis.
|
121
122
|
test_files: []
|
122
|
-
has_rdoc:
|