multi_redis 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 61930ec26bd7e401367fff65403353e02a6d9c7f
4
- data.tar.gz: b9ac5d3857fea25471585dda965067e5509bfee6
3
+ metadata.gz: 679e1a349b9f03a3c6c16634f9a5e8b2051ae5ca
4
+ data.tar.gz: 940d7c657fa3dd83977a536e78e44d366c910072
5
5
  SHA512:
6
- metadata.gz: a1b888ede24eea2c0efb87dfc3db0e4b0cebdad0bd4114c101af9d34afe7d428976b38148d6bba12da701546f8083ad3d898ac0211d273adcc7f85ca8162a1d9
7
- data.tar.gz: 39bee42c79812c41203c46029cfdebb3bbb07f6754bc47188f08a0065ee57ec804ad14e37cbefed9714123835fc9bd2ff83e1c981ffd5de6fa58adb53392c088
6
+ metadata.gz: 5b47def10a775566a8c70cd4b3bbddef629349ebdfe197c2daf6170df572f872a9d7b090fd24d6e530eaf9ffe0067d8314a915aff6d9a8d0ad9eb77374c96e16
7
+ data.tar.gz: ceeca19ecbd97dd7a51ab6dd9d5f4d5b30391bdc8980e0c7eced2f7f45c3c1ebc7e074b74664bf5bb1066eb79378e20c96c6e5aaab1d275853f27beb21d0ae29
data/Gemfile CHANGED
@@ -2,15 +2,12 @@ source "https://rubygems.org"
2
2
 
3
3
  gem 'redis', '~> 3.0'
4
4
 
5
- # Add dependencies to develop your gem here.
6
- # Include everything needed to run rake, tests, features, etc.
7
5
  group :development do
8
- gem 'bundler'
9
- gem 'rake'
10
- gem 'rspec'
11
- gem 'jeweler'
12
- gem 'gem-release'
13
- gem 'rake-version'
14
- gem 'simplecov'
15
- gem 'coveralls', require: false
6
+ gem 'bundler', '~> 1.5'
7
+ gem 'rake', '~> 10.1'
8
+ gem 'rspec', '~> 2.14'
9
+ gem 'jeweler', '~> 2.0'
10
+ gem 'rake-version', '~> 0.4'
11
+ gem 'simplecov', '~> 0.8'
12
+ gem 'coveralls', '~> 0.7', require: false
16
13
  end
data/README.md CHANGED
@@ -25,48 +25,46 @@ Assume you have two separate methods that call redis:
25
25
  $redis = Redis.new
26
26
  $redis.set 'key1', 'foo'
27
27
  $redis.set 'key2', 'bar'
28
- $redis.set 'key3', 'baz'
29
28
 
30
29
  class MyRedisClass
31
30
 
32
31
  def do_stuff
33
-
34
- # run two calls atomically in a MULTI/EXEC
35
- values = $redis.multi do
36
- $redis.get 'key1'
37
- $redis.getset 'key2', 'newvalue'
38
- end
39
-
40
- "value 1 is #{values[0]}, value 2 is #{values[1]}"
32
+ $redis.get 'key1'
41
33
  end
42
34
 
43
35
  def do_other_stuff
44
- value = $redis.get 'key3'
45
- "hey #{value}"
36
+ $redis.get 'key2'
46
37
  end
47
38
  end
48
39
 
49
40
  o = MyRedisClass.new
50
- o.do_stuff #=> "value 1 is foo, value 2 is bar"
51
- o.do_other_stuff #=> "hey baz"
41
+ o.do_stuff #=> "foo"
42
+ o.do_other_stuff #=> "bar"
52
43
  ```
53
44
 
54
- This works, but the redis client executes two separate requests to the server:
45
+ This works, but the redis client executes two separate requests to the server, and waits for the result of the first one to start the second one:
55
46
 
56
47
  ```
57
48
  Request 1:
58
- - MULTI
59
- - GET foo
60
- - GETSET bar newvalue
61
- - EXEC
49
+ - GET key1
62
50
 
63
51
  Request 2:
