redis2-namespaced 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.order +170 -0
  4. data/.travis/Gemfile +11 -0
  5. data/.travis.yml +55 -0
  6. data/.yardopts +3 -0
  7. data/CHANGELOG.md +285 -0
  8. data/LICENSE +20 -0
  9. data/README.md +251 -0
  10. data/Rakefile +403 -0
  11. data/benchmarking/logging.rb +71 -0
  12. data/benchmarking/pipeline.rb +51 -0
  13. data/benchmarking/speed.rb +21 -0
  14. data/benchmarking/suite.rb +24 -0
  15. data/benchmarking/worker.rb +71 -0
  16. data/examples/basic.rb +15 -0
  17. data/examples/dist_redis.rb +43 -0
  18. data/examples/incr-decr.rb +17 -0
  19. data/examples/list.rb +26 -0
  20. data/examples/pubsub.rb +37 -0
  21. data/examples/sets.rb +36 -0
  22. data/examples/unicorn/config.ru +3 -0
  23. data/examples/unicorn/unicorn.rb +20 -0
  24. data/lib/redis2/client.rb +419 -0
  25. data/lib/redis2/connection/command_helper.rb +44 -0
  26. data/lib/redis2/connection/hiredis.rb +63 -0
  27. data/lib/redis2/connection/registry.rb +12 -0
  28. data/lib/redis2/connection/ruby.rb +322 -0
  29. data/lib/redis2/connection/synchrony.rb +124 -0
  30. data/lib/redis2/connection.rb +9 -0
  31. data/lib/redis2/distributed.rb +853 -0
  32. data/lib/redis2/errors.rb +40 -0
  33. data/lib/redis2/hash_ring.rb +131 -0
  34. data/lib/redis2/pipeline.rb +141 -0
  35. data/lib/redis2/subscribe.rb +83 -0
  36. data/lib/redis2/version.rb +3 -0
  37. data/lib/redis2.rb +2533 -0
  38. data/redis.gemspec +43 -0
  39. data/test/bitpos_test.rb +69 -0
  40. data/test/blocking_commands_test.rb +42 -0
  41. data/test/command_map_test.rb +30 -0
  42. data/test/commands_on_hashes_test.rb +21 -0
  43. data/test/commands_on_lists_test.rb +20 -0
  44. data/test/commands_on_sets_test.rb +77 -0
  45. data/test/commands_on_sorted_sets_test.rb +109 -0
  46. data/test/commands_on_strings_test.rb +101 -0
  47. data/test/commands_on_value_types_test.rb +131 -0
  48. data/test/connection_handling_test.rb +189 -0
  49. data/test/db/.gitkeep +0 -0
  50. data/test/distributed_blocking_commands_test.rb +46 -0
  51. data/test/distributed_commands_on_hashes_test.rb +10 -0
  52. data/test/distributed_commands_on_lists_test.rb +22 -0
  53. data/test/distributed_commands_on_sets_test.rb +83 -0
  54. data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
  55. data/test/distributed_commands_on_strings_test.rb +59 -0
  56. data/test/distributed_commands_on_value_types_test.rb +95 -0
  57. data/test/distributed_commands_requiring_clustering_test.rb +164 -0
  58. data/test/distributed_connection_handling_test.rb +23 -0
  59. data/test/distributed_internals_test.rb +70 -0
  60. data/test/distributed_key_tags_test.rb +52 -0
  61. data/test/distributed_persistence_control_commands_test.rb +26 -0
  62. data/test/distributed_publish_subscribe_test.rb +92 -0
  63. data/test/distributed_remote_server_control_commands_test.rb +66 -0
  64. data/test/distributed_scripting_test.rb +102 -0
  65. data/test/distributed_sorting_test.rb +20 -0
  66. data/test/distributed_test.rb +58 -0
  67. data/test/distributed_transactions_test.rb +32 -0
  68. data/test/encoding_test.rb +18 -0
  69. data/test/error_replies_test.rb +59 -0
  70. data/test/helper.rb +218 -0
  71. data/test/helper_test.rb +24 -0
  72. data/test/internals_test.rb +410 -0
  73. data/test/lint/blocking_commands.rb +150 -0
  74. data/test/lint/hashes.rb +162 -0
  75. data/test/lint/lists.rb +143 -0
  76. data/test/lint/sets.rb +125 -0
  77. data/test/lint/sorted_sets.rb +238 -0
  78. data/test/lint/strings.rb +260 -0
  79. data/test/lint/value_types.rb +122 -0
  80. data/test/persistence_control_commands_test.rb +26 -0
  81. data/test/pipelining_commands_test.rb +242 -0
  82. data/test/publish_subscribe_test.rb +210 -0
  83. data/test/remote_server_control_commands_test.rb +117 -0
  84. data/test/scanning_test.rb +413 -0
  85. data/test/scripting_test.rb +78 -0
  86. data/test/sorting_test.rb +59 -0
  87. data/test/support/connection/hiredis.rb +1 -0
  88. data/test/support/connection/ruby.rb +1 -0
  89. data/test/support/connection/synchrony.rb +17 -0
  90. data/test/support/redis_mock.rb +115 -0
  91. data/test/support/wire/synchrony.rb +24 -0
  92. data/test/support/wire/thread.rb +5 -0
  93. data/test/synchrony_driver.rb +88 -0
  94. data/test/test.conf +9 -0
  95. data/test/thread_safety_test.rb +32 -0
  96. data/test/transactions_test.rb +264 -0
  97. data/test/unknown_commands_test.rb +14 -0
  98. data/test/url_param_test.rb +132 -0
  99. metadata +226 -0
