ohm 2.1.0 → 2.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 879b753fd8424a547c5cf83753b2f4d7dd817a14
4
- data.tar.gz: fab1f4b6971328047ed17e37d589ebc47c32d082
3
+ metadata.gz: 9c7c0f6876c2563f59ad8e20dcbab465e5cffcb8
4
+ data.tar.gz: e4f06f406172d073ff4ae25ac2d4f9ebad74c034
5
5
  SHA512:
6
- metadata.gz: 3538cccef143743083a6a31c672e4e5bacec42f743196fbcf52f1c68ca4ca3bcc3169f9633a40cdfc1633d48428c60e53e999641e252b640767d85b6b8db3117
7
- data.tar.gz: b51d4ad91623b2357492837a37d1f0f03928e4a2e526849afe4cd558987089fab2a930233065d1230e6dd82c70c6c0341435c0fbbf7f9a467953480e09ec99c4
6
+ metadata.gz: 9a55ef1b2043b4950a617ffa984a01a410a2aea17d656072c4732a995d7dcff550b38437a60ad382b9f30f9b87a2541064597ccb40c3e70043cf38f2cb77638e
7
+ data.tar.gz: ef38056726cf8956480bf728150168a1d2dd1337b5987b9ce6845c771c05f1c53fc38d84d92bab4c8fa015e3069ddfb141f548eee6d68b051417608a9dd2cc23
data/.gems CHANGED
@@ -1,4 +1,5 @@
1
- cutest -v 1.2.1
2
1
  msgpack -v 0.5.8
3
2
  nido -v 0.0.1
4
- redic -v 1.1.0
3
+ redic -v 1.3.0
4
+ cutest -v 1.2.2
5
+ stal -v 0.1.0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 2.2.0
2
+
3
+ - Use Stal for set operations
4
+
5
+ ## 2.1.0
6
+
7
+ - Add `combine` filter
8
+
1
9
  ## 2.0.0
2
10
 
3
11
  - Lists now respond to range.
@@ -228,6 +236,10 @@
228
236
  [scrivener]: https://github.com/soveran/scrivener
229
237
  [redic]: https://github.com/amakawa/redic
230
238
 
239
+ ## 1.4.0
240
+
241
+ - Add `combine` filter
242
+
231
243
  ## 1.3.2
232
244
 
233
245
  - Fetching a batch of objects is now done in batches of 1000 objects at
@@ -29,18 +29,18 @@ class User < Ohm::Model
29
29
  # Because a `User` literally has a `list` of activities, using a Redis
30
30
  # `list` to model the activities would be a good choice. We default to
31
31
  # getting the first 100 activities, and use
32
- # [lrange](http://code.google.com/p/redis/wiki/LrangeCommand) directly.
32
+ # [lrange](http://redis.io/commands/lrange) directly.
33
33
  def activities(start = 0, limit = 100)
34
- key[:activities].lrange(start, start + limit)
34
+ redis.call 'LRANGE', key[:activities], start, start + limit
35
35
  end
36
36
 
37
37
  # Broadcasting a message to all the `followers` of a user would simply
38
38
  # be prepending the message for each if his `followers`. We also use
39
39
  # the Redis command
40
- # [lpush](http://code.google.com/p/redis/wiki/RpushCommand) directly.
40
+ # [lpush](http://redis.io/commands/lpush) directly.
41
41
  def broadcast(str)
42
42
  followers.each do |user|
43
- user.key[:activities].lpush(str)
43
+ redis.call 'LPUSH', user.key[:activities], str
44
44
  end
45
45
  end
46
46
 
@@ -81,8 +81,8 @@ end
81
81
  test "jane following john" do |john, jane|
82
82
  jane.follow(john)
83
83
 
84
- assert [john] == jane.following.to_a
85
- assert [jane] == john.followers.to_a
84
+ assert_equal [john], jane.following.to_a
85
+ assert_equal [jane], john.followers.to_a
86
86
  end