64
- - GET baz
52
+ - GET key2
53
+ ```
54
+
55
+ `redis-rb` allows you to run both in the same command pipeline:
56
+
57
+ ```rb
58
+ results = $redis.pipelined do
59
+ $redis.get 'key1'
60
+ $redis.get 'key2'
61
+ end
62
+
63
+ results[0] #=> "foo"
64
+ results[1] #=> "bar"
65
65
  ```
66
66
 
67
- The client will wait for the response from the first request before starting the second one.
68
- One round trip could be saved by executing the second request in the same MULTI/EXEC block.
69
- But it would be hard to refactor these two methods to do that while still keeping them separate.
67
+ But it would be hard to refactor the two methods to use a pipeline while still keeping them separate.
70
68
 
71
69
  Multi Redis provides a pattern to structure this code so that your separate redis calls may be executed together in one request when needed.
72
70
 
@@ -74,42 +72,39 @@ Multi Redis provides a pattern to structure this code so that your separate redi
74
72
  $redis = Redis.new
75
73
  $redis.set 'key1', 'foo'
76
74
  $redis.set 'key2', 'bar'
77
- $redis.set 'key3', 'baz'
78
75
 
79
- # Create a redis operation, i.e. an operation that performs redis calls.
76
+ # Create a redis operation, i.e. an operation that performs redis calls, for the first method.
80
77
  do_stuff = MultiRedis::Operation.new do
81
78
 
82
- # Multi blocks will be run atomically in a MULTI/EXEC.
79
+ # Pipelined blocks will be run in a command pipeline.
83
80
  # All redis commands will return futures inside this block, so you can't use the values immediately.
84
- # Store futures in the provided data object for later use.
85
- multi do |mr|
86
- mr.data.value1 = $redis.get 'key1'
87
- mr.data.value2 = $redis.getset 'key2', 'newvalue'
81
+ pipelined do |mr|
82
+ $redis.get 'key1'
88
83
  end
89
84
 
90
- # Run blocks are executed after the previous multi block (or blocks) are completed and all futures have been resolved.
91
- # The data object now contains the values of the futures you stored.
85
+ # This run block will be executed after the pipelined block is completed and all futures have been resolved.
86
+ # The #last_replies method of the Multi Redis context will return the results of all redis calls in the pipelined block.
92
87
  run do |mr|
93
- "value 1 is #{mr.data.value1}, value 2 is #{mr.data.value2}"
88
+ mr.last_replies[0] # => "foo"
94
89
  end
95
90
  end
96
91
 
97
- # The return value of the operation is that of the last run block.
98
- result = do_stuff.execute #=> "value 1 is foo, value 2 is bar"
92
+ # The return value of the operation is that of the last block.
93
+ result = do_stuff.execute #=> "foo"
99
94
 
100
- # Create the other redis operation.
95
+ # Create the redis operation for the other method.
101
96
  do_other_stuff = MultiRedis::Operation.new do
102
97
 
103
98
  multi do |mr|
104
- mr.data.value = $redis.get 'key3'
99
+ $redis.get 'key2'
105
100
  end
106
101
 
107
102
  run do |mr|
108
- "hey #{mr.data.value}"
103
+ mr.last_replies[0] #=> "bar"
109
104
  end
110
105
  end
111
106
 
112
- result = do_other_stuff.execute #=> "hey baz"
107
+ result = do_other_stuff.execute #=> "bar"
113
108
  ```
114
109
 
115
110
  The two operations can still be executed separately like before, but they can also be combined through Multi Redis:
@@ -118,21 +113,18 @@ The two operations can still be executed separately like before, but they can al
118
113
  MultiRedis.execute do_stuff, do_other_stuff
119
114
  ```
120
115
 
121
- All redis calls get grouped into the same MULTI/EXEC:
116
+ Both redis calls get grouped into the same command pipeline:
122
117
 
123
118
  ```
124
119
  One request:
125
- - MULTI
126
120
  - GET foo
127
- - GETSET bar newvalue
128
- - GET baz
129
- - EXEC
121
+ - GET bar
130
122
  ```
131
123
 
132
- The array of results is also returned by the `execute` call:
124
+ The array of results is returned by the `execute` call:
133
125
 
134
126
  ```rb
