modesty 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. data/Gemfile +13 -0
  2. data/Gemfile.lock +18 -0
  3. data/LICENSE +21 -0
  4. data/README.md +121 -0
  5. data/Rakefile +29 -0
  6. data/VERSION +1 -0
  7. data/init.rb +1 -0
  8. data/lib/modesty.rb +26 -0
  9. data/lib/modesty/api.rb +14 -0
  10. data/lib/modesty/core_ext.rb +5 -0
  11. data/lib/modesty/core_ext/array.rb +21 -0
  12. data/lib/modesty/core_ext/fixnum.rb +5 -0
  13. data/lib/modesty/core_ext/hash.rb +39 -0
  14. data/lib/modesty/core_ext/string.rb +9 -0
  15. data/lib/modesty/core_ext/symbol.rb +33 -0
  16. data/lib/modesty/datastore.rb +51 -0
  17. data/lib/modesty/datastore/redis.rb +180 -0
  18. data/lib/modesty/experiment.rb +87 -0
  19. data/lib/modesty/experiment/base.rb +47 -0
  20. data/lib/modesty/experiment/builder.rb +48 -0
  21. data/lib/modesty/experiment/console.rb +4 -0
  22. data/lib/modesty/experiment/data.rb +75 -0
  23. data/lib/modesty/experiment/interface.rb +29 -0
  24. data/lib/modesty/experiment/significance.rb +376 -0
  25. data/lib/modesty/experiment/stats.rb +163 -0
  26. data/lib/modesty/frameworks/rails.rb +27 -0
  27. data/lib/modesty/identity.rb +32 -0
  28. data/lib/modesty/load.rb +80 -0
  29. data/lib/modesty/load/load_experiments.rb +14 -0
  30. data/lib/modesty/load/load_metrics.rb +17 -0
  31. data/lib/modesty/metric.rb +56 -0
  32. data/lib/modesty/metric/base.rb +38 -0
  33. data/lib/modesty/metric/builder.rb +23 -0
  34. data/lib/modesty/metric/data.rb +133 -0
  35. data/modesty.gemspec +192 -0
  36. data/spec/core_ext_spec.rb +17 -0
  37. data/spec/experiment_spec.rb +239 -0
  38. data/spec/identity_spec.rb +161 -0
  39. data/spec/load_spec.rb +87 -0
  40. data/spec/metric_spec.rb +176 -0
  41. data/spec/rails_spec.rb +48 -0
  42. data/spec/redis_spec.rb +29 -0
  43. data/spec/significance_spec.rb +147 -0
  44. data/spec/spec.opts +1 -0
  45. data/test/myapp/config/modesty.yml +9 -0
  46. data/test/myapp/modesty/experiments/cookbook.rb +4 -0
  47. data/test/myapp/modesty/metrics/kitchen_metrics.rb +9 -0
  48. data/test/myapp/modesty/metrics/stove/burner_metrics.rb +2 -0
  49. data/vendor/.piston.yml +8 -0
  50. data/vendor/mock_redis/.gitignore +2 -0
  51. data/vendor/mock_redis/README +8 -0
  52. data/vendor/mock_redis/lib/mock_redis.rb +10 -0
  53. data/vendor/mock_redis/lib/mock_redis/hash.rb +61 -0
  54. data/vendor/mock_redis/lib/mock_redis/list.rb +6 -0
  55. data/vendor/mock_redis/lib/mock_redis/misc.rb +69 -0
  56. data/vendor/mock_redis/lib/mock_redis/set.rb +108 -0
  57. data/vendor/mock_redis/lib/mock_redis/string.rb +32 -0
  58. data/vendor/redis-rb/.gitignore +8 -0
  59. data/vendor/redis-rb/LICENSE +20 -0
  60. data/vendor/redis-rb/README.markdown +129 -0
  61. data/vendor/redis-rb/Rakefile +155 -0
  62. data/vendor/redis-rb/benchmarking/logging.rb +62 -0
  63. data/vendor/redis-rb/benchmarking/pipeline.rb +51 -0
  64. data/vendor/redis-rb/benchmarking/speed.rb +21 -0
  65. data/vendor/redis-rb/benchmarking/suite.rb +24 -0
  66. data/vendor/redis-rb/benchmarking/thread_safety.rb +38 -0
  67. data/vendor/redis-rb/benchmarking/worker.rb +71 -0
  68. data/vendor/redis-rb/examples/basic.rb +15 -0
  69. data/vendor/redis-rb/examples/dist_redis.rb +43 -0
  70. data/vendor/redis-rb/examples/incr-decr.rb +17 -0
  71. data/vendor/redis-rb/examples/list.rb +26 -0
  72. data/vendor/redis-rb/examples/pubsub.rb +31 -0
  73. data/vendor/redis-rb/examples/sets.rb +36 -0
  74. data/vendor/redis-rb/examples/unicorn/config.ru +3 -0
  75. data/vendor/redis-rb/examples/unicorn/unicorn.rb +20 -0
  76. data/vendor/redis-rb/lib/redis.rb +676 -0
  77. data/vendor/redis-rb/lib/redis/client.rb +201 -0
  78. data/vendor/redis-rb/lib/redis/compat.rb +21 -0
  79. data/vendor/redis-rb/lib/redis/connection.rb +134 -0
  80. data/vendor/redis-rb/lib/redis/distributed.rb +526 -0
  81. data/vendor/redis-rb/lib/redis/hash_ring.rb +131 -0
  82. data/vendor/redis-rb/lib/redis/pipeline.rb +13 -0
  83. data/vendor/redis-rb/lib/redis/subscribe.rb +79 -0
  84. data/vendor/redis-rb/redis.gemspec +29 -0
  85. data/vendor/redis-rb/test/commands_on_hashes_test.rb +46 -0
  86. data/vendor/redis-rb/test/commands_on_lists_test.rb +50 -0
  87. data/vendor/redis-rb/test/commands_on_sets_test.rb +78 -0
  88. data/vendor/redis-rb/test/commands_on_sorted_sets_test.rb +109 -0
  89. data/vendor/redis-rb/test/commands_on_strings_test.rb +70 -0
  90. data/vendor/redis-rb/test/commands_on_value_types_test.rb +88 -0
  91. data/vendor/redis-rb/test/connection_handling_test.rb +87 -0
  92. data/vendor/redis-rb/test/db/.gitignore +1 -0
  93. data/vendor/redis-rb/test/distributd_key_tags_test.rb +53 -0
  94. data/vendor/redis-rb/test/distributed_blocking_commands_test.rb +54 -0
  95. data/vendor/redis-rb/test/distributed_commands_on_hashes_test.rb +12 -0
  96. data/vendor/redis-rb/test/distributed_commands_on_lists_test.rb +18 -0
  97. data/vendor/redis-rb/test/distributed_commands_on_sets_test.rb +85 -0
  98. data/vendor/redis-rb/test/distributed_commands_on_strings_test.rb +50 -0
  99. data/vendor/redis-rb/test/distributed_commands_on_value_types_test.rb +73 -0
  100. data/vendor/redis-rb/test/distributed_commands_requiring_clustering_test.rb +141 -0
  101. data/vendor/redis-rb/test/distributed_connection_handling_test.rb +25 -0
  102. data/vendor/redis-rb/test/distributed_internals_test.rb +18 -0
  103. data/vendor/redis-rb/test/distributed_persistence_control_commands_test.rb +24 -0
  104. data/vendor/redis-rb/test/distributed_publish_subscribe_test.rb +90 -0
  105. data/vendor/redis-rb/test/distributed_remote_server_control_commands_test.rb +31 -0
  106. data/vendor/redis-rb/test/distributed_sorting_test.rb +21 -0
  107. data/vendor/redis-rb/test/distributed_test.rb +60 -0
  108. data/vendor/redis-rb/test/distributed_transactions_test.rb +34 -0
  109. data/vendor/redis-rb/test/encoding_test.rb +16 -0
  110. data/vendor/redis-rb/test/helper.rb +86 -0
  111. data/vendor/redis-rb/test/internals_test.rb +27 -0
  112. data/vendor/redis-rb/test/lint/hashes.rb +90 -0
  113. data/vendor/redis-rb/test/lint/internals.rb +53 -0
  114. data/vendor/redis-rb/test/lint/lists.rb +93 -0
  115. data/vendor/redis-rb/test/lint/sets.rb +66 -0
  116. data/vendor/redis-rb/test/lint/sorted_sets.rb +132 -0
  117. data/vendor/redis-rb/test/lint/strings.rb +98 -0
  118. data/vendor/redis-rb/test/lint/value_types.rb +84 -0
  119. data/vendor/redis-rb/test/persistence_control_commands_test.rb +22 -0
  120. data/vendor/redis-rb/test/pipelining_commands_test.rb +78 -0
  121. data/vendor/redis-rb/test/publish_subscribe_test.rb +151 -0
  122. data/vendor/redis-rb/test/redis_mock.rb +64 -0
  123. data/vendor/redis-rb/test/remote_server_control_commands_test.rb +56 -0
  124. data/vendor/redis-rb/test/sorting_test.rb +44 -0
  125. data/vendor/redis-rb/test/test.conf +8 -0
  126. data/vendor/redis-rb/test/thread_safety_test.rb +34 -0
  127. data/vendor/redis-rb/test/transactions_test.rb +91 -0
  128. data/vendor/redis-rb/test/unknown_commands_test.rb +14 -0
  129. data/vendor/redis-rb/test/url_param_test.rb +52 -0
  130. 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,15 @@
1
+ require 'redis'
2
+
3
+ r = Redis.new
4
+
5
+ r.del('foo')
6
+
7
+ puts
8
+
9
+ p'set foo to "bar"'
10
+ r['foo'] = 'bar'
11
+
12
+ puts
13
+
14
+ p 'value of foo'
15
+ p r['foo']
@@ -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,17 @@
1
+ require 'redis'
2
+
3
+ r = Redis.new
4
+
5
+ puts
6
+ p 'incr'
7
+ r.del 'counter'
8
+
9
+ p r.incr('counter')
10
+ p r.incr('counter')
11
+ p r.incr('counter')
12
+
13
+ puts
14
+ p 'decr'
15
+ p r.decr('counter')
16
+ p r.decr('counter')
17
+ p r.decr('counter')
@@ -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,3 @@
1
+ run lambda { |env|
2
+ [200, {"Content-Type" => "text/plain"}, [$redis.randomkey]]
3
+ }
@@ -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"