87
87
 
88
88
  # Broadcasting a message should simply notify all the followers of the
@@ -125,17 +125,12 @@ class User
125
125
  # We define a constant where we set the maximum number of activity entries.
126
126
  MAX = 10
127
127
 
128
- # Using `MAX` as the reference, we check if the number of activities exceeds
129
- # `MAX`, and use
130
- # [ltrim](http://code.google.com/p/redis/wiki/LtrimCommand) to truncate
131
- # the activities.
128
+ # Using `MAX` as the reference, we truncate the activities feed using
129
+ # [ltrim](http://redis.io/commands/ltrim).
132
130
  def broadcast(str)
133
131
  followers.each do |user|
134
- user.key[:activities].lpush(str)
135
-
136
- if user.key[:activities].llen > MAX
137
- user.key[:activities].ltrim(0, MAX - 1)
138
- end
132
+ redis.call 'LPUSH', user.key[:activities], str
133
+ redis.call 'LTRIM', user.key[:activities], 0, MAX - 1
139
134
  end
140
135
  end
141
136
  end
@@ -158,5 +153,5 @@ end
158
153
  #
159
154
  # As a final note, keep in mind that the Ohm solution would still need
160
155
  # sharding for large datasets, but that would be again trivial to implement
161
- # using [redis-rb](http://github.com/ezmobius/redis-rb)'s distributed support
156
+ # using [redis-rb](http://github.com/redis/redis-rb)'s distributed support
162
157
  # and sharding it against the *user_id*.
@@ -29,11 +29,11 @@ require "ohm"
29
29
 
30
30
  # We define both a `Video` and `Audio` model, with a `list` of *comments*.
31
31
  class Video < Ohm::Model
32
- list :comments, Comment
32
+ list :comments, :Comment
33
33
  end
34
34
 
35
35
  class Audio < Ohm::Model
36
- list :comments, Comment
36
+ list :comments, :Comment
37
37
  end
38
38
 
39
39
  # The `Comment` model for this example will just contain one attribute called
@@ -65,10 +65,10 @@ test "adding all sorts of comments" do
65
65
  audio.comments.push(audio_comment)
66
66
 
67
67
  assert video.comments.include?(video_comment)
68
- assert video.comments.size == 1
68
+ assert_equal video.comments.size, 1
69
69
 
70
70
  assert audio.comments.include?(audio_comment)
71
- assert audio.comments.size == 1
71
+ assert_equal audio.comments.size, 1
72
72
  end
73
73
 
74
74
 
@@ -100,14 +100,8 @@ test "getting paged chunks of comments" do
100
100
 
101
101
  20.times { |i| video.comments.push(Comment.create(:body => "C#{i + 1}")) }
102
102
 
103
- assert %w(C1 C2 C3 C4 C5) == video.comments[0, 4].map(&:body)
104
- assert %w(C6 C7 C8 C9 C10) == video.comments[5, 9].map(&:body)
105
-
106
- # ** Range style is also supported.
107
- assert %w(C11 C12 C13 C14 C15) == video.comments[10..14].map(&:body)
108
-
109
- # ** Also you can just pass in a single number.
110
- assert "C16" == video.comments[15].body
103
+ assert_equal %w(C1 C2 C3 C4 C5), video.comments.range(0, 4).map(&:body)
104
+ assert_equal %w(C6 C7 C8 C9 C10), video.comments.range(5, 9).map(&:body)
111
105
  end
112
106
 
113
107
  #### Caveats
@@ -121,4 +115,4 @@ end
121
115
  # `SORTED SET`, and to use the timestamp (or the negative of the timestamp) as
122
116
  # the score to maintain the desired order. Deleting a comment from a
123
117
  # `SORTED SET` would be a simple
124
- # [ZREM](http://code.google.com/p/redis/wiki/ZremCommand) call.
118
+ # [ZREM](http://redis.io/commands/zrem) call.
data/lib/ohm.rb CHANGED
@@ -3,8 +3,7 @@
3
3
  require "msgpack"
4
4
  require "nido"
5
5
  require "redic"
6
- require "securerandom"
7
- require_relative "ohm/command"
6
+ require "stal"
8
7
 
9
8
  module Ohm
10
9
  LUA_CACHE = Hash.new { |h, k| h[k] = Hash.new }
@@ -76,7 +75,7 @@ module Ohm
76
75
  Hash[*arr]
77
76
  end
78
77
 
79
- def self.sort(redis, key, options)
78
+ def self.sort_options(options)
80
79
  args = []
81
80
 
82
81
  args.concat(["BY", options[:by]]) if options[:by]
@@ -85,7 +84,7 @@ module Ohm
85
84
  args.concat(options[:order].split(" ")) if options[:order]
86
85
  args.concat(["STORE", options[:store]]) if options[:store]
87
86
 
88
- redis.call("SORT", key, *args)
87
+ return args
89
88
  end
90
89
  end
91
90
 
@@ -285,6 +284,7 @@ module Ohm
285
284
  # # => true
286
285
  #
287
286
  def delete(model)
287
+
288
288
  # LREM key 0 <id> means remove all elements matching <id>
289
289
  # @see http://redis.io/commands/lrem
290
290
  redis.call("LREM", key, 0, model.id)
@@ -321,27 +321,103 @@ module Ohm
321
321
  end
322
322
  end
323
323
 
324
- # Defines most of the methods used by `Set` and `MultiSet`.
325
- class BasicSet
324
+ class Set
326
325
  include Collection
327
326
 
328
- # Allows you to sort by any attribute in the hash, this doesn't include
329
- # the +id+. If you want to sort by ID, use #sort.
327
+ attr :key
328
+ attr :model
329
+ attr :namespace
330
+
331
+ def initialize(model, namespace, key)
332
+ @model = model
333
+ @namespace = namespace
334
+ @key = key
335
+ end
336
+
337
+ # Retrieve a specific element using an ID from this set.
338
+ #
339
+ # Example:
340
+ #
341
+ # # Let's say we got the ID 1 from a request parameter.
342
+ # id = 1
343
+ #
344
+ # # Retrieve the post if it's included in the user's posts.
345
+ # post = user.posts[id]
346
+ #
347
+ def [](id)
348
+ model[id] if exists?(id)
349
+ end
350
+
351
+ # Returns an array with all the ID's of the set.
352
+ #
353
+ # class Post < Ohm::Model
354
+ # end
330
355
  #
331
356
  # class User < Ohm::Model
332
357
  # attribute :name
358
+ # index :name
359
+ #
360
+ # set :posts, :Post
333
361
  # end
334
362
  #
335
- # User.all.sort_by(:name, :order => "ALPHA")
336
- # User.all.sort_by(:name, :order => "ALPHA DESC")
337
- # User.all.sort_by(:name, :order => "ALPHA DESC", :limit => [0, 10])
363
+ # User.create(name: "John")
364
+ # User.create(name: "Jane")
338
365
  #
339
- # Note: This is slower compared to just doing `sort`, specifically
340
- # because Redis has to read each individual hash in order to sort
341
- # them.
366
+ # User.all.ids
367
+ # # => ["1", "2"]
342
368
  #
343
- def sort_by(att, options = {})
344
- sort(options.merge(:by => to_key(att)))
369
+ # User.find(name: "John").union(name: "Jane").ids
370
+ # # => ["1", "2"]
371
+ #
372
+ def ids
373
+ if Array === key
374
+ Stal.solve(redis, key)
375
+ else
376
+ redis.call("SMEMBERS", key)
377
+ end
378
+ end
379
+
380
+ # Returns the total size of the set using SCARD.
381
+ def size
382
+ Stal.solve(redis, ["SCARD", key])
383
+ end
384
+
385
+ # Returns +true+ if +id+ is included in the set. Otherwise, returns +false+.
386
+ #
387
+ # Example:
388
+ #
389
+ # class Post < Ohm::Model
390
+ # end
391
+ #
392
+ # class User < Ohm::Model
393
+ # set :posts, :Post
394
+ # end
395
+ #
396
+ # user = User.create
397
+ # post = Post.create
398
+ # user.posts.add(post)
399
+ #
400
+ # user.posts.exists?('nonexistent') # => false
401
+ # user.posts.exists?(post.id) # => true
402
+ #
403
+ def exists?(id)
404
+ Stal.solve(redis, ["SISMEMBER", key, id]) == 1
405
+ end
406
+
407
+ # Check if a model is included in this set.
408
+ #
409
+ # Example:
410
+ #
411
+ # u = User.create
412
+ #
413
+ # User.all.include?(u)
414
+ # # => true
415
+ #
416
+ # Note: Ohm simply checks that the model's ID is included in the
417
+ # set. It doesn't do any form of type checking.
418
+ #
419
+ def include?(model)
420
+ exists?(model.id)
345
421
  end
346
422
 
347
423
  # Allows you to sort your models using their IDs. This is much
@@ -370,31 +446,30 @@ module Ohm
370
446
  def sort(options = {})
371
447
  if options.has_key?(:get)
372
448
  options[:get] = to_key(options[:get])
373
- return execute { |key| Utils.sort(redis, key, options) }
374
- end
375
449
 
376
- fetch(execute { |key| Utils.sort(redis, key, options) })
450
+ Stal.solve(redis, ["SORT", key, *Utils.sort_options(options)])
451
+ else
452
+ fetch(Stal.solve(redis, ["SORT", key, *Utils.sort_options(options)]))
453
+ end
377
454
  end
378
455
 
379
- # Check if a model is included in this set.
380
- #
381
- # Example:
456
+ # Allows you to sort by any attribute in the hash, this doesn't include
457
+ # the +id+. If you want to sort by ID, use #sort.
382
458
  #
383
- # u = User.create
459
+ # class User < Ohm::Model
460
+ # attribute :name
461
+ # end
384
462
  #
385
- # User.all.include?(u)
386
- # # => true
463
+ # User.all.sort_by(:name, :order => "ALPHA")
464
+ # User.all.sort_by(:name, :order => "ALPHA DESC")
465
+ # User.all.sort_by(:name, :order => "ALPHA DESC", :limit => [0, 10])
387
466
  #
388
- # Note: Ohm simply checks that the model's ID is included in the
389
- # set. It doesn't do any form of type checking.
467
+ # Note: This is slower compared to just doing `sort`, specifically
468
+ # because Redis has to read each individual hash in order to sort
469
+ # them.
390
470
  #
391
- def include?(model)
392
- exists?(model.id)
393
- end
394
-
395
- # Returns the total size of the set using SCARD.
396
- def size
397
- execute { |key| redis.call("SCARD", key) }
471
+ def sort_by(att, options = {})
472
+ sort(options.merge(:by => to_key(att)))
398
473
  end
399
474
 
400
475
  # Syntactic sugar for `sort_by` or `sort` when you only need the
@@ -419,88 +494,6 @@ module Ohm
419
494
  end
420
495
  end
421
496
 
422
- # Returns an array with all the ID's of the set.
423
- #
424
- # class Post < Ohm::Model
425
- # end
426
- #
427
- # class User < Ohm::Model
428
- # attribute :name
429
- # index :name
430
- #
431
- # set :posts, :Post
432
- # end
433
- #
434
- # User.create(name: "John")
435
- # User.create(name: "Jane")
436
- #
437
- # User.all.ids
438
- # # => ["1", "2"]
439
- #
440
- # User.find(name: "John").union(name: "Jane").ids
441
- # # => ["1", "2"]
442
- #
443
- def ids
444
- execute { |key| redis.call("SMEMBERS", key) }
445
- end
446
-
447
- # Retrieve a specific element using an ID from this set.
448
- #
449
- # Example:
450
- #
451
- # # Let's say we got the ID 1 from a request parameter.
452
- # id = 1
453
- #
454
- # # Retrieve the post if it's included in the user's posts.
455
- # post = user.posts[id]
456
- #
457
- def [](id)
458
- model[id] if exists?(id)
459
- end
460
-
461
- # Returns +true+ if +id+ is included in the set. Otherwise, returns +false+.
462
- #
463
- # Example:
464
- #
465
- # class Post < Ohm::Model
466
- # end
467
- #
468
- # class User < Ohm::Model
469
- # set :posts, :Post
470
- # end
471
- #
472
- # user = User.create
473
- # post = Post.create
474
- # user.posts.add(post)
475
- #
476
- # user.posts.exists?('nonexistent') # => false
477
- # user.posts.exists?(post.id) # => true
478
- #
479
- def exists?(id)
480
- execute { |key| redis.call("SISMEMBER", key, id) == 1 }
481
- end
482
-
483
- private
484
- def to_key(att)
485
- if model.counters.include?(att)
486
- namespace["*:counters->%s" % att]
487
- else
488
- namespace["*->%s" % att]
489
- end
490
- end
491
- end
492
-
493
- class Set < BasicSet
494
- attr :key
495
- attr :namespace
496
- attr :model
497
-
498
- def initialize(key, namespace, model)
499
- @key = key
500
- @namespace = namespace
501
- @model = model
502
- end
503
-
504
497
  # Chain new fiters on an existing set.
505
498
  #
506
499
  # Example:
@@ -509,8 +502,8 @@ module Ohm
509
502
  # set.find(:age => 30)
510
503
  #
511
504
  def find(dict)
512
- MultiSet.new(
513
- namespace, model, Command[:sinterstore, key, *model.filters(dict)]
505
+ Ohm::Set.new(
506
+ model, namespace, [:SINTER, key, *model.filters(dict)]
514
507
  )
515
508
  end
516
509
 
@@ -525,7 +518,9 @@ module Ohm
525
518
  # User.find(:name => "John").except(:country => "US")
526
519
  #
527
520
  def except(dict)
528
- MultiSet.new(namespace, model, key).except(dict)
521
+ Ohm::Set.new(
522
+ model, namespace, [:SDIFF, key, [:SUNION, *model.filters(dict)]]
523
+ )
529
524
  end
530
525
 
531
526
  # Perform an intersection between the existent set and
@@ -539,7 +534,9 @@ module Ohm
539
534
  # # The result will include all users with active status
540
535
  # # and with names "John" or "Jane".
541
536
  def combine(dict)
542
- MultiSet.new(namespace, model, key).combine(dict)
537
+ Ohm::Set.new(
538
+ model, namespace, [:SINTER, key, [:SUNION, *model.filters(dict)]]
539
+ )
543
540
  end
544
541
 
545
542
  # Do a union to the existing set using any number of filters.
@@ -553,12 +550,18 @@ module Ohm
553
550
  # User.find(:name => "John").union(:name => "Jane")
554
551
  #
555
552
  def union(dict)
556
- MultiSet.new(namespace, model, key).union(dict)
553
+ Ohm::Set.new(
554
+ model, namespace, [:SUNION, key, [:SINTER, *model.filters(dict)]]
555
+ )
557
556
  end
558
557
 
559
558
  private
560
- def execute
561
- yield key
559
+ def to_key(att)
560
+ if model.counters.include?(att)
561
+ namespace["*:counters->%s" % att]
562
+ else
563
+ namespace["*->%s" % att]
564
+ end
562
565
  end
563
566
 
564
567
  def redis
@@ -567,6 +570,7 @@ module Ohm
567
570
  end
568
571
 
569
572
  class MutableSet < Set
573
+
570
574
  # Add a model directly to the set.
571
575
  #
572
576
  # Example:
@@ -624,128 +628,6 @@ module Ohm
624
628
  end
625
629
  end
626
630
 
627
- # Anytime you filter a set with more than one requirement, you
628
- # internally use a `MultiSet`. `MultiSet` is a bit slower than just
629
- # a `Set` because it has to `SINTERSTORE` all the keys prior to
630
- # retrieving the members, size, etc.
631
- #
632
- # Example:
633
- #
634
- # User.all.kind_of?(Ohm::Set)
635
- # # => true
636
- #
637
- # User.find(:name => "John").kind_of?(Ohm::Set)
638
- # # => true
639
- #
640
- # User.find(:name => "John", :age => 30).kind_of?(Ohm::MultiSet)
641
- # # => true
642
- #
643
- class MultiSet < BasicSet
644
- attr :namespace
645
- attr :model
646
- attr :command
647
-
648
- def initialize(namespace, model, command)
649
- @namespace = namespace
650
- @model = model
651
- @command = command
652
- end
653
-
654
- # Chain new fiters on an existing set.
655
- #
656
- # Example:
657
- #
658
- # set = User.find(:name => "John", :age => 30)
659
- # set.find(:status => 'pending')
660
- #
661
- def find(dict)
662
- MultiSet.new(
663
- namespace, model, Command[:sinterstore, command, intersected(dict)]
664
- )
665
- end
666
-
667
- # Reduce the set using any number of filters.
668
- #
669
- # Example:
670
- #
671
- # set = User.find(:name => "John")
672
- # set.except(:country => "US")
673
- #
674
- # # You can also do it in one line.
675
- # User.find(:name => "John").except(:country => "US")
676
- #
677
- def except(dict)
678
- MultiSet.new(
679
- namespace, model, Command[:sdiffstore, command, unioned(dict)]
680
- )
681
- end
682
-
683
- # Perform an intersection between the existent set and
684
- # the new set created by the union of the passed filters.
685
- #
686
- # Example:
687
- #
688
- # set = User.find(:status => "active")
689
- # set.combine(:name => ["John", "Jane"])
690
- #
691
- # # The result will include all users with active status
692
- # # and with names "John" or "Jane".
693
- def combine(dict)
694
- MultiSet.new(
695
- namespace, model, Command[:sinterstore, command, unioned(dict)]
696
- )
697
- end
698
-
699
- # Do a union to the existing set using any number of filters.
700
- #
701
- # Example:
702
- #
703
- # set = User.find(:name => "John")
704
- # set.union(:name => "Jane")
705
- #
706
- # # You can also do it in one line.
707
- # User.find(:name => "John").union(:name => "Jane")
708
- #
709
- def union(dict)
710
- MultiSet.new(
711
- namespace, model, Command[:sunionstore, command, intersected(dict)]
712
- )
713
- end
714
-
715
- private
716
- def redis
717
- model.redis
718
- end
719
-
720
- def intersected(dict)
721
- Command[:sinterstore, *model.filters(dict)]
722
- end
723
-
724
- def unioned(dict)
725
- Command[:sunionstore, *model.filters(dict)]
726
- end
727
-
728
- def execute
729
- # namespace[:tmp] is where all the temp keys should be stored in.
730
- # redis will be where all the commands are executed against.
731
- response = command.call(namespace[:tmp], redis)
732
-
733
- begin
734
-
735
- # At this point, we have the final aggregated set, which we yield
736
- # to the caller. the caller can do all the normal set operations,
737
- # i.e. SCARD, SMEMBERS, etc.
738
- yield response
739
-
740
- ensure
741
-
742
- # We have to make sure we clean up the temporary keys to avoid
743
- # memory leaks and the unintended explosion of memory usage.
744
- command.clean
745
- end
746
- end
747
- end
748
-
749
631
  # The base class for all your models. In order to better understand
750
632
  # it, here is a semi-realtime explanation of the details involved
751
633
  # when creating a User instance.
@@ -927,9 +809,9 @@ module Ohm
927
809
  keys = filters(dict)
928
810
 
929
811
  if keys.size == 1
930
- Ohm::Set.new(keys.first, key, self)
812
+ Ohm::Set.new(self, key, keys.first)
931
813
  else
932
- Ohm::MultiSet.new(key, self, Command.new(:sinterstore, *keys))
814
+ Ohm::Set.new(self, key, [:SINTER, *keys])
933
815
  end
934
816
  end
935
817
 
@@ -980,7 +862,7 @@ module Ohm
980
862
  define_method name do
981
863
  model = Utils.const(self.class, model)
982
864
 
983
- Ohm::MutableSet.new(key[name], model.key, model)
865
+ Ohm::MutableSet.new(model, model.key, key[name])
984
866
  end
985
867
  end
986
868
 
@@ -1190,7 +1072,7 @@ module Ohm
1190
1072
 
1191
1073
  # An Ohm::Set wrapper for Model.key[:all].
1192
1074
  def self.all
1193
- Set.new(key[:all], key, self)
1075
+ Ohm::Set.new(self, key, key[:all])
1194
1076
  end
1195
1077
 
1196
1078
  # Syntactic sugar for Model.new(atts).save
data/makefile CHANGED
@@ -1,4 +1,9 @@
1
- .PHONY: test
1
+ .PHONY: all test examples
2
+
3
+ all: test
2
4
 
3
5
  test:
4
6
  cutest -r ./test/helper.rb ./test/*.rb
7
+
8
+ examples:
9
+ RUBYLIB="./lib" cutest ./examples/*.rb
data/ohm.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "ohm"
3
- s.version = "2.1.0"
3
+ s.version = "2.2.0"
4
4
  s.summary = %{Object-hash mapping library for Redis.}
5
5
  s.description = %Q{Ohm is a library that allows to store an object in Redis, a persistent key-value database. It has very good performance.}
6
6
  s.authors = ["Michel Martens", "Damian Janowski", "Cyril David"]
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.add_dependency "redic"
16
16
  s.add_dependency "nido"
17
+ s.add_dependency "stal"
17
18
  s.add_dependency "msgpack"
18
19
 
19
20
  s.add_development_dependency "cutest"
data/test/indices.rb CHANGED
@@ -54,12 +54,6 @@ test "avoid intersections with the all collection" do
54
54
  assert_equal "User:indices:email:foo", User.find(:email => "foo").key
55
55
  end
56
56
 
57
- test "cleanup the temporary key after use" do
58
- assert User.find(:email => "foo", :activation_code => "bar").to_a
59
-
60
- assert Ohm.redis.call("KEYS", "User:temp:*").empty?
61
- end
62
-
63
57
  test "allow multiple chained finds" do
64
58
  assert 1 == User.find(:email => "foo").find(:activation_code => "bar").find(:update => "baz").size
65
59
  end
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: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michel Martens
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-01-22 00:00:00.000000000 Z
13
+ date: 2015-03-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: redic
@@ -40,6 +40,20 @@ dependencies:
40
40
  - - '>='
41
41
  - !ruby/object:Gem::Version
42
42
  version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: stal
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
43
57
  - !ruby/object:Gem::Dependency
44
58
  name: msgpack
45
59
  requirement: !ruby/object:Gem::Requirement
@@ -95,14 +109,12 @@ files:
95
109
  - examples/slug.rb
96
110
  - examples/tagging.rb
97
111
  - lib/ohm.rb
98
- - lib/ohm/command.rb
99
112
  - lib/ohm/json.rb
100
113
  - lib/ohm/lua/delete.lua
101
114
  - lib/ohm/lua/save.lua
102
115
  - makefile
103
116
  - ohm.gemspec
104
117
  - test/association.rb
105
- - test/command.rb
106
118
  - test/connection.rb
107
119
  - test/core.rb
108
120
  - test/counters.rb
@@ -143,4 +155,3 @@ signing_key:
143
155
  specification_version: 4
144
156
  summary: Object-hash mapping library for Redis.
145
157
  test_files: []
146
- has_rdoc:
data/lib/ohm/command.rb DELETED
@@ -1,51 +0,0 @@
1
- module Ohm
2
- class Command
3
- def self.[](operation, head, *tail)
4
- return head if tail.empty?
5
-
6
- new(operation, head, *tail)
7
- end
8
-
9
- attr :operation
10
- attr :args
11
- attr :keys
12
-
13
- def initialize(operation, *args)
14
- @operation = operation
15
- @args = args
16
- @keys = []
17
- end
18
-
19
- def call(nido, redis)
20
- newkey(nido, redis) do |key|
21
- redis.call(@operation, key, *params(nido, redis))
22
- end
23
- end
24
-
25
- def clean
26
- keys.each do |key, redis|
27
- redis.call("DEL", key)
28
- end
29
-
30
- subcommands.each { |cmd| cmd.clean }
31
- end
32
-
33
- private
34
- def subcommands
35
- args.select { |arg| arg.respond_to?(:call) }
36
- end
37
-
38
- def params(nido, redis)
39
- args.map { |arg| arg.respond_to?(:call) ? arg.call(nido, redis) : arg }
40
- end
41
-
42
- def newkey(nido, redis)
43
- key = nido[SecureRandom.hex(32)]
44
- keys << [key, redis]
45
-
46
- yield key
47
-
48
- return key
49
- end
50
- end
51
- end
data/test/command.rb DELETED
@@ -1,55 +0,0 @@
1
- require_relative "helper"
2
-
3
- scope do
4
- setup do
5
- redis = Redic.new
6
- redis.call("FLUSHDB")
7
-
8
- nido = Nido.new("User:tmp")
9
-
10
- [1, 2, 3].each { |i| redis.call("SADD", "A", i) }
11
- [1, 4, 5].each { |i| redis.call("SADD", "B", i) }
12
-
13
- [10, 11, 12].each { |i| redis.call("SADD", "C", i) }
14
- [11, 12, 13].each { |i| redis.call("SADD", "D", i) }
15
- [12, 13, 14].each { |i| redis.call("SADD", "E", i) }
16
-
17
- [10, 11, 12].each { |i| redis.call("SADD", "F", i) }
18
- [11, 12, 13].each { |i| redis.call("SADD", "G", i) }
19
- [12, 13, 14].each { |i| redis.call("SADD", "H", i) }
20
-
21
- [redis, nido]
22
- end
23
-
24
- test "special condition: single argument returns that arg" do
25
- assert_equal "A", Ohm::Command[:sinterstore, "A"]
26
- end
27
-
28
- test "full stack test" do |redis, nido|
29
- cmd1 = Ohm::Command[:sinterstore, "A", "B"]
30
-
31
- res = cmd1.call(nido, redis)
32
- assert_equal ["1"], redis.call("SMEMBERS", res)
33
-
34
- cmd1.clean
35
- assert_equal 0, redis.call("EXISTS", res)
36
-
37
- cmd2 = Ohm::Command[:sinterstore, "C", "D", "E"]
38
- cmd3 = Ohm::Command[:sunionstore, cmd1, cmd2]
39
-
40
- res = cmd3.call(nido, redis)
41
- assert_equal ["1", "12"], redis.call("SMEMBERS", res)
42
-
43
- cmd3.clean
44
- assert redis.call("KEYS", nido["*"]).empty?
45
-
46
- cmd4 = Ohm::Command[:sinterstore, "F", "G", "H"]
47
- cmd5 = Ohm::Command[:sdiffstore, cmd3, cmd4]
48
-
49
- res = cmd5.call(nido, redis)
50
- assert_equal ["1"], redis.call("SMEMBERS", res)
51
-
52
- cmd5.clean
53
- assert redis.call("KEYS", nido["*"]).empty?
54
- end
55
- end