135
- MultiRedis.execute do_stuff, do_other_stuff #=> [ 'value 1 is foo, value 2 is bar', 'hey baz' ]
127
+ MultiRedis.execute do_stuff, do_other_stuff #=> [ "foo", "bar" ]
136
128
  ```
137
129
 
138
130
  ## Meta
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -1,179 +1,39 @@
1
- require 'ostruct'
2
1
  require 'redis'
3
- require 'thread'
4
2
 
5
3
  module MultiRedis
6
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
7
5
 
8
- @redis = nil
9
- @mutex = Mutex.new
10
- @executing = false
11
- @operations = []
12
- @arguments = []
13
-
14
- def self.redis= redis
15
- @redis = redis
16
- end
17
-
18
- def self.redis
19
- @redis
6
+ class << self
7
+ attr_accessor :redis
20
8
  end
21
9
 
22
10
  def self.execute *args, &block
23
11
 
24
- operations, arguments = @mutex.synchronize do
25
- @operations = args.dup
26
- @arguments = []
27
- @executing = true
28
- yield if block_given?
29
- @executing = false
30
- [ @operations.dup.tap{ |ops| @operations.clear }, @arguments ]
31
- end
32
-
33
- Executor.new(operations, args: arguments).execute
34
- end
35
-
36
- def self.executing?
37
- @executing
38
- end
12
+ options = args.last.kind_of?(Hash) ? args.pop : {}
39
13
 
40
- def self.register_operation op, *args
41
- op.tap do |op|
42
- @operations << op
43
- @arguments << args
14
+ executor = nil
15
+ @mutex.synchronize do
16
+ @executor = Executor.new options
17
+ args.each{ |op| @executor.add op }
18
+ yield if block_given?
19
+ executor = @executor
20
+ @executor = nil
44
21
  end
45
- end
46
-
47
- module Extension
48
22
 
49
- def multi_redis_operation symbol, options = {}, &block
50
- op = Operation.new self, options, &block
51
- define_method symbol do |*args|
52
- op.execute *args
53
- end
54
- self
55
- end
23
+ executor.execute
56
24
  end
57
25
 
58
- class Executor
59
-
60
- def initialize operations, options = {}
61
- @operations = operations
62
- @arguments = options[:args] || []
63
- @redis = options[:redis]
64
- end
65
-
66
- def execute options = {}
67
-
68
- redis = @redis || MultiRedis.redis
69
- contexts = Array.new(@operations.length){ |i| Context.new redis }
70
- stacks = @operations.collect{ |op| op.steps.dup }
71
- args = stacks.collect.with_index{ |a,i| @arguments[i] || [] }
72
- final_results = Array.new @operations.length
26
+ private
73
27
 
74
- while stacks.any? &:any?
75
-
76
- # execute all non-multi steps
77
- stacks.each_with_index do |steps,i|
78
- final_results[i] = steps.shift.execute(contexts[i], args[i]) while steps.first && !steps.first.multi_type
79
- end
80
-
81
- # execute all pipelined steps, if any
82
- pipelined_steps = stacks.collect{ |steps| steps.first && steps.first.multi_type == :pipelined ? steps.shift : nil }
83
- if pipelined_steps.any?
84
- results = []
85
- redis.pipelined do
86
- pipelined_steps.each_with_index do |step,i|
87
- if step
88
- final_results[i] = step.execute(contexts[i], args[i])
89
- contexts[i].last_results = redis.client.futures[results.length, redis.client.futures.length]
90
- results += contexts[i].last_results
91
- end
92
- end
93
- end
94
- pipelined_steps.each_with_index{ |step,i| contexts[i].resolve_futures! if step }
95
- end
96
-
97
- # execute all multi steps, if any
98
- multi_steps = stacks.collect{ |steps| steps.first && steps.first.multi_type == :multi ? steps.shift : nil }
99
- if multi_steps.any?
100
- results = []
101
- redis.multi do
102
- multi_steps.each_with_index do |step,i|
103
- if step
104
- final_results[i] = step.execute(contexts[i], args[i])
105
- contexts[i].last_results = redis.client.futures[results.length, redis.client.futures.length]
106
- results += contexts[i].last_results
107
- end
108
- end
109
- end
110
- multi_steps.each_with_index{ |step,i| contexts[i].resolve_futures! if step }
111
- end
112
- end
113
-
114
- final_results
115
- end
116
- end
117
-
118
- class Operation
119
- attr_reader :steps
120
-
121
- def initialize *args, &block
122
-
123
- options = args.last.kind_of?(Hash) ? args.pop : {}
124
-
125
- @target = args.shift || options[:target] || self
126
- @redis = options[:redis]
127
- @steps = []
128
-
129
- DSL.new(self).instance_eval &block
130
- end
131
-
132
- def execute *args
133
- if MultiRedis.executing?
134
- MultiRedis.register_operation self, *args
135
- else
136
- Executor.new([ self ], args: [ args ], redis: @redis).execute.first
137
- end
138
- end
139
-
140
- def add_step multi_type = nil, &block
141
- @steps << Step.new(@target, multi_type, block)
142
- end
143
-
144
- class DSL
145
-
146
- def initialize op
147
- @op = op
148
- end
149
-
150
- def multi &block
151
- @op.add_step :multi, &block
152
- end
153
-
154
- def pipelined &block
155
- @op.add_step :pipelined, &block
156
- end
28
+ @mutex = Mutex.new
29
+ @executor = nil
157
30
 
158
- def run &block
159
- @op.add_step &block
160
- end
161
- end
31
+ def self.executor
32
+ @executor
162
33
  end
163
34
 
164
- class Step
165
-
166
- def initialize target, multi_type, block
167
- @target, @multi_type, @block = target, multi_type, block
168
- end
169
-
170
- def execute context, *args
171
- @target.instance_exec *args.unshift(context), &@block
172
- end
173
-
174
- def multi_type
175
- @multi_type
176
- end
35
+ def self.executing?
36
+ !!@executor
177
37
  end
178
38
  end
179
39
 
@@ -1,13 +1,28 @@
1
-
2
1
  module MultiRedis
3
2
 
4
3
  class Context
5
- attr_accessor :last_results
4
+ attr_accessor :last_result
5
+ attr_accessor :last_replies
6
6
 
7
- def initialize redis
8
- @last_results = []
9
- @data = Data.new
7
+ def initialize redis, shared_context = nil
10
8
  @redis = redis
9
+ @data = Data.new
10
+ @last_replies = []
11
+ @shared_context = shared_context
12
+ end
13
+
14
+ def execute operation, *args
15
+ @last_result = operation.execute self, *args
16
+ if @resolve = @redis.client.respond_to?(:futures)
17
+ @last_replies = @redis.client.futures[@shared_context.last_replies.length, @redis.client.futures.length]
18
+ @shared_context.last_replies.concat @last_replies
19
+ end
20
+ @shared_context.last_result = @last_result
21
+ @last_result
22
+ end
23
+
24
+ def shared
25
+ @shared_context
11
26
  end
12
27
 
13
28
  def redis
@@ -19,10 +34,11 @@ module MultiRedis
19
34
  end
20
35
 
21
36
  def resolve_futures!
22
- @data.contents.each_key do |k|
23
- @data.contents[k] = @data.contents[k].value if @data.contents[k].is_a? Redis::Future
37
+ return unless @resolve
38
+ @data.each_key do |k|
39
+ @data[k] = @data[k].value if @data[k].is_a? Redis::Future
24
40
  end
25
- @last_results.collect!{ |r| r.is_a?(Redis::Future) ? r.value : r }
41
+ @last_replies.collect!{ |r| r.is_a?(Redis::Future) ? r.value : r }
26
42
  end
27
43
  end
28
44
  end
@@ -1,27 +1,14 @@
1
-
2
1
  module MultiRedis
3
2
 
4
- class Data
5
- attr_reader :contents
6
-
7
- def initialize
8
- @contents = Hash.new
9
- end
10
-
11
- def [] k
12
- @contents[k]
13
- end
14
-
15
- def []= k, v
16
- @contents[k.to_sym] = v
17
- end
3
+ class Data < Hash
18
4
 
19
5
  def method_missing symbol, *args, &block
20
- if @contents.key? symbol
21
- @contents[symbol]
22
- elsif m = symbol.to_s.match(/\A(.*)\=\Z/)
23
- raise "Reserved name" if respond_to? acc = m[1].to_sym
24
- @contents[acc] = args[0]
6
+ if args.empty?
7
+ self[symbol]
8
+ elsif args.length == 1 && m = symbol.to_s.match(/\A(.*)\=\Z/)
9
+ acc = m[1].to_sym
10
+ raise ArgumentError, "Cannot set property #{acc}, method ##{acc} already exists" if respond_to? acc
11
+ self[acc] = args[0]
25
12
  else
26
13
  super symbol, *args, &block
27
14
  end
@@ -0,0 +1,97 @@
1
+ module MultiRedis
2
+
3
+ class Executor
4
+
5
+ def initialize options = {}
6
+ @operations = []
7
+ @redis = options[:redis]
8
+ end
9
+
10
+ def add operation, *args
11
+ @operations << { op: operation, args: args }
12
+ end
13
+
14
+ def execute options = {}
15
+
16
+ redis = @redis || MultiRedis.redis
17
+ shared_context = Context.new redis
18
+
19
+ total = 0
20
+ execution = @operations.collect do |operation|
21
+ total += operation[:op].steps.length
22
+ OperationExecution.new operation[:op], operation[:args], shared_context
23
+ end
24
+
25
+ while execution.any?{ |oe| !oe.done? } && total >= 1
26
+ total -= 1 # safeguard against infinite loop
27
+
28
+ TYPES.each do |type|
29
+
30
+ execution.each do |oe|
31
+ oe.execute_current_step while oe.next? :call
32
+ end
33
+
34
+ if execution.any?{ |oe| oe.next? type }
35
+ shared_context.last_replies.clear
36
+ redis.send type do
37
+ execution.each do |oe|
38
+ oe.execute_current_step if oe.next? type
39
+ end
40
+ end
41
+ execution.each{ |oe| oe.resolve_futures! }
42
+ end
43
+ end
44
+ end
45
+
46
+ execution.each{ |oe| oe.resolve_operation_future! }
47
+ execution.collect!{ |oe| oe.final_results }
48
+ end
49
+
50
+ private
51
+
52
+ TYPES = [ :pipelined, :multi ]
53
+
54
+ class OperationExecution
55
+ attr_reader :final_results
56
+
57
+ def initialize operation, args, shared_context
58
+
59
+ @operation = operation
60
+ @args = args
61
+
62
+ @context = Context.new shared_context.redis, shared_context
63
+ @steps = operation.steps
64
+
65
+ @current_index = 0
66
+ end
67
+
68
+ def done?
69
+ !current_step
70
+ end
71
+
72
+ def next? type
73
+ current_step && current_step.type == type
74
+ end
75
+
76
+ def execute_current_step
77
+ results = @context.execute current_step, *@args
78
+ @current_index += 1
79
+ @final_results = results
80
+ end
81
+
82
+ def resolve_futures!
83
+ @context.resolve_futures!
84
+ end
85
+
86
+ def resolve_operation_future!
87
+ @operation.future.value = @final_results if @operation.future
88
+ end
89
+
90
+ private
91
+
92
+ def current_step
93
+ @steps[@current_index]
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,13 @@
1
+ module MultiRedis
2
+
3
+ module Extension
4
+
5
+ def multi_redis_operation symbol, options = {}, &block
6
+ op = Operation.new options.merge(target: self), &block
7
+ define_method symbol do |*args|
8
+ op.execute *args
9
+ end
10
+ self
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module MultiRedis
2
+
3
+ class FutureNotReady < RuntimeError
4
+
5
+ def initialize
6
+ super "Value will be available once the operation executes."
7
+ end
8
+ end
9
+
10
+ class Future
11
+ FutureNotReady = ::MultiRedis::FutureNotReady.new
12
+ attr_writer :value
13
+
14
+ def initialize value = nil
15
+ @value = value || FutureNotReady
16
+ end
17
+
18
+ def value
19
+ raise @value if @value.kind_of? RuntimeError
20
+ @value
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,61 @@
1
+ module MultiRedis
2
+
3
+ class Operation
4
+ attr_accessor :redis
5
+ attr_reader :steps, :future
6
+
7
+ def initialize options = {}, &block
8
+
9
+ @target = options[:target] || self
10
+ @redis = options[:redis]
11
+ @steps = []
12
+
13
+ configure &block if block
14
+ end
15
+
16
+ def configure &block
17
+ DSL.new(self).instance_eval &block
18
+ end
19
+
20
+ def execute *args
21
+ if MultiRedis.executing?
22
+ MultiRedis.executor.add self, *args
23
+ @future = Future.new
24
+ else
25
+ e = Executor.new redis: @redis
26
+ e.add self, *args
27
+ e.execute.first.tap do |result|
28
+ @future = Future.new result
29
+ end
30
+ end
31
+ end
32
+
33
+ def add type, &block
34
+ raise ArgumentError, "Unknown type #{type}, must be one of #{TYPES.join ', '}." unless TYPES.include? type
35
+ @steps << Step.new(@target, type, block)
36
+ end
37
+
38
+ private
39
+
40
+ TYPES = [ :call, :pipelined, :multi ]
41
+
42
+ class DSL
43
+
44
+ def initialize op
45
+ @op = op
46
+ end
47
+
48
+ def multi &block
49
+ @op.add :multi, &block
50
+ end
51
+
52
+ def pipelined &block
53
+ @op.add :pipelined, &block
54
+ end
55
+
56
+ def run &block
57
+ @op.add :call, &block
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,17 @@
1
+ module MultiRedis
2
+
3
+ class Step
4
+
5
+ def initialize target, type, block
6
+ @target, @type, @block = target, type, block
7
+ end
8
+
9
+ def execute context, *args
10
+ @target.instance_exec *args.unshift(context), &@block
11
+ end
12
+
13
+ def type
14
+ @type
15
+ end
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multi_redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Oulevay
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-09 00:00:00.000000000 Z
11
+ date: 2014-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -28,114 +28,100 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '1.5'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '1.5'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '10.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '10.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '2.14'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '2.14'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: jeweler
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: gem-release
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
73
+ - - "~>"
88
74
  - !ruby/object:Gem::Version
89
- version: '0'
75
+ version: '2.0'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - ">="
80
+ - - "~>"
95
81
  - !ruby/object:Gem::Version
96
- version: '0'
82
+ version: '2.0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: rake-version
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - ">="
87
+ - - "~>"
102
88
  - !ruby/object:Gem::Version
103
- version: '0'
89
+ version: '0.4'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - ">="
94
+ - - "~>"
109
95
  - !ruby/object:Gem::Version
110
- version: '0'
96
+ version: '0.4'
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: simplecov
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
- - - ">="
101
+ - - "~>"
116
102
  - !ruby/object:Gem::Version
117
- version: '0'
103
+ version: '0.8'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
- - - ">="
108
+ - - "~>"
123
109
  - !ruby/object:Gem::Version
124
- version: '0'
110
+ version: '0.8'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: coveralls
127
113
  requirement: !ruby/object:Gem::Requirement
128
114
  requirements:
129
- - - ">="
115
+ - - "~>"
130
116
  - !ruby/object:Gem::Version
131
- version: '0'
117
+ version: '0.7'
132
118
  type: :development
133
119
  prerelease: false
134
120
  version_requirements: !ruby/object:Gem::Requirement
135
121
  requirements:
136
- - - ">="
122
+ - - "~>"
137
123
  - !ruby/object:Gem::Version
138
- version: '0'
124
+ version: '0.7'
139
125
  description: Allows you to organize your redis calls in separate classes but still
140
126
  execute them atomically with pipelined or multi.
141
127
  email: git@alphahydrae.com
@@ -152,6 +138,11 @@ files:
152
138
  - lib/multi_redis.rb
153
139
  - lib/multi_redis/context.rb
154
140
  - lib/multi_redis/data.rb
141
+ - lib/multi_redis/executor.rb
142
+ - lib/multi_redis/extension.rb
143
+ - lib/multi_redis/future.rb
144
+ - lib/multi_redis/operation.rb
145
+ - lib/multi_redis/step.rb
155
146
  homepage: http://github.com/AlphaHydrae/multi_redis
156
147
  licenses:
157
148
  - MIT