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.
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"