redis2-namespaced 3.0.7

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 (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