data/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link]
2
+
3
+ [travis-image]: https://secure.travis-ci.org/redis/redis-rb.png?branch=master
4
+ [travis-link]: http://travis-ci.org/redis/redis-rb
5
+ [travis-home]: http://travis-ci.org/
6
+ [inchpages-image]: http://inch-pages.github.io/github/redis/redis-rb.png
7
+ [inchpages-link]: http://inch-pages.github.io/github/redis/redis-rb
8
+
9
+ A Ruby client library for [Redis2][redis-home].
10
+
11
+ [redis-home]: http://redis.io
12
+
13
+ A Ruby client that tries to match Redis2' API one-to-one, while still
14
+ providing an idiomatic interface. It features thread-safety, client-side
15
+ sharding, pipelining, and an obsession for performance.
16
+
17
+ ## Upgrading from 2.x to 3.0
18
+
19
+ Please refer to the [CHANGELOG][changelog-3.0.0] for a summary of the
20
+ most important changes, as well as a full list of changes.
21
+
22
+ [changelog-3.0.0]: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#300
23
+
24
+ ## Getting started
25
+
26
+ As of version 2.0 this client only targets Redis2 version 2.0 and higher.
27
+ You can use an older version of this client if you need to interface
28
+ with a Redis2 instance older than 2.0, but this is no longer supported.
29
+
30
+ You can connect to Redis2 by instantiating the `Redis2` class:
31
+
32
+ ```ruby
33
+ require "redis"
34
+
35
+ redis = Redis2.new
36
+ ```
37
+
38
+ This assumes Redis2 was started with a default configuration, and is
39
+ listening on `localhost`, port 6379. If you need to connect to a remote
40
+ server or a different port, try:
41
+
42
+ ```ruby
43
+ redis = Redis2.new(:host => "10.0.1.1", :port => 6380, :db => 15)
44
+ ```
45
+
46
+ You can also specify connection options as an URL:
47
+
48
+ ```ruby
49
+ redis = Redis2.new(:url => "redis://:p4ssw0rd@10.0.1.1:6380/15")
50
+ ```
51
+
52
+ By default, the client will try to read the `REDIS_URL` environment variable
53
+ and use that as URL to connect to. The above statement is therefore equivalent
54
+ to setting this environment variable and calling `Redis2.new` without arguments.
55
+
56
+ To connect to Redis2 listening on a Unix socket, try:
57
+
58
+ ```ruby
59
+ redis = Redis2.new(:path => "/tmp/redis.sock")
60
+ ```
61
+
62
+ To connect to a password protected Redis2 instance, use:
63
+
64
+ ```ruby
65
+ redis = Redis2.new(:password => "mysecret")
66
+ ```
67
+
68
+ The Redis2 class exports methods that are named identical to the commands
69
+ they execute. The arguments these methods accept are often identical to
70
+ the arguments specified on the [Redis2 website][redis-commands]. For
71
+ instance, the `SET` and `GET` commands can be called like this:
72
+
73
+ [redis-commands]: http://redis.io/commands
74
+
75
+ ```ruby
76
+ redis.set("mykey", "hello world")
77
+ # => "OK"
78
+
79
+ redis.get("mykey")
80
+ # => "hello world"
81
+ ```
82
+
83
+ All commands, their arguments and return values are documented, and
84
+ available on [rdoc.info][rdoc].
85
+
86
+ [rdoc]: http://rdoc.info/github/redis/redis-rb/
87
+
88
+ ## Storing objects
89
+
90
+ Redis2 only stores strings as values. If you want to store an object, you
91
+ can use a serialization mechanism such as JSON:
92
+
93
+ ```ruby
94
+ require "json"
95
+
96
+ redis.set "foo", [1, 2, 3].to_json
97
+ # => OK
98
+
99
+ JSON.parse(redis.get("foo"))
100
+ # => [1, 2, 3]
101
+ ```
102
+
103
+ ## Pipelining
104
+
105
+ When multiple commands are executed sequentially, but are not dependent,
106
+ the calls can be *pipelined*. This means that the client doesn't wait
107
+ for reply of the first command before sending the next command. The
108
+ advantage is that multiple commands are sent at once, resulting in
109
+ faster overall execution.
110
+
111
+ The client can be instructed to pipeline commands by using the
112
+ `#pipelined` method. After the block is executed, the client sends all
113
+ commands to Redis2 and gathers their replies. These replies are returned
114
+ by the `#pipelined` method.
115
+
116
+ ```ruby
117
+ redis.pipelined do
118
+ redis.set "foo", "bar"
119
+ redis.incr "baz"
120
+ end
121
+ # => ["OK", 1]
122
+ ```
123
+
124
+ ### Executing commands atomically
125
+
126
+ You can use `MULTI/EXEC` to run a number of commands in an atomic
127
+ fashion. This is similar to executing a pipeline, but the commands are
128
+ preceded by a call to `MULTI`, and followed by a call to `EXEC`. Like
129
+ the regular pipeline, the replies to the commands are returned by the
130
+ `#multi` method.
131
+
132
+ ```ruby
133
+ redis.multi do
134
+ redis.set "foo", "bar"
135
+ redis.incr "baz"
136
+ end
137
+ # => ["OK", 1]
138
+ ```
139
+
140
+ ### Futures
141
+
142
+ Replies to commands in a pipeline can be accessed via the *futures* they
143
+ emit (since redis-rb 3.0). All calls inside a pipeline block return a
144
+ `Future` object, which responds to the `#value` method. When the
145
+ pipeline has succesfully executed, all futures are assigned their
146
+ respective replies and can be used.
147
+
148
+ ```ruby
149
+ redis.pipelined do
150
+ @set = redis.set "foo", "bar"
151
+ @incr = redis.incr "baz"
152
+ end
153
+
154
+ @set.value
155
+ # => "OK"
156
+
157
+ @incr.value
158
+ # => 1
159
+ ```
160
+
161
+ ## Alternate drivers
162
+
163
+ By default, redis-rb uses Ruby's socket library to talk with Redis2.
164
+ To use an alternative connection driver it should be specified as option
165
+ when instantiating the client object. These instructions are only valid
166
+ for **redis-rb 3.0**. For instructions on how to use alternate drivers from
167
+ **redis-rb 2.2**, please refer to an [older README][readme-2.2.2].
168
+
169
+ [readme-2.2.2]: https://github.com/redis/redis-rb/blob/v2.2.2/README.md
170
+
171
+ ### hiredis
172
+
173
+ The hiredis driver uses the connection facility of hiredis-rb. In turn,
174
+ hiredis-rb is a binding to the official hiredis client library. It
175
+ optimizes for speed, at the cost of portability. Because it is a C
176
+ extension, JRuby is not supported (by default).
177
+
178
+ It is best to use hiredis when you have large replies (for example:
179
+ `LRANGE`, `SMEMBERS`, `ZRANGE`, etc.) and/or use big pipelines.
180
+
181
+ In your Gemfile, include hiredis:
182
+
183
+ ```ruby
184
+ gem "redis", "~> 3.0.1"
185
+ gem "hiredis", "~> 0.4.5"
186
+ ```
187
+
188
+ When instantiating the client object, specify hiredis:
189
+
190
+ ```ruby
191
+ redis = Redis2.new(:driver => :hiredis)
192
+ ```
193
+
194
+ ### synchrony
195
+
196
+ The synchrony driver adds support for [em-synchrony][em-synchrony].
197
+ This makes redis-rb work with EventMachine's asynchronous I/O, while not
198
+ changing the exposed API. The hiredis gem needs to be available as
199
+ well, because the synchrony driver uses hiredis for parsing the Redis2
200
+ protocol.
201
+
202
+ [em-synchrony]: https://github.com/igrigorik/em-synchrony
203
+
204
+ In your Gemfile, include em-synchrony and hiredis:
205
+
206
+ ```ruby
207
+ gem "redis", "~> 3.0.1"
208
+ gem "hiredis", "~> 0.4.5"
209
+ gem "em-synchrony"
210
+ ```
211
+
212
+ When instantiating the client object, specify synchrony:
213
+
214
+ ```ruby
215
+ redis = Redis2.new(:driver => :synchrony)
216
+ ```
217
+
218
+ ## Testing
219
+
220
+ This library is tested using [Travis][travis-home], where it is tested
221
+ against the following interpreters and drivers:
222
+
223
+ * MRI 1.8.7 (drivers: ruby, hiredis)
224
+ * MRI 1.9.2 (drivers: ruby, hiredis, synchrony)
225
+ * MRI 1.9.3 (drivers: ruby, hiredis, synchrony)
226
+ * MRI 2.0.0 (drivers: ruby, hiredis, synchrony)
227
+ * JRuby 1.7 (1.8 mode) (drivers: ruby)
228
+ * JRuby 1.7 (1.9 mode) (drivers: ruby)
229
+
230
+ ## Contributors
231
+
232
+ (ordered chronologically with more than 5 commits, see `git shortlog -sn` for
233
+ all contributors)
234
+
235
+ * Ezra Zygmuntowicz
236
+ * Taylor Weibley
237
+ * Matthew Clark
238
+ * Brian McKinney
239
+ * Luca Guidi
240
+ * Salvatore Sanfillipo
241
+ * Chris Wanstrath
242
+ * Damian Janowski
243
+ * Michel Martens
244
+ * Nick Quaranto
245
+ * Pieter Noordhuis
246
+ * Ilya Grigorik
247
+
248
+ ## Contributing
249
+
250
+ [Fork the project](https://github.com/redis/redis-rb) and send pull
251
+ requests. You can also ask for help at `#redis-rb` on Freenode.
data/Rakefile ADDED
@@ -0,0 +1,403 @@
1
+ require 'rubygems'
2
+ require 'rubygems/package_task'
3
+ require 'rake/testtask'
4
+
5
+ ENV["REDIS_BRANCH"] ||= "unstable"
6
+
7
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
8
+ require 'redis2/version'
9
+
10
+ REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__)
11
+ REDIS_CNF = File.join(REDIS_DIR, "test.conf")
12
+ REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
13
+ BINARY = "tmp/redis-#{ENV["REDIS_BRANCH"]}/src/redis-server"
14
+
15
+ task :default => :run
16
+
17
+ desc "Run tests and manage server start/stop"
18
+ task :run => [:start, :test, :stop]
19
+
20
+ desc "Start the Redis2 server"
21
+ task :start => BINARY do
22
+ sh "#{BINARY} --version"
23
+
24
+ redis_running = \
25
+ begin
26
+ File.exists?(REDIS_PID) && Process.kill(0, File.read(REDIS_PID).to_i)
27
+ rescue Errno::ESRCH
28
+ FileUtils.rm REDIS_PID
29
+ false
30
+ end
31
+
32
+ unless redis_running
33
+ unless system("#{BINARY} #{REDIS_CNF}")
34
+ abort "could not start redis-server"
35
+ end
36
+ end
37
+ end
38
+
39
+ desc "Stop the Redis2 server"
40
+ task :stop do
41
+ if File.exists?(REDIS_PID)
42
+ Process.kill "INT", File.read(REDIS_PID).to_i
43
+ FileUtils.rm REDIS_PID
44
+ end
45
+ end
46
+
47
+ file BINARY do
48
+ branch = ENV.fetch("REDIS_BRANCH")
49
+
50
+ sh <<-SH
51
+ mkdir -p tmp;
52
+ cd tmp;
53
+ wget https://github.com/antirez/redis/archive/#{branch}.tar.gz -O #{branch}.tar.gz;
54
+ tar xf #{branch}.tar.gz;
55
+ cd redis-#{branch};
56
+ make
57
+ SH
58
+ end
59
+
60
+ Rake::TestTask.new do |t|
61
+ t.options = "-v"
62
+ t.test_files = FileList["test/*_test.rb"]
63
+ end
64
+
65
+ task :doc => ["doc:generate", "doc:prepare"]
66
+
67
+ namespace :doc do
68
+ task :generate do
69
+ require "shellwords"
70
+
71
+ `rm -rf doc`
72
+
73
+ current_branch = `git branch`[/^\* (.*)$/, 1]
74
+
75
+ begin
76
+ tags = `git tag -l`.split("\n").sort.reverse
77
+
78
+ tags.each do |tag|
79
+ `git checkout -q #{tag} 2>/dev/null`
80
+
81
+ unless $?.success?
82
+ $stderr.puts "Need a clean working copy. Please git-stash away."
83
+ exit 1
84
+ end
85
+
86
+ puts tag
87
+
88
+ `mkdir -p doc/#{tag}`
89
+
90
+ files = `git ls-tree -r HEAD lib`.split("\n").map do |line|
91
+ line[/\t(.*)$/, 1]
92
+ end
93
+
94
+ opts = [
95
+ "--title", "A Ruby client for Redis2",
96
+ "--output", "doc/#{tag}",
97
+ "--no-cache",
98
+ "--no-save",
99
+ "-q",
100
+ *files
101
+ ]
102
+
103
+ `yardoc #{Shellwords.shelljoin opts}`
104
+ end
105
+ ensure
106
+ `git checkout -q #{current_branch}`
107
+ end
108
+ end
109
+
110
+ task :prepare do
111
+ versions = `git tag -l`.split("\n").grep(/^v/).sort
112
+ latest_version = versions.last
113
+
114
+ File.open("doc/.htaccess", "w") do |file|
115
+ file.puts "RedirectMatch 302 ^/?$ /#{latest_version}"
116
+ end
117
+
118
+ File.open("doc/robots.txt", "w") do |file|
119
+ file.puts "User-Agent: *"
120
+
121
+ (versions - [latest_version]).each do |version|
122
+ file.puts "Disallow: /#{version}"
123
+ end
124
+ end
125
+
126
+ google_analytics = <<-EOS
127
+ <script type="text/javascript">
128
+
129
+ var _gaq = _gaq || [];
130
+ _gaq.push(['_setAccount', 'UA-11356145-2']);
131
+ _gaq.push(['_trackPageview']);
132
+
133
+ (function() {
134
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
135
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
136
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
137
+ })();
138
+
139
+ </script>
140
+ EOS
141
+
142
+ Dir["doc/**/*.html"].each do |path|
143
+ lines = IO.readlines(path)
144
+
145
+ File.open(path, "w") do |file|
146
+ lines.each do |line|
147
+ if line.include?("</head>")
148
+ file.write(google_analytics)
149
+ end
150
+
151
+ file.write(line)
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ task :deploy do
158
+ system "rsync --del -avz doc/ redis-rb.keyvalue.org:deploys/redis-rb.keyvalue.org/"
159
+ end
160
+ end
161
+
162
+ class Source
163
+
164
+ MATCHER = "(?:\\s{%d}#[^\\n]*\\n)*^\\s{%d}def ([a-z_?]+)(?:\(.*?\))?\\n.*?^\\s{%d}end\\n\\n"
165
+
166
+ def initialize(data, options = {})
167
+ @doc = parse(File.read(data), options)
168
+ end
169
+
170
+ def methods
171
+ @doc.select do |d|
172
+ d.is_a?(Method)
173
+ end.map do |d|
174
+ d.name
175
+ end
176
+ end
177
+
178
+ def move(a, b)
179
+ ao = @doc.find { |m| m.is_a?(Method) && m.name == a }
180
+ bo = @doc.find { |m| m.is_a?(Method) && m.name == b }
181
+ ai = @doc.index(ao)
182
+ bi = @doc.index(bo)
183
+
184
+ @doc.delete_at(ai)
185
+ @doc.insert(bi, ao)
186
+
187
+ nil
188
+ end
189
+
190
+ def to_s
191
+ @doc.join
192
+ end
193
+
194
+ protected
195
+
196
+ def parse(data, options = {})
197
+ re = Regexp.new(MATCHER % ([options[:indent]] * 3), Regexp::MULTILINE)
198
+ tail = data.dup
199
+ doc = []
200
+
201
+ while match = re.match(tail)
202
+ doc << match.pre_match
203
+ doc << Method.new(match)
204
+ tail = match.post_match
205
+ end
206
+
207
+ doc << tail if tail
208
+ doc
209
+ end
210
+
211
+ class Method
212
+
213
+ def initialize(match)
214
+ @match = match
215
+ end
216
+
217
+ def name
218
+ @match[1]
219
+ end
220
+
221
+ def to_s
222
+ @match[0]
223
+ end
224
+ end
225
+ end
226
+
227
+ namespace :commands do
228
+ def redis_commands
229
+ $redis_commands ||= doc.keys.map do |key|
230
+ key.split(" ").first.downcase
231
+ end.uniq
232
+ end
233
+
234
+ def doc
235
+ $doc ||= begin
236
+ require "open-uri"
237
+ require "json"
238
+
239
+ JSON.parse(open("https://github.com/antirez/redis-doc/raw/master/commands.json").read)
240
+ end
241
+ end
242
+
243
+ task :order do
244
+ require "json"
245
+
246
+ reference = if File.exist?(".order")
247
+ JSON.parse(File.read(".order"))
248
+ else
249
+ {}
250
+ end
251
+
252
+ buckets = {}
253
+ doc.each do |k, v|
254
+ buckets[v["group"]] ||= []
255
+ buckets[v["group"]] << k.split.first.downcase
256
+ buckets[v["group"]].uniq!
257
+ end
258
+
259
+ result = (reference.keys + (buckets.keys - reference.keys)).map do |g|
260
+ [g, reference[g] + (buckets[g] - reference[g])]
261
+ end
262
+
263
+ File.open(".order", "w") do |f|
264
+ f.write(JSON.pretty_generate(Hash[result]))
265
+ end
266
+ end
267
+
268
+ def reorder(file, options = {})
269
+ require "json"
270
+ require "set"
271
+
272
+ STDERR.puts "reordering #{file}..."
273
+
274
+ reference = if File.exist?(".order")
275
+ JSON.parse(File.read(".order"))
276
+ else
277
+ {}
278
+ end
279
+
280
+ dst = Source.new(file, options)
281
+
282
+ src_methods = reference.map { |k, v| v }.flatten
283
+ dst_methods = dst.methods
284
+
285
+ src_set = Set.new(src_methods)
286
+ dst_set = Set.new(dst_methods)
287
+
288
+ intersection = src_set & dst_set
289
+ intersection.delete("initialize")
290
+
291
+ loop do
292
+ src_methods = reference.map { |k, v| v }.flatten
293
+ dst_methods = dst.methods
294
+
295
+ src_methods = src_methods.select do |m|
296
+ intersection.include?(m)
297
+ end
298
+
299
+ dst_methods = dst_methods.select do |m|
300
+ intersection.include?(m)
301
+ end
302
+
303
+ if src_methods == dst_methods
304
+ break
305
+ end
306
+
307
+ rv = yield(src_methods, dst_methods, dst)
308
+ break if rv == false
309
+ end
310
+
311
+ File.open(file, "w") do |f|
312
+ f.write(dst.to_s)
313
+ end
314
+ end
315
+
316
+ task :reorder do
317
+ blk = lambda do |src_methods, dst_methods, dst|
318
+ src_methods.zip(dst_methods).each do |a, b|
319
+ if a != b
320
+ dst.move(a, b)
321
+ break
322
+ end
323
+ end
324
+ end
325
+
326
+ reorder "lib/redis.rb", :indent => 2, &blk
327
+ reorder "lib/redis/distributed.rb", :indent => 4, &blk
328
+ end
329
+
330
+ def missing(file, options = {})
331
+ src = Source.new(file, options)
332
+
333
+ defined_methods = src.methods.map(&:downcase)
334
+ required_methods = redis_commands.map(&:downcase)
335
+
336
+ STDOUT.puts "missing in #{file}:"
337
+ STDOUT.puts (required_methods - defined_methods).inspect
338
+ end
339
+
340
+ task :missing do
341
+ missing "lib/redis.rb", :indent => 2
342
+ missing "lib/redis/distributed.rb", :indent => 4
343
+ end
344
+
345
+ def document(file)
346
+ source = File.read(file)
347
+
348
+ doc.each do |name, command|
349
+ source.sub!(/(?:^ *# .*\n)*(^ *#\n(^ *# .+?\n)*)*^( *)def #{name.downcase}(\(|$)/) do
350
+ extra_comments, indent, extra_args = $1, $3, $4
351
+ comment = "#{indent}# #{command["summary"].strip}."
352
+
353
+ IO.popen("par p#{2 + indent.size} 80", "r+") do |io|
354
+ io.puts comment
355
+ io.close_write
356
+ comment = io.read
357
+ end
358
+
359
+ "#{comment}#{extra_comments}#{indent}def #{name.downcase}#{extra_args}"
360
+ end
361
+ end
362
+
363
+ File.open(file, "w") { |f| f.write(source) }
364
+ end
365
+
366
+ task :doc do
367
+ document "lib/redis.rb"
368
+ document "lib/redis/distributed.rb"
369
+ end
370
+
371
+ task :verify do
372
+ require "redis"
373
+ require "stringio"
374
+
375
+ require "./test/helper"
376
+
377
+ OPTIONS[:logger] = Logger.new("./tmp/log")
378
+
379
+ Rake::Task["test:ruby"].invoke
380
+
381
+ redis = Redis2.new
382
+
383
+ report = ["Command", "\033[0mDefined?\033[0m", "\033[0mTested?\033[0m"]
384
+
385
+ yes, no = "\033[1;32mYes\033[0m", "\033[1;31mNo\033[0m"
386
+
387
+ log = File.read("./tmp/log")
388
+
389
+ redis_commands.sort.each do |name, _|
390
+ defined, tested = redis.respond_to?(name), log[">> #{name.upcase}"]
391
+
392
+ next if defined && tested
393
+
394
+ report << name
395
+ report << (defined ? yes : no)
396
+ report << (tested ? yes : no)
397
+ end
398
+
399
+ IO.popen("rs 0 3", "w") do |io|
400
+ io.puts report.join("\n")
401
+ end
402
+ end
403
+ end
@@ -0,0 +1,71 @@
1
+ # Run with
2
+ #
3
+ # $ ruby -Ilib benchmarking/logging.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
+ require "logger"
15
+
16
+ def log(level, namespace = nil)
17
+ logger = (namespace || Kernel).const_get(:Logger).new("/dev/null")
18
+ logger.level = (namespace || Logger).const_get(level)
19
+ logger
20
+ end
21
+
22
+ def stress(redis)
23
+ redis.flushdb
24
+
25
+ n = (ARGV.shift || 2000).to_i
26
+
27
+ n.times do |i|
28
+ key = "foo:#{i}"
29
+ redis.set key, i
30
+ redis.get key
31
+ end
32
+ end
33
+
34
+ default = Redis2.new
35
+
36
+ logging_redises = [
37
+ Redis2.new(:logger => log(:DEBUG)),
38
+ Redis2.new(:logger => log(:INFO)),
39
+ ]
40
+
41
+ begin
42
+ require "log4r"
43
+
44
+ logging_redises += [
45
+ Redis2.new(:logger => log(:DEBUG, Log4r)),
46
+ Redis2.new(:logger => log(:INFO, Log4r)),
47
+ ]
48
+ rescue LoadError
49
+ $stderr.puts "Log4r not installed. `gem install log4r` if you want to compare it against Ruby's Logger (spoiler: it's much faster)."
50
+ end
51
+
52
+ benchmark "Default options (no logger)" do
53
+ stress(default)
54
+ end
55
+
56
+ logging_redises.each do |redis|
57
+ logger = redis.client.logger
58
+
59
+ case logger
60
+ when Logger
61
+ level = Logger::SEV_LABEL[logger.level]
62
+ when Log4r::Logger
63
+ level = logger.levels[logger.level]
64
+ end
65
+
66
+ benchmark "#{logger.class} on #{level}" do
67
+ stress(redis)
68
+ end
69
+ end
70
+
71
+ run 10