modesty 0.1.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.
- data/Gemfile +13 -0
- data/Gemfile.lock +18 -0
- data/LICENSE +21 -0
- data/README.md +121 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/modesty.rb +26 -0
- data/lib/modesty/api.rb +14 -0
- data/lib/modesty/core_ext.rb +5 -0
- data/lib/modesty/core_ext/array.rb +21 -0
- data/lib/modesty/core_ext/fixnum.rb +5 -0
- data/lib/modesty/core_ext/hash.rb +39 -0
- data/lib/modesty/core_ext/string.rb +9 -0
- data/lib/modesty/core_ext/symbol.rb +33 -0
- data/lib/modesty/datastore.rb +51 -0
- data/lib/modesty/datastore/redis.rb +180 -0
- data/lib/modesty/experiment.rb +87 -0
- data/lib/modesty/experiment/base.rb +47 -0
- data/lib/modesty/experiment/builder.rb +48 -0
- data/lib/modesty/experiment/console.rb +4 -0
- data/lib/modesty/experiment/data.rb +75 -0
- data/lib/modesty/experiment/interface.rb +29 -0
- data/lib/modesty/experiment/significance.rb +376 -0
- data/lib/modesty/experiment/stats.rb +163 -0
- data/lib/modesty/frameworks/rails.rb +27 -0
- data/lib/modesty/identity.rb +32 -0
- data/lib/modesty/load.rb +80 -0
- data/lib/modesty/load/load_experiments.rb +14 -0
- data/lib/modesty/load/load_metrics.rb +17 -0
- data/lib/modesty/metric.rb +56 -0
- data/lib/modesty/metric/base.rb +38 -0
- data/lib/modesty/metric/builder.rb +23 -0
- data/lib/modesty/metric/data.rb +133 -0
- data/modesty.gemspec +192 -0
- data/spec/core_ext_spec.rb +17 -0
- data/spec/experiment_spec.rb +239 -0
- data/spec/identity_spec.rb +161 -0
- data/spec/load_spec.rb +87 -0
- data/spec/metric_spec.rb +176 -0
- data/spec/rails_spec.rb +48 -0
- data/spec/redis_spec.rb +29 -0
- data/spec/significance_spec.rb +147 -0
- data/spec/spec.opts +1 -0
- data/test/myapp/config/modesty.yml +9 -0
- data/test/myapp/modesty/experiments/cookbook.rb +4 -0
- data/test/myapp/modesty/metrics/kitchen_metrics.rb +9 -0
- data/test/myapp/modesty/metrics/stove/burner_metrics.rb +2 -0
- data/vendor/.piston.yml +8 -0
- data/vendor/mock_redis/.gitignore +2 -0
- data/vendor/mock_redis/README +8 -0
- data/vendor/mock_redis/lib/mock_redis.rb +10 -0
- data/vendor/mock_redis/lib/mock_redis/hash.rb +61 -0
- data/vendor/mock_redis/lib/mock_redis/list.rb +6 -0
- data/vendor/mock_redis/lib/mock_redis/misc.rb +69 -0
- data/vendor/mock_redis/lib/mock_redis/set.rb +108 -0
- data/vendor/mock_redis/lib/mock_redis/string.rb +32 -0
- data/vendor/redis-rb/.gitignore +8 -0
- data/vendor/redis-rb/LICENSE +20 -0
- data/vendor/redis-rb/README.markdown +129 -0
- data/vendor/redis-rb/Rakefile +155 -0
- data/vendor/redis-rb/benchmarking/logging.rb +62 -0
- data/vendor/redis-rb/benchmarking/pipeline.rb +51 -0
- data/vendor/redis-rb/benchmarking/speed.rb +21 -0
- data/vendor/redis-rb/benchmarking/suite.rb +24 -0
- data/vendor/redis-rb/benchmarking/thread_safety.rb +38 -0
- data/vendor/redis-rb/benchmarking/worker.rb +71 -0
- data/vendor/redis-rb/examples/basic.rb +15 -0
- data/vendor/redis-rb/examples/dist_redis.rb +43 -0
- data/vendor/redis-rb/examples/incr-decr.rb +17 -0
- data/vendor/redis-rb/examples/list.rb +26 -0
- data/vendor/redis-rb/examples/pubsub.rb +31 -0
- data/vendor/redis-rb/examples/sets.rb +36 -0
- data/vendor/redis-rb/examples/unicorn/config.ru +3 -0
- data/vendor/redis-rb/examples/unicorn/unicorn.rb +20 -0
- data/vendor/redis-rb/lib/redis.rb +676 -0
- data/vendor/redis-rb/lib/redis/client.rb +201 -0
- data/vendor/redis-rb/lib/redis/compat.rb +21 -0
- data/vendor/redis-rb/lib/redis/connection.rb +134 -0
- data/vendor/redis-rb/lib/redis/distributed.rb +526 -0
- data/vendor/redis-rb/lib/redis/hash_ring.rb +131 -0
- data/vendor/redis-rb/lib/redis/pipeline.rb +13 -0
- data/vendor/redis-rb/lib/redis/subscribe.rb +79 -0
- data/vendor/redis-rb/redis.gemspec +29 -0
- data/vendor/redis-rb/test/commands_on_hashes_test.rb +46 -0
- data/vendor/redis-rb/test/commands_on_lists_test.rb +50 -0
- data/vendor/redis-rb/test/commands_on_sets_test.rb +78 -0
- data/vendor/redis-rb/test/commands_on_sorted_sets_test.rb +109 -0
- data/vendor/redis-rb/test/commands_on_strings_test.rb +70 -0
- data/vendor/redis-rb/test/commands_on_value_types_test.rb +88 -0
- data/vendor/redis-rb/test/connection_handling_test.rb +87 -0
- data/vendor/redis-rb/test/db/.gitignore +1 -0
- data/vendor/redis-rb/test/distributd_key_tags_test.rb +53 -0
- data/vendor/redis-rb/test/distributed_blocking_commands_test.rb +54 -0
- data/vendor/redis-rb/test/distributed_commands_on_hashes_test.rb +12 -0
- data/vendor/redis-rb/test/distributed_commands_on_lists_test.rb +18 -0
- data/vendor/redis-rb/test/distributed_commands_on_sets_test.rb +85 -0
- data/vendor/redis-rb/test/distributed_commands_on_strings_test.rb +50 -0
- data/vendor/redis-rb/test/distributed_commands_on_value_types_test.rb +73 -0
- data/vendor/redis-rb/test/distributed_commands_requiring_clustering_test.rb +141 -0
- data/vendor/redis-rb/test/distributed_connection_handling_test.rb +25 -0
- data/vendor/redis-rb/test/distributed_internals_test.rb +18 -0
- data/vendor/redis-rb/test/distributed_persistence_control_commands_test.rb +24 -0
- data/vendor/redis-rb/test/distributed_publish_subscribe_test.rb +90 -0
- data/vendor/redis-rb/test/distributed_remote_server_control_commands_test.rb +31 -0
- data/vendor/redis-rb/test/distributed_sorting_test.rb +21 -0
- data/vendor/redis-rb/test/distributed_test.rb +60 -0
- data/vendor/redis-rb/test/distributed_transactions_test.rb +34 -0
- data/vendor/redis-rb/test/encoding_test.rb +16 -0
- data/vendor/redis-rb/test/helper.rb +86 -0
- data/vendor/redis-rb/test/internals_test.rb +27 -0
- data/vendor/redis-rb/test/lint/hashes.rb +90 -0
- data/vendor/redis-rb/test/lint/internals.rb +53 -0
- data/vendor/redis-rb/test/lint/lists.rb +93 -0
- data/vendor/redis-rb/test/lint/sets.rb +66 -0
- data/vendor/redis-rb/test/lint/sorted_sets.rb +132 -0
- data/vendor/redis-rb/test/lint/strings.rb +98 -0
- data/vendor/redis-rb/test/lint/value_types.rb +84 -0
- data/vendor/redis-rb/test/persistence_control_commands_test.rb +22 -0
- data/vendor/redis-rb/test/pipelining_commands_test.rb +78 -0
- data/vendor/redis-rb/test/publish_subscribe_test.rb +151 -0
- data/vendor/redis-rb/test/redis_mock.rb +64 -0
- data/vendor/redis-rb/test/remote_server_control_commands_test.rb +56 -0
- data/vendor/redis-rb/test/sorting_test.rb +44 -0
- data/vendor/redis-rb/test/test.conf +8 -0
- data/vendor/redis-rb/test/thread_safety_test.rb +34 -0
- data/vendor/redis-rb/test/transactions_test.rb +91 -0
- data/vendor/redis-rb/test/unknown_commands_test.rb +14 -0
- data/vendor/redis-rb/test/url_param_test.rb +52 -0
- metadata +277 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Run with
|
|
2
|
+
#
|
|
3
|
+
# $ ruby -Ilib benchmarking/speed.rb
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require "benchmark"
|
|
7
|
+
require "redis"
|
|
8
|
+
|
|
9
|
+
r = Redis.new
|
|
10
|
+
n = (ARGV.shift || 20000).to_i
|
|
11
|
+
|
|
12
|
+
elapsed = Benchmark.realtime do
|
|
13
|
+
# n sets, n gets
|
|
14
|
+
n.times do |i|
|
|
15
|
+
key = "foo#{i}"
|
|
16
|
+
r[key] = key * 10
|
|
17
|
+
r[key]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
puts '%.2f Kops' % (2 * n / 1000 / elapsed)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
def run_in_background(command)
|
|
4
|
+
fork { system command }
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def with_all_segments(&block)
|
|
8
|
+
0.upto(9) do |segment_number|
|
|
9
|
+
block_size = 100000
|
|
10
|
+
start_index = segment_number * block_size
|
|
11
|
+
end_index = start_index + block_size - 1
|
|
12
|
+
block.call(start_index, end_index)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#with_all_segments do |start_index, end_index|
|
|
17
|
+
# puts "Initializing keys from #{start_index} to #{end_index}"
|
|
18
|
+
# system "ruby worker.rb initialize #{start_index} #{end_index} 0"
|
|
19
|
+
#end
|
|
20
|
+
|
|
21
|
+
with_all_segments do |start_index, end_index|
|
|
22
|
+
run_in_background "ruby worker.rb write #{start_index} #{end_index} 10"
|
|
23
|
+
run_in_background "ruby worker.rb read #{start_index} #{end_index} 1"
|
|
24
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Run with
|
|
2
|
+
#
|
|
3
|
+
# $ ruby -Ilib benchmarking/thread_safety.rb
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require "bench"
|
|
8
|
+
rescue LoadError
|
|
9
|
+
$stderr.puts "`gem install bench` and try again."
|
|
10
|
+
exit 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require "redis"
|
|
14
|
+
|
|
15
|
+
def stress(redis)
|
|
16
|
+
redis.flushdb
|
|
17
|
+
|
|
18
|
+
n = (ARGV.shift || 2000).to_i
|
|
19
|
+
|
|
20
|
+
n.times do |i|
|
|
21
|
+
key = "foo:#{i}"
|
|
22
|
+
redis.set key, i
|
|
23
|
+
redis.get key
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
default = Redis.new
|
|
28
|
+
thread_safe = Redis.new(:thread_safe => true)
|
|
29
|
+
|
|
30
|
+
benchmark "Default" do
|
|
31
|
+
stress(default)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
benchmark "Thread-safe" do
|
|
35
|
+
stress(thread_safe)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
run 10
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
BENCHMARK_ROOT = File.dirname(__FILE__)
|
|
2
|
+
REDIS_ROOT = File.join(BENCHMARK_ROOT, "..", "lib")
|
|
3
|
+
|
|
4
|
+
$: << REDIS_ROOT
|
|
5
|
+
require 'redis'
|
|
6
|
+
require 'benchmark'
|
|
7
|
+
|
|
8
|
+
def show_usage
|
|
9
|
+
puts <<-EOL
|
|
10
|
+
Usage: worker.rb [read:write] <start_index> <end_index> <sleep_msec>
|
|
11
|
+
EOL
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def shift_from_argv
|
|
15
|
+
value = ARGV.shift
|
|
16
|
+
unless value
|
|
17
|
+
show_usage
|
|
18
|
+
exit -1
|
|
19
|
+
end
|
|
20
|
+
value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
operation = shift_from_argv.to_sym
|
|
24
|
+
start_index = shift_from_argv.to_i
|
|
25
|
+
end_index = shift_from_argv.to_i
|
|
26
|
+
sleep_msec = shift_from_argv.to_i
|
|
27
|
+
sleep_duration = sleep_msec/1000.0
|
|
28
|
+
|
|
29
|
+
redis = Redis.new
|
|
30
|
+
|
|
31
|
+
case operation
|
|
32
|
+
when :initialize
|
|
33
|
+
|
|
34
|
+
start_index.upto(end_index) do |i|
|
|
35
|
+
redis[i] = 0
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
when :clear
|
|
39
|
+
|
|
40
|
+
start_index.upto(end_index) do |i|
|
|
41
|
+
redis.delete(i)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
when :read, :write
|
|
45
|
+
|
|
46
|
+
puts "Starting to #{operation} at segment #{end_index + 1}"
|
|
47
|
+
|
|
48
|
+
loop do
|
|
49
|
+
t1 = Time.now
|
|
50
|
+
start_index.upto(end_index) do |i|
|
|
51
|
+
case operation
|
|
52
|
+
when :read
|
|
53
|
+
redis.get(i)
|
|
54
|
+
when :write
|
|
55
|
+
redis.incr(i)
|
|
56
|
+
else
|
|
57
|
+
raise "Unknown operation: #{operation}"
|
|
58
|
+
end
|
|
59
|
+
sleep sleep_duration
|
|
60
|
+
end
|
|
61
|
+
t2 = Time.now
|
|
62
|
+
|
|
63
|
+
requests_processed = end_index - start_index
|
|
64
|
+
time = t2 - t1
|
|
65
|
+
puts "#{t2.strftime("%H:%M")} [segment #{end_index + 1}] : Processed #{requests_processed} requests in #{time} seconds - #{(requests_processed/time).round} requests/sec"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
else
|
|
69
|
+
raise "Unknown operation: #{operation}"
|
|
70
|
+
end
|
|
71
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "redis"
|
|
2
|
+
require "redis/distributed"
|
|
3
|
+
|
|
4
|
+
r = Redis::Distributed.new %w[redis://localhost:6379 redis://localhost:6380 redis://localhost:6381 redis://localhost:6382]
|
|
5
|
+
|
|
6
|
+
r.flushdb
|
|
7
|
+
|
|
8
|
+
r['urmom'] = 'urmom'
|
|
9
|
+
r['urdad'] = 'urdad'
|
|
10
|
+
r['urmom1'] = 'urmom1'
|
|
11
|
+
r['urdad1'] = 'urdad1'
|
|
12
|
+
r['urmom2'] = 'urmom2'
|
|
13
|
+
r['urdad2'] = 'urdad2'
|
|
14
|
+
r['urmom3'] = 'urmom3'
|
|
15
|
+
r['urdad3'] = 'urdad3'
|
|
16
|
+
p r['urmom']
|
|
17
|
+
p r['urdad']
|
|
18
|
+
p r['urmom1']
|
|
19
|
+
p r['urdad1']
|
|
20
|
+
p r['urmom2']
|
|
21
|
+
p r['urdad2']
|
|
22
|
+
p r['urmom3']
|
|
23
|
+
p r['urdad3']
|
|
24
|
+
|
|
25
|
+
r.rpush 'listor', 'foo1'
|
|
26
|
+
r.rpush 'listor', 'foo2'
|
|
27
|
+
r.rpush 'listor', 'foo3'
|
|
28
|
+
r.rpush 'listor', 'foo4'
|
|
29
|
+
r.rpush 'listor', 'foo5'
|
|
30
|
+
|
|
31
|
+
p r.rpop('listor')
|
|
32
|
+
p r.rpop('listor')
|
|
33
|
+
p r.rpop('listor')
|
|
34
|
+
p r.rpop('listor')
|
|
35
|
+
p r.rpop('listor')
|
|
36
|
+
|
|
37
|
+
puts "key distribution:"
|
|
38
|
+
|
|
39
|
+
r.ring.nodes.each do |node|
|
|
40
|
+
p [node.client, node.keys("*")]
|
|
41
|
+
end
|
|
42
|
+
r.flushdb
|
|
43
|
+
p r.keys('*')
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'redis'
|
|
3
|
+
|
|
4
|
+
r = Redis.new
|
|
5
|
+
|
|
6
|
+
r.del 'logs'
|
|
7
|
+
|
|
8
|
+
puts
|
|
9
|
+
|
|
10
|
+
p "pushing log messages into a LIST"
|
|
11
|
+
r.rpush 'logs', 'some log message'
|
|
12
|
+
r.rpush 'logs', 'another log message'
|
|
13
|
+
r.rpush 'logs', 'yet another log message'
|
|
14
|
+
r.rpush 'logs', 'also another log message'
|
|
15
|
+
|
|
16
|
+
puts
|
|
17
|
+
p 'contents of logs LIST'
|
|
18
|
+
|
|
19
|
+
p r.lrange('logs', 0, -1)
|
|
20
|
+
|
|
21
|
+
puts
|
|
22
|
+
p 'Trim logs LIST to last 2 elements(easy circular buffer)'
|
|
23
|
+
|
|
24
|
+
r.ltrim('logs', -2, -1)
|
|
25
|
+
|
|
26
|
+
p r.lrange('logs', 0, -1)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require "redis"
|
|
2
|
+
|
|
3
|
+
puts <<-EOS
|
|
4
|
+
To play with this example use redis-cli from another terminal, like this:
|
|
5
|
+
|
|
6
|
+
$ redis-cli publish one hello
|
|
7
|
+
|
|
8
|
+
Finally force the example to exit sending the 'exit' message with:
|
|
9
|
+
|
|
10
|
+
$ redis-cli publish two exit
|
|
11
|
+
|
|
12
|
+
EOS
|
|
13
|
+
|
|
14
|
+
redis = Redis.connect
|
|
15
|
+
|
|
16
|
+
trap(:INT) { puts; exit }
|
|
17
|
+
|
|
18
|
+
redis.subscribe(:one, :two) do |on|
|
|
19
|
+
on.subscribe do |channel, subscriptions|
|
|
20
|
+
puts "Subscribed to ##{channel} (#{subscriptions} subscriptions)"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
on.message do |channel, message|
|
|
24
|
+
puts "##{channel}: #{message}"
|
|
25
|
+
redis.unsubscribe if message == "exit"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
on.unsubscribe do |channel, subscriptions|
|
|
29
|
+
puts "Unsubscribed from ##{channel} (#{subscriptions} subscriptions)"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'redis'
|
|
3
|
+
|
|
4
|
+
r = Redis.new
|
|
5
|
+
|
|
6
|
+
r.del 'foo-tags'
|
|
7
|
+
r.del 'bar-tags'
|
|
8
|
+
|
|
9
|
+
puts
|
|
10
|
+
p "create a set of tags on foo-tags"
|
|
11
|
+
|
|
12
|
+
r.sadd 'foo-tags', 'one'
|
|
13
|
+
r.sadd 'foo-tags', 'two'
|
|
14
|
+
r.sadd 'foo-tags', 'three'
|
|
15
|
+
|
|
16
|
+
puts
|
|
17
|
+
p "create a set of tags on bar-tags"
|
|
18
|
+
|
|
19
|
+
r.sadd 'bar-tags', 'three'
|
|
20
|
+
r.sadd 'bar-tags', 'four'
|
|
21
|
+
r.sadd 'bar-tags', 'five'
|
|
22
|
+
|
|
23
|
+
puts
|
|
24
|
+
p 'foo-tags'
|
|
25
|
+
|
|
26
|
+
p r.smembers('foo-tags')
|
|
27
|
+
|
|
28
|
+
puts
|
|
29
|
+
p 'bar-tags'
|
|
30
|
+
|
|
31
|
+
p r.smembers('bar-tags')
|
|
32
|
+
|
|
33
|
+
puts
|
|
34
|
+
p 'intersection of foo-tags and bar-tags'
|
|
35
|
+
|
|
36
|
+
p r.sinter('foo-tags', 'bar-tags')
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "redis"
|
|
2
|
+
|
|
3
|
+
worker_processes 3
|
|
4
|
+
|
|
5
|
+
# If you set the connection to Redis *before* forking,
|
|
6
|
+
# you will cause forks to share a file descriptor.
|
|
7
|
+
#
|
|
8
|
+
# This causes a concurrency problem by which one fork
|
|
9
|
+
# can read or write to the socket while others are
|
|
10
|
+
# performing other operations.
|
|
11
|
+
#
|
|
12
|
+
# Most likely you'll be getting ProtocolError exceptions
|
|
13
|
+
# mentioning a wrong initial byte in the reply.
|
|
14
|
+
#
|
|
15
|
+
# Thus we need to connect to Redis after forking the
|
|
16
|
+
# worker processes.
|
|
17
|
+
|
|
18
|
+
after_fork do |server, worker|
|
|
19
|
+
$redis = Redis.connect
|
|
20
|
+
end
|
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
|
|
3
|
+
class Redis
|
|
4
|
+
VERSION = "2.1.1"
|
|
5
|
+
|
|
6
|
+
class ProtocolError < RuntimeError
|
|
7
|
+
def initialize(reply_type)
|
|
8
|
+
super(<<-EOS.gsub(/(?:^|\n)\s*/, " "))
|
|
9
|
+
Got '#{reply_type}' as initial reply byte.
|
|
10
|
+
If you're running in a multi-threaded environment, make sure you
|
|
11
|
+
pass the :thread_safe option when initializing the connection.
|
|
12
|
+
If you're in a forking environment, such as Unicorn, you need to
|
|
13
|
+
connect to Redis after forking.
|
|
14
|
+
EOS
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.deprecate(message, trace = caller[0])
|
|
19
|
+
$stderr.puts "\n#{message} (in #{trace})"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr :client
|
|
23
|
+
|
|
24
|
+
def self.connect(options = {})
|
|
25
|
+
options = options.dup
|
|
26
|
+
|
|
27
|
+
require "uri"
|
|
28
|
+
|
|
29
|
+
url = URI(options.delete(:url) || ENV["REDIS_URL"] || "redis://127.0.0.1:6379/0")
|
|
30
|
+
|
|
31
|
+
options[:host] ||= url.host
|
|
32
|
+
options[:port] ||= url.port
|
|
33
|
+
options[:password] ||= url.password
|
|
34
|
+
options[:db] ||= url.path[1..-1].to_i
|
|
35
|
+
|
|
36
|
+
new(options)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.current
|
|
40
|
+
Thread.current[:redis] ||= Redis.connect
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.current=(redis)
|
|
44
|
+
Thread.current[:redis] = redis
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize(options = {})
|
|
48
|
+
if options[:thread_safe]
|
|
49
|
+
@client = Client::ThreadSafe.new(options)
|
|
50
|
+
else
|
|
51
|
+
@client = Client.new(options)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def auth(password)
|
|
56
|
+
@client.call(:auth, password)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def select(db)
|
|
60
|
+
@client.db = db
|
|
61
|
+
@client.call(:select, db)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def info
|
|
65
|
+
Hash[*@client.call(:info).split(/:|\r\n/)]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def config(action, *args)
|
|
69
|
+
response = @client.call(:config, action, *args)
|
|
70
|
+
response = Hash[*response] if action == :get
|
|
71
|
+
response
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def flushdb
|
|
75
|
+
@client.call(:flushdb)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def flushall
|
|
79
|
+
@client.call(:flushall)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def save
|
|
83
|
+
@client.call(:save)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def bgsave
|
|
87
|
+
@client.call(:bgsave)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def bgrewriteaof
|
|
91
|
+
@client.call(:bgrewriteaof)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def get(key)
|
|
95
|
+
@client.call(:get, key)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def getset(key, value)
|
|
99
|
+
@client.call(:getset, key, value)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def mget(*keys)
|
|
103
|
+
@client.call(:mget, *keys)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def append(key, value)
|
|
107
|
+
@client.call(:append, key, value)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def substr(key, start, stop)
|
|
111
|
+
@client.call(:substr, key, start, stop)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def strlen(key)
|
|
115
|
+
@client.call(:strlen, key)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def hgetall(key)
|
|
119
|
+
Hash[*@client.call(:hgetall, key)]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def hget(key, field)
|
|
123
|
+
@client.call(:hget, key, field)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def hdel(key, field)
|
|
127
|
+
@client.call(:hdel, key, field)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def hkeys(key)
|
|
131
|
+
@client.call(:hkeys, key)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def keys(pattern = "*")
|
|
135
|
+
_array @client.call(:keys, pattern)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def randomkey
|
|
139
|
+
@client.call(:randomkey)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def echo(value)
|
|
143
|
+
@client.call(:echo, value)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def ping
|
|
147
|
+
@client.call(:ping)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def lastsave
|
|
151
|
+
@client.call(:lastsave)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def dbsize
|
|
155
|
+
@client.call(:dbsize)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def exists(key)
|
|
159
|
+
_bool @client.call(:exists, key)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def llen(key)
|
|
163
|
+
@client.call(:llen, key)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def lrange(key, start, stop)
|
|
167
|
+
@client.call(:lrange, key, start, stop)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def ltrim(key, start, stop)
|
|
171
|
+
@client.call(:ltrim, key, start, stop)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def lindex(key, index)
|
|
175
|
+
@client.call(:lindex, key, index)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def linsert(key, where, pivot, value)
|
|
179
|
+
@client.call(:linsert, key, where, pivot, value)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def lset(key, index, value)
|
|
183
|
+
@client.call(:lset, key, index, value)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def lrem(key, count, value)
|
|
187
|
+
@client.call(:lrem, key, count, value)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def rpush(key, value)
|
|
191
|
+
@client.call(:rpush, key, value)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def rpushx(key, value)
|
|
195
|
+
@client.call(:rpushx, key, value)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def lpush(key, value)
|
|
199
|
+
@client.call(:lpush, key, value)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def lpushx(key, value)
|
|
203
|
+
@client.call(:lpushx, key, value)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def rpop(key)
|
|
207
|
+
@client.call(:rpop, key)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def blpop(*args)
|
|
211
|
+
@client.call_without_timeout(:blpop, *args)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def brpop(*args)
|
|
215
|
+
@client.call_without_timeout(:brpop, *args)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def rpoplpush(source, destination)
|
|
219
|
+
@client.call(:rpoplpush, source, destination)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def lpop(key)
|
|
223
|
+
@client.call(:lpop, key)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def smembers(key)
|
|
227
|
+
@client.call(:smembers, key)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def sismember(key, member)
|
|
231
|
+
_bool @client.call(:sismember, key, member)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def sadd(key, value)
|
|
235
|
+
_bool @client.call(:sadd, key, value)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def srem(key, value)
|
|
239
|
+
_bool @client.call(:srem, key, value)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def smove(source, destination, member)
|
|
243
|
+
_bool @client.call(:smove, source, destination, member)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def spop(key)
|
|
247
|
+
@client.call(:spop, key)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def scard(key)
|
|
251
|
+
@client.call(:scard, key)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def sinter(*keys)
|
|
255
|
+
@client.call(:sinter, *keys)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def sinterstore(destination, *keys)
|
|
259
|
+
@client.call(:sinterstore, destination, *keys)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def sunion(*keys)
|
|
263
|
+
@client.call(:sunion, *keys)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def sunionstore(destination, *keys)
|
|
267
|
+
@client.call(:sunionstore, destination, *keys)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def sdiff(*keys)
|
|
271
|
+
@client.call(:sdiff, *keys)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def sdiffstore(destination, *keys)
|
|
275
|
+
@client.call(:sdiffstore, destination, *keys)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def srandmember(key)
|
|
279
|
+
@client.call(:srandmember, key)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def zadd(key, score, member)
|
|
283
|
+
_bool @client.call(:zadd, key, score, member)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def zrank(key, member)
|
|
287
|
+
@client.call(:zrank, key, member)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def zrevrank(key, member)
|
|
291
|
+
@client.call(:zrevrank, key, member)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def zincrby(key, increment, member)
|
|
295
|
+
@client.call(:zincrby, key, increment, member)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def zcard(key)
|
|
299
|
+
@client.call(:zcard, key)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def zrange(key, start, stop, options = {})
|
|
303
|
+
command = CommandOptions.new(options) do |c|
|
|
304
|
+
c.bool :with_scores
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
@client.call(:zrange, key, start, stop, *command.to_a)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def zrangebyscore(key, min, max, options = {})
|
|
311
|
+
command = CommandOptions.new(options) do |c|
|
|
312
|
+
c.splat :limit
|
|
313
|
+
c.bool :with_scores
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
@client.call(:zrangebyscore, key, min, max, *command.to_a)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def zcount(key, start, stop)
|
|
320
|
+
@client.call(:zcount, key, start, stop)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def zrevrange(key, start, stop, options = {})
|
|
324
|
+
command = CommandOptions.new(options) do |c|
|
|
325
|
+
c.bool :with_scores
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
@client.call(:zrevrange, key, start, stop, *command.to_a)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def zremrangebyscore(key, min, max)
|
|
332
|
+
@client.call(:zremrangebyscore, key, min, max)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def zremrangebyrank(key, start, stop)
|
|
336
|
+
@client.call(:zremrangebyrank, key, start, stop)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def zscore(key, member)
|
|
340
|
+
@client.call(:zscore, key, member)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def zrem(key, member)
|
|
344
|
+
_bool @client.call(:zrem, key, member)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def zinterstore(destination, keys, options = {})
|
|
348
|
+
command = CommandOptions.new(options) do |c|
|
|
349
|
+
c.splat :weights
|
|
350
|
+
c.value :aggregate
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
@client.call(:zinterstore, destination, keys.size, *(keys + command.to_a))
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def zunionstore(destination, keys, options = {})
|
|
357
|
+
command = CommandOptions.new(options) do |c|
|
|
358
|
+
c.splat :weights
|
|
359
|
+
c.value :aggregate
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
@client.call(:zunionstore, destination, keys.size, *(keys + command.to_a))
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def move(key, db)
|
|
366
|
+
_bool @client.call(:move, key, db)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def setnx(key, value)
|
|
370
|
+
_bool @client.call(:setnx, key, value)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def del(*keys)
|
|
374
|
+
@client.call(:del, *keys)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def rename(old_name, new_name)
|
|
378
|
+
@client.call(:rename, old_name, new_name)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def renamenx(old_name, new_name)
|
|
382
|
+
_bool @client.call(:renamenx, old_name, new_name)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def expire(key, seconds)
|
|
386
|
+
_bool @client.call(:expire, key, seconds)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def persist(key)
|
|
390
|
+
_bool @client.call(:persist, key)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def ttl(key)
|
|
394
|
+
@client.call(:ttl, key)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def expireat(key, unix_time)
|
|
398
|
+
_bool @client.call(:expireat, key, unix_time)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def hset(key, field, value)
|
|
402
|
+
_bool @client.call(:hset, key, field, value)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def hsetnx(key, field, value)
|
|
406
|
+
_bool @client.call(:hsetnx, key, field, value)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def hmset(key, *attrs)
|
|
410
|
+
@client.call(:hmset, key, *attrs)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def mapped_hmset(key, hash)
|
|
414
|
+
hmset(key, *hash.to_a.flatten)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def hmget(key, *fields)
|
|
418
|
+
@client.call(:hmget, key, *fields)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def mapped_hmget(key, *fields)
|
|
422
|
+
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def hlen(key)
|
|
426
|
+
@client.call(:hlen, key)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def hvals(key)
|
|
430
|
+
@client.call(:hvals, key)
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def hincrby(key, field, increment)
|
|
434
|
+
@client.call(:hincrby, key, field, increment)
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def discard
|
|
438
|
+
@client.call(:discard)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def hexists(key, field)
|
|
442
|
+
_bool @client.call(:hexists, key, field)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def monitor(&block)
|
|
446
|
+
@client.call_loop(:monitor, &block)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def debug(*args)
|
|
450
|
+
@client.call(:debug, *args)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def sync
|
|
454
|
+
@client.call(:sync)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def [](key)
|
|
458
|
+
get(key)
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def []=(key,value)
|
|
462
|
+
set(key, value)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def set(key, value)
|
|
466
|
+
@client.call(:set, key, value)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def setex(key, ttl, value)
|
|
470
|
+
@client.call(:setex, key, ttl, value)
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def mset(*args)
|
|
474
|
+
@client.call(:mset, *args)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def mapped_mset(hash)
|
|
478
|
+
mset(*hash.to_a.flatten)
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def msetnx(*args)
|
|
482
|
+
@client.call(:msetnx, *args)
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def mapped_msetnx(hash)
|
|
486
|
+
msetnx(*hash.to_a.flatten)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def mapped_mget(*keys)
|
|
490
|
+
Hash[*keys.zip(mget(*keys)).flatten]
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def sort(key, options = {})
|
|
494
|
+
command = CommandOptions.new(options) do |c|
|
|
495
|
+
c.value :by
|
|
496
|
+
c.splat :limit
|
|
497
|
+
c.multi :get
|
|
498
|
+
c.words :order
|
|
499
|
+
c.value :store
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
@client.call(:sort, key, *command.to_a)
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def incr(key)
|
|
506
|
+
@client.call(:incr, key)
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def incrby(key, increment)
|
|
510
|
+
@client.call(:incrby, key, increment)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def decr(key)
|
|
514
|
+
@client.call(:decr, key)
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def decrby(key, decrement)
|
|
518
|
+
@client.call(:decrby, key, decrement)
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def type(key)
|
|
522
|
+
@client.call(:type, key)
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def quit
|
|
526
|
+
@client.call(:quit)
|
|
527
|
+
rescue Errno::ECONNRESET
|
|
528
|
+
ensure
|
|
529
|
+
@client.disconnect
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def shutdown
|
|
533
|
+
@client.call(:shutdown)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def slaveof(host, port)
|
|
537
|
+
@client.call(:slaveof, host, port)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def pipelined
|
|
541
|
+
original, @client = @client, Pipeline.new
|
|
542
|
+
yield
|
|
543
|
+
original.call_pipelined(@client.commands) unless @client.commands.empty?
|
|
544
|
+
ensure
|
|
545
|
+
@client = original
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def watch(*keys)
|
|
549
|
+
@client.call(:watch, *keys)
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def unwatch
|
|
553
|
+
@client.call(:unwatch)
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def exec
|
|
557
|
+
@client.call(:exec)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def multi(&block)
|
|
561
|
+
result = @client.call :multi
|
|
562
|
+
|
|
563
|
+
return result unless block_given?
|
|
564
|
+
|
|
565
|
+
begin
|
|
566
|
+
yield(self)
|
|
567
|
+
rescue Exception => e
|
|
568
|
+
discard
|
|
569
|
+
raise e
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
exec
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def publish(channel, message)
|
|
576
|
+
@client.call(:publish, channel, message)
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def subscribed?
|
|
580
|
+
@client.kind_of? SubscribedClient
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def unsubscribe(*channels)
|
|
584
|
+
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
|
|
585
|
+
@client.unsubscribe(*channels)
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def punsubscribe(*channels)
|
|
589
|
+
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
|
|
590
|
+
@client.punsubscribe(*channels)
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
def subscribe(*channels, &block)
|
|
594
|
+
subscription(:subscribe, channels, block)
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def psubscribe(*channels, &block)
|
|
598
|
+
subscription(:psubscribe, channels, block)
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def id
|
|
602
|
+
@client.id
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def inspect
|
|
606
|
+
"#<Redis client v#{Redis::VERSION} connected to #{id} (Redis v#{info["redis_version"]})>"
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def method_missing(command, *args)
|
|
610
|
+
@client.call(command, *args)
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
class CommandOptions
|
|
614
|
+
def initialize(options)
|
|
615
|
+
@result = []
|
|
616
|
+
@options = options
|
|
617
|
+
yield(self)
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
def bool(name)
|
|
621
|
+
insert(name) { |argument, value| [argument] }
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
def value(name)
|
|
625
|
+
insert(name) { |argument, value| [argument, value] }
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def splat(name)
|
|
629
|
+
insert(name) { |argument, value| [argument, *value] }
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def multi(name)
|
|
633
|
+
insert(name) { |argument, value| [argument].product(Array(value)).flatten }
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def words(name)
|
|
637
|
+
insert(name) { |argument, value| value.split(" ") }
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
def to_a
|
|
641
|
+
@result
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
def insert(name)
|
|
645
|
+
@result += yield(name.to_s.upcase.gsub("_", ""), @options[name]) if @options[name]
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
private
|
|
650
|
+
|
|
651
|
+
def _bool(value)
|
|
652
|
+
value == 1
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
def _array(value)
|
|
656
|
+
value.kind_of?(Array) ? value : value.split(" ")
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def subscription(method, channels, block)
|
|
660
|
+
return @client.call(method, *channels) if subscribed?
|
|
661
|
+
|
|
662
|
+
begin
|
|
663
|
+
original, @client = @client, SubscribedClient.new(@client)
|
|
664
|
+
@client.send(method, *channels, &block)
|
|
665
|
+
ensure
|
|
666
|
+
@client = original
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
require "redis/connection" unless defined?(Redis::Connection)
|
|
673
|
+
require "redis/client"
|
|
674
|
+
require "redis/pipeline"
|
|
675
|
+
require "redis/subscribe"
|
|
676
|
+
require "redis/compat"
|