ohm 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/ohm.rb +82 -75
  2. data/test/issue-52.rb +34 -0
  3. 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| key.sort(options) }
216
+ return execute { |key| db.sort(key, options) }
196
217
  end
197
218
 
198
- fetch(execute { |key| key.sort(options) })
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| key.scard }
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| key.smembers }
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| key.sismember(id) }
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
- key.llen
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[key.lindex(0)]
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[key.lindex(-1)]
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
- key.del
335
- ids.each { |id| key.rpush(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
- key.rpush(model.id)
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
- key.lpush(model.id)
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
- key.lrem(0, model.id)
396
+ db.lrem(key, 0, model.id)
391
397
  end
392
398
 
393
399
  private
394
400
  def ids
395
- key.lrange(0, -1)
401
+ db.lrange(key, 0, -1)
396
402
  end
397
403
 
398
- def fetch(ids)
399
- arr = model.db.pipelined do
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
- key.sadd(model.id)
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
- key.srem(model.id)
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
- key.del
513
- ids.each { |id| key.sadd(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
- model.db.del(*temp_keys)
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
- temp.sinterstore(*list)
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
- main.send(operation, main, temp)
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].sismember(id)
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].hget(val)
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(:name => "John", :status => "pending", :email => "foo@me.com")
818
- # User.find(:provider => "me", :name => "John", :status => "pending").include?(u)
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].hget(name).to_i
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(key.hgetall) unless new?
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] = key.hget(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? ? key.hdel(att) : key.hset(att, val)
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].hincrby(att, count)
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 = key.hgetall
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].sadd(id)
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] = key.hgetall
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].del }
1328
- model.key[:all].srem(id)
1329
- key[:counters].del
1330
- key.del
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].incr
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
- key.del
1439
- key.hmset(*_skip_empty(attributes).to_a.flatten)
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].hget(send(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].hset(val, id)
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].hdel(atts[att.to_s])
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].srem(id)
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
- index.sadd(id)
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.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-04-25 00:00:00.000000000 Z
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: &2156471380 !ruby/object:Gem::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: *2156471380
26
+ version_requirements: *2155998060
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: nest
29
- requirement: &2156470820 !ruby/object:Gem::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: *2156470820
37
+ version_requirements: *2155997480
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: scrivener
40
- requirement: &2156470340 !ruby/object:Gem::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: *2156470340
48
+ version_requirements: *2155996820
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: cutest
51
- requirement: &2156469880 !ruby/object:Gem::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: *2156469880
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: