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.
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: