redis-copy 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -21,7 +21,7 @@ The current options can be grabbed using the `--help` flag.
21
21
 
22
22
  ```
23
23
  $ redis-copy --help
24
- redis-copy v0.0.2
24
+ redis-copy v0.0.5
25
25
  Usage: redis-copy [options] <source> <destination>
26
26
  <source> and <destination> must be redis connection uris
27
27
  like [redis://]<hostname>[:<port>][/<db>]
@@ -31,13 +31,14 @@ Specific options:
31
31
  auto: uses new if available, otherwise fallback
32
32
  new: use redis DUMP and RESTORE commands (faster)
33
33
  classic: migrates via multiple type-specific commands
34
+ --[no-]pipeline Use redis pipeline where available (default true)
35
+ -d, --[no-]debug Write debug output (default false)
36
+ -t, --[no-]trace Enable backtrace on failure (default false)
37
+ -f, --[no-]fail-fast Abort on first failure (default false)
38
+ --[no-]verify [PERCENT] Verify percentage of transfers -- VERY SLOW (default 0)
39
+ --[no-]prompt Prompt for confirmation (default true)
40
+ --[no-]allow-nonempty Allow non-empty destination (default false)
34
41
  --[no-]dry-run Output configuration and exit
35
- -d, --[no-]debug Write debug output
36
- -t, --[no-]trace Enable backtrace on failure
37
- -f, --[no-]fail-fast Abort on first failure
38
- -y, --yes Automatically accept any prompts
39
- --[no-]allow-nonempty Allow non-empty destination
40
-
41
42
  ```
42
43
 
43
44
  ## Example:
@@ -45,6 +45,17 @@ module RedisCopy
45
45
  ui.notify("FAIL: #{key.dump}")
46
46
  ui.abort if options[:fail_fast]
47
47
  end
48
+
49
+ probably(options[:verify]) do
50
+ if strategem.verify?(key)
51
+ stats[:verified] += 1
52
+ else
53
+ stats[:borked] += 1
54
+ ui.notify("BORK: #{key.dump}")
55
+ ui.abort if options[:fail_fast]
56
+ end
57
+ end
58
+
48
59
  ui.notify("PROGRESS: #{stats.inspect}") if (stats[:attempt] % 1000).zero?
49
60
  end.tap do |stats|
50
61
  ui.notify("DONE: #{stats.inspect}")
@@ -53,6 +64,17 @@ module RedisCopy
53
64
 
54
65
  private
55
66
 
67
+ # yields the block, probably.
68
+ # @param probability [Integer] 0-100
69
+ # @return [void]
70
+ # used by ::copy in determining whether to verify.
71
+ def probably(probability)
72
+ return if probability.zero?
73
+ if probability >= 100 || (Random::rand(100) % 100) < probability
74
+ yield
75
+ end
76
+ end
77
+
56
78
  def same_redis?(redis_a, redis_b)
57
79
  # Redis::Client#id returns the connection uri
58
80
  # e.g. 'redis://localhost:6379/0'
@@ -10,10 +10,12 @@ module RedisCopy
10
10
  ui: :command_line,
11
11
  key_emitter: :default,
12
12
  strategy: :auto,
13
+ verify: 0,
13
14
  pipeline: :true,
14
15
  fail_fast: false,
15
16
  prompt: true,
16
17
  trace: false,
18
+ debug: false,
17
19
  allow_nonempty: false,
18
20
  }.freeze unless defined?(DEFAULTS)
19
21
 
@@ -64,8 +66,21 @@ module RedisCopy
64
66
  options[:fail_fast] = ff
65
67
  end
66
68
 
67
- opts.on('--[no-]prompt', "Prompt for confirmation (default #{DEFAULTS[:prompt]})") do
68
- options[:prompt] = true
69
+ opts.on('--[no-]verify [PERCENT]',
70
+ "Verify percentage of transfers -- VERY SLOW (default #{DEFAULTS[:verify]})"
71
+ ) do |verify|
72
+ options[:verify] = case verify
73
+ when /\A1?[0-9]{2}\z/
74
+ verify.to_i
75
+ when false, 'false', 'none'
76
+ 0
77
+ else
78
+ 100
79
+ end
80
+ end
81
+
82
+ opts.on('--[no-]prompt', "Prompt for confirmation (default #{DEFAULTS[:prompt]})") do |prompt|
83
+ options[:prompt] = prompt
69
84
  end
70
85
 
71
86
  opts.on('--[no-]allow-nonempty', "Allow non-empty destination (default #{DEFAULTS[:allow_nonempty]})") do |allow_nonempty|
@@ -28,6 +28,10 @@ module RedisCopy
28
28
  raise NotImplementedError
29
29
  end
30
30
 
31
+ def dbsize
32
+ @redis.dbsize
33
+ end
34
+
31
35
  def to_s
32
36
  self.class.name.demodulize.humanize
33
37
  end
@@ -36,10 +40,16 @@ module RedisCopy
36
40
  class Default
37
41
  include KeyEmitter
38
42
 
39
- def initialize(redis, ui, options = {})
40
- ui.abort unless ui.confirm? <<-EOWARNING.strip_heredoc
43
+ def keys
44
+ dbsize = self.dbsize
45
+
46
+ # HT: http://stackoverflow.com/a/11466770
47
+ dbsize_str = dbsize.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
48
+
49
+ @ui.abort unless (dbsize < 10_000) || (@ui.confirm? <<-EOWARNING.strip_heredoc)
41
50
  WARNING: #{self} key emitter uses redis.keys('*') to
42
- get its list of keys.
51
+ get its list of keys, and you have #{dbsize_str} keys in
52
+ your source DB.
43
53
 
44
54
  The redis keys command [reference](http://redis.io/commands/keys)
45
55
  says this:
@@ -52,10 +62,7 @@ module RedisCopy
52
62
  > regular application code. If you're looking for a way to find
53
63
  > keys in a subset of your keyspace, consider using sets.
54
64
  EOWARNING
55
- super
56
- end
57
65
 
58
- def keys
59
66
  @ui.debug "REDIS: #{@redis.client.id} KEYS *"
60
67
  @redis.keys('*').to_enum
61
68
  end
@@ -21,6 +21,10 @@ module RedisCopy
21
21
  copierklass.new(source, destination, ui, options)
22
22
  end
23
23
 
24
+ def self.included(base)
25
+ base.send(:include, Verifier)
26
+ end
27
+
24
28
  # @param source [Redis]
25
29
  # @param destination [Redis]
26
30
  def initialize(source, destination, ui, options = {})
@@ -40,5 +44,46 @@ module RedisCopy
40
44
  return super if defined? super
41
45
  raise NotImplementedError
42
46
  end
47
+
48
+ def verify?(key)
49
+ @ui.debug("VERIFY: #{key.dump}")
50
+ type = @src.type(key)
51
+ proc = case type
52
+ when 'string' then proc { |r| r.get key }
53
+ when 'hash' then proc { |r| r.hgetall key }
54
+ when 'zset' then proc { |r| r.zrange(key, 0, -1, :with_scores => true) }
55
+ when 'set' then proc { |r| r.smembers(key).sort }
56
+ when 'list' then proc { |r| r.lrange(key, 0, -1) }
57
+ else
58
+ @ui.debug("BORK: #{key.dump} has unknown type #{type.dump}!")
59
+ return false
60
+ end
61
+
62
+ return false unless same_response?(&proc)
63
+ return false unless same_response? { |r| r.ttl key }
64
+
65
+ true
66
+ end
67
+
68
+ private
69
+
70
+ def same_response?(&blk)
71
+ responses = {
72
+ source: capture_result(@src, &blk),
73
+ destination: capture_result(@dst, &blk)
74
+ }
75
+ if (responses[:source] == responses[:destination])
76
+ return true
77
+ else
78
+ @ui.debug("MISMATCH: #{keys.dump} #{responses.inspect}")
79
+ return false
80
+ end
81
+ end
82
+
83
+ def capture_result(redis, &block)
84
+ return [:returned, block.call(redis)]
85
+ rescue Object => exception
86
+ return [:raised, exception]
87
+ end
43
88
  end
44
89
  end
@@ -6,6 +6,7 @@ module RedisCopy
6
6
  include Strategy
7
7
 
8
8
  def copy(key)
9
+ @ui.debug("COPY: #{key.dump}")
9
10
  vtype = @src.type(key)
10
11
  ttl = @src.ttl(key)
11
12
 
@@ -6,6 +6,8 @@ module RedisCopy
6
6
  include Strategy
7
7
 
8
8
  def copy(key)
9
+ @ui.debug("COPY: #{key.dump}")
10
+
9
11
  ttl = @src.ttl(key)
10
12
  # TTL returns seconds, -1 means none set
11
13
  # RESTORE ttl is in miliseconds, 0 means none set
@@ -1,5 +1,8 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require_relative 'ui/auto_run'
4
+ require_relative 'ui/command_line'
5
+
3
6
  module RedisCopy
4
7
  module UI
5
8
  def self.load(options = {})
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module RedisCopy
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.5'
5
5
  end
@@ -6,9 +6,11 @@ describe RedisCopy::KeyEmitter::Default do
6
6
  let(:ui) { double.as_null_object }
7
7
  let(:instance) { RedisCopy::KeyEmitter::Default.new(redis, ui)}
8
8
  let(:connection_uri) { 'redis://12.34.56.78:9000/15' }
9
+ let(:key_count) { 100_000 }
9
10
 
10
11
  before(:each) do
11
12
  redis.stub_chain('client.id').and_return(connection_uri)
13
+ redis.stub(:dbsize) { key_count }
12
14
  ui.stub(:debug).with(anything)
13
15
  end
14
16
 
@@ -28,6 +30,22 @@ describe RedisCopy::KeyEmitter::Default do
28
30
  exactly(:once)
29
31
  instance.keys
30
32
  end
33
+ context 'when source has > 10,000 keys' do
34
+ let(:key_count) { 100_000 }
35
+ it 'should ask for confirmation' do
36
+ ui.should_receive(:confirm?) do |confirmation|
37
+ confirmation.should match /\b100,000\b/
38
+ end
39
+ instance.keys
40
+ end
41
+ end
42
+ context 'when source has <= 10,000 keys' do
43
+ let(:key_count) { 1_000 }
44
+ it 'should not ask for confirmation' do
45
+ ui.should_not_receive(:confirm?)
46
+ instance.keys
47
+ end
48
+ end
31
49
  end
32
50
  end
33
51
  end
@@ -31,60 +31,76 @@ class RedisMultiplex < Struct.new(:source, :destination)
31
31
  end
32
32
  end
33
33
 
34
+ shared_examples_for(:no_ttl) do
35
+ # key, redis,
36
+ subject { redis.ttl(key) }
37
+ it { should eq -1 }
38
+ end
39
+
40
+ shared_examples_for(:ttl_set) do
41
+ # key, redis, ttl
42
+ subject { redis.ttl(key) }
43
+ it { should eq ttl }
44
+ end
45
+
46
+ shared_examples_for '#verify?' do
47
+ before(:each) do
48
+ ui.stub(:debug).and_call_original
49
+ ui.stub(:notify) do |message|
50
+ puts message
51
+ end
52
+ end
53
+ it 'should verify successfully' do
54
+ strategy.verify?(key).should be_true
55
+ end
56
+ end
57
+
34
58
  shared_examples_for(RedisCopy::Strategy) do
35
59
  let(:key) { rand(16**128).to_s(16) }
36
60
  after(:each) { multiplex.both { |redis| redis.del(key) } }
61
+ let(:ttl) { 100 }
37
62
 
38
63
  context '#copy' do
64
+ before(:each) { populate.call }
39
65
  context 'string' do
40
66
  let(:source_string) { rand(16**256).to_s(16) }
41
- before(:each) { source.set(key, source_string) }
67
+ let(:populate) { proc {source.set(key, source_string)} }
42
68
  [true,false].each do |with_expiry|
43
69
  context "with_expiry(#{with_expiry})" do
44
- before(:each) { source.expire(key, 100) } if with_expiry
70
+ before(:each) { source.expire(key, ttl) } if with_expiry
45
71
  context 'before' do
46
72
  context 'source' do
73
+ let(:redis) { source }
47
74
  subject { source.get(key) }
48
75
  it { should_not be_nil }
49
76
  it { should eq source_string }
50
- context 'ttl' do
51
- subject { source.ttl(key) }
52
- it { should eq 100 } if with_expiry
53
- it { should eq -1 } unless with_expiry
54
- end
77
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
55
78
  end
56
79
  context 'destination' do
80
+ let(:redis) { destination }
57
81
  subject { destination.get(key) }
58
82
  it { should be_nil }
59
- context 'ttl' do
60
- subject { destination.ttl(key) }
61
- it { should eq -1 }
62
- end
83
+ it_should_behave_like :no_ttl
63
84
  end
64
85
  end
65
86
 
66
87
  context 'after' do
67
88
  before(:each) { strategy.copy(key) }
68
89
  context 'source' do
90
+ let(:redis) { source }
69
91
  subject { source.get(key) }
70
92
  it { should_not be_nil }
71
93
  it { should eq source_string }
72
- context 'ttl' do
73
- subject { source.ttl(key) }
74
- it { should eq 100 } if with_expiry
75
- it { should eq -1 } unless with_expiry
76
- end
94
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
77
95
  end
78
96
  context 'destination' do
97
+ let(:redis) { destination }
79
98
  subject { destination.get(key) }
80
99
  it { should_not be_nil }
81
100
  it { should eq source_string }
82
- context 'ttl' do
83
- subject { destination.ttl(key) }
84
- it { should eq 100 } if with_expiry
85
- it { should eq -1 } unless with_expiry
86
- end
101
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
87
102
  end
103
+ it_should_behave_like '#verify?'
88
104
  end
89
105
  end
90
106
  end
@@ -94,53 +110,43 @@ shared_examples_for(RedisCopy::Strategy) do
94
110
  let(:source_list) do
95
111
  %w(foo bar baz buz bingo jango)
96
112
  end
97
- before(:each) { source_list.each{|x| source.rpush(key, x)} }
113
+ let(:populate) { proc { source_list.each{|x| source.rpush(key, x)} } }
98
114
  [true,false].each do |with_expiry|
99
115
  context "with_expiry(#{with_expiry})" do
100
116
  before(:each) { source.expire(key, 100) } if with_expiry
101
117
  context 'before' do
102
118
  context 'source' do
119
+ let(:redis) { source }
103
120
  subject { source.lrange(key, 0, -1) }
104
121
  it { should_not be_empty }
105
122
  it { should eq source_list }
106
- context 'ttl' do
107
- subject { source.ttl(key) }
108
- it { should eq 100 } if with_expiry
109
- it { should eq -1 } unless with_expiry
110
- end
123
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
111
124
  end
112
125
  context 'destination' do
126
+ let(:redis) { destination }
113
127
  subject { destination.lrange(key, 0, -1) }
114
128
  it { should be_empty }
115
- context 'ttl' do
116
- subject { destination.ttl(key) }
117
- it { should eq -1 }
118
- end
129
+ it_should_behave_like :no_ttl
119
130
  end
120
131
  end
121
132
 
122
133
  context 'after' do
123
134
  before(:each) { strategy.copy(key) }
124
135
  context 'source' do
136
+ let(:redis) { source }
125
137
  subject { source.lrange(key, 0, -1) }
126
138
  it { should_not be_empty }
127
139
  it { should eq source_list }
128
- context 'ttl' do
129
- subject { source.ttl(key) }
130
- it { should eq 100 } if with_expiry
131
- it { should eq -1 } unless with_expiry
132
- end
140
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
133
141
  end
134
142
  context 'destination' do
143
+ let(:redis) { destination }
135
144
  subject { destination.lrange(key, 0, -1) }
136
145
  it { should_not be_empty }
137
146
  it { should eq source_list }
138
- context 'ttl' do
139
- subject { destination.ttl(key) }
140
- it { should eq 100 } if with_expiry
141
- it { should eq -1 } unless with_expiry
142
- end
147
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
143
148
  end
149
+ it_should_behave_like '#verify?'
144
150
  end
145
151
  end
146
152
  end
@@ -150,52 +156,41 @@ shared_examples_for(RedisCopy::Strategy) do
150
156
  let(:source_list) do
151
157
  %w(foo bar baz buz bingo jango)
152
158
  end
153
- before(:each) { source_list.each{|x| source.sadd(key, x)} }
159
+ let(:populate) { proc { source_list.each{|x| source.sadd(key, x)} } }
154
160
  [true,false].each do |with_expiry|
155
161
  context "with_expiry(#{with_expiry})" do
156
162
  before(:each) { source.expire(key, 100) } if with_expiry
157
163
  context 'before' do
158
164
  context 'source' do
165
+ let(:redis) { source }
159
166
  subject { source.smembers(key) }
160
167
  it { should_not be_empty }
161
168
  it { should =~ source_list }
162
- context 'ttl' do
163
- subject { source.ttl(key) }
164
- it { should eq 100 } if with_expiry
165
- it { should eq -1 } unless with_expiry
166
- end
169
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
167
170
  end
168
171
  context 'destination' do
172
+ let(:redis) { destination }
169
173
  subject { destination.smembers(key) }
170
174
  it { should be_empty }
171
- context 'ttl' do
172
- subject { destination.ttl(key) }
173
- it { should eq -1 }
174
- end
175
+ it_should_behave_like :no_ttl
175
176
  end
176
177
  end
177
178
 
178
179
  context 'after' do
179
180
  before(:each) { strategy.copy(key) }
180
181
  context 'source' do
182
+ let(:redis) { source }
181
183
  subject { source.smembers(key) }
182
184
  it { should_not be_empty }
183
185
  it { should =~ source_list }
184
- context 'ttl' do
185
- subject { source.ttl(key) }
186
- it { should eq 100 } if with_expiry
187
- it { should eq -1 } unless with_expiry
188
- end
186
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
189
187
  end
190
188
  context 'destination' do
189
+ let(:redis) { destination }
191
190
  subject { destination.smembers(key) }
192
191
  it { should_not be_empty }
193
192
  it { should =~ source_list }
194
- context 'ttl' do
195
- subject { destination.ttl(key) }
196
- it { should eq 100 } if with_expiry
197
- it { should eq -1 } unless with_expiry
198
- end
193
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
199
194
  end
200
195
  end
201
196
  end
@@ -209,53 +204,43 @@ shared_examples_for(RedisCopy::Strategy) do
209
204
  'baz' => 'buz'
210
205
  }
211
206
  end
212
- before(:each) { source.mapped_hmset(key, source_hash) }
207
+ let(:populate) { proc { source.mapped_hmset(key, source_hash) } }
213
208
  [true,false].each do |with_expiry|
214
209
  context "with_expiry(#{with_expiry})" do
215
210
  before(:each) { source.expire(key, 100) } if with_expiry
216
211
  context 'before' do
217
212
  context 'source' do
213
+ let(:redis) { source }
218
214
  subject { source.hgetall(key) }
219
215
  it { should_not be_empty }
220
216
  it { should eq source_hash }
221
- context 'ttl' do
222
- subject { source.ttl(key) }
223
- it { should eq 100 } if with_expiry
224
- it { should eq -1 } unless with_expiry
225
- end
217
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
226
218
  end
227
219
  context 'destination' do
220
+ let(:redis) { destination }
228
221
  subject { destination.hgetall(key) }
229
222
  it { should be_empty }
230
- context 'ttl' do
231
- subject { destination.ttl(key) }
232
- it { should eq -1 }
233
- end
223
+ it_should_behave_like :no_ttl
234
224
  end
235
225
  end
236
226
 
237
227
  context 'after' do
238
228
  before(:each) { strategy.copy(key) }
239
229
  context 'source' do
230
+ let(:redis) { source }
240
231
  subject { source.hgetall(key) }
241
232
  it { should_not be_empty }
242
233
  it { should eq source_hash }
243
- context 'ttl' do
244
- subject { source.ttl(key) }
245
- it { should eq 100 } if with_expiry
246
- it { should eq -1 } unless with_expiry
247
- end
234
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
248
235
  end
249
236
  context 'destination' do
237
+ let(:redis) { destination }
250
238
  subject { destination.hgetall(key) }
251
239
  it { should_not be_empty }
252
240
  it { should eq source_hash }
253
- context 'ttl' do
254
- subject { destination.ttl(key) }
255
- it { should eq 100 } if with_expiry
256
- it { should eq -1 } unless with_expiry
257
- end
241
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
258
242
  end
243
+ it_should_behave_like '#verify?'
259
244
  end
260
245
  end
261
246
  end
@@ -272,53 +257,43 @@ shared_examples_for(RedisCopy::Strategy) do
272
257
  end
273
258
  let(:vs_source_zset) { source_zset.to_a }
274
259
  let(:sv_source_zset) { vs_source_zset.map(&:reverse) }
275
- before(:each) { source.zadd(key, sv_source_zset) }
260
+ let(:populate) { proc { source.zadd(key, sv_source_zset) } }
276
261
  [true,false].each do |with_expiry|
277
262
  context "with_expiry(#{with_expiry})" do
278
263
  before(:each) { source.expire(key, 100) } if with_expiry
279
264
  context 'before' do
280
265
  context 'source' do
266
+ let(:redis) { source }
281
267
  subject { source.zrange(key, 0, -1, :with_scores => true) }
282
268
  it { should_not be_empty }
283
269
  it { should =~ vs_source_zset }
284
- context 'ttl' do
285
- subject { source.ttl(key) }
286
- it { should eq 100 } if with_expiry
287
- it { should eq -1 } unless with_expiry
288
- end
270
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
289
271
  end
290
272
  context 'destination' do
273
+ let(:redis) { destination }
291
274
  subject { destination.zrange(key, 0, -1, :with_scores => true) }
292
275
  it { should be_empty }
293
- context 'ttl' do
294
- subject { destination.ttl(key) }
295
- it { should eq -1 }
296
- end
276
+ it_should_behave_like :no_ttl
297
277
  end
298
278
  end
299
279
 
300
280
  context 'after' do
301
281
  before(:each) { strategy.copy(key) }
302
282
  context 'source' do
283
+ let(:redis) { source }
303
284
  subject { source.zrange(key, 0, -1, :with_scores => true) }
304
285
  it { should_not be_empty }
305
286
  it { should =~ vs_source_zset }
306
- context 'ttl' do
307
- subject { source.ttl(key) }
308
- it { should eq 100 } if with_expiry
309
- it { should eq -1 } unless with_expiry
310
- end
287
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
311
288
  end
312
289
  context 'destination' do
290
+ let(:redis) { destination }
313
291
  subject { destination.zrange(key, 0, -1, :with_scores => true) }
314
292
  it { should_not be_empty }
315
293
  it { should =~ vs_source_zset }
316
- context 'ttl' do
317
- subject { destination.ttl(key) }
318
- it { should eq 100 } if with_expiry
319
- it { should eq -1 } unless with_expiry
320
- end
294
+ it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
321
295
  end
296
+ it_should_behave_like '#verify?'
322
297
  end
323
298
  end
324
299
  end
@@ -328,7 +303,8 @@ end
328
303
 
329
304
  describe RedisCopy::Strategy do
330
305
  let(:options) { Hash.new } # append using before(:each) { options.update(foo: true) }
331
- let(:ui) { double.as_null_object }
306
+ # let(:ui) { double.as_null_object }
307
+ let(:ui) { RedisCopy::UI::CommandLine.new(options) }
332
308
  let(:strategy) { strategy_class.new(source, destination, ui, options)}
333
309
  let(:multiplex) { RedisMultiplex.new(source, destination) }
334
310
  let(:source) { Redis.new(db: 14) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-copy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-11 00:00:00.000000000 Z
12
+ date: 2013-11-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler