redis 2.0.0 → 2.2.1

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 (81) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +208 -0
  4. data/Rakefile +183 -73
  5. data/TODO.md +4 -0
  6. data/benchmarking/logging.rb +62 -0
  7. data/benchmarking/pipeline.rb +51 -0
  8. data/benchmarking/speed.rb +21 -0
  9. data/benchmarking/suite.rb +24 -0
  10. data/benchmarking/thread_safety.rb +38 -0
  11. data/benchmarking/worker.rb +71 -0
  12. data/examples/basic.rb +15 -0
  13. data/examples/dist_redis.rb +43 -0
  14. data/examples/incr-decr.rb +17 -0
  15. data/examples/list.rb +26 -0
  16. data/examples/pubsub.rb +31 -0
  17. data/examples/sets.rb +36 -0
  18. data/examples/unicorn/config.ru +3 -0
  19. data/examples/unicorn/unicorn.rb +20 -0
  20. data/lib/redis/client.rb +149 -165
  21. data/lib/redis/connection/command_helper.rb +45 -0
  22. data/lib/redis/connection/hiredis.rb +49 -0
  23. data/lib/redis/connection/registry.rb +12 -0
  24. data/lib/redis/connection/ruby.rb +135 -0
  25. data/lib/redis/connection/synchrony.rb +129 -0
  26. data/lib/redis/connection.rb +9 -0
  27. data/lib/redis/distributed.rb +182 -7
  28. data/lib/redis/pipeline.rb +22 -1
  29. data/lib/redis/subscribe.rb +19 -4
  30. data/lib/redis/version.rb +3 -0
  31. data/lib/redis.rb +715 -155
  32. data/redis.gemspec +24 -0
  33. data/test/commands_on_hashes_test.rb +32 -0
  34. data/test/commands_on_lists_test.rb +60 -0
  35. data/test/commands_on_sets_test.rb +78 -0
  36. data/test/commands_on_sorted_sets_test.rb +109 -0
  37. data/test/commands_on_strings_test.rb +80 -0
  38. data/test/commands_on_value_types_test.rb +88 -0
  39. data/test/connection_handling_test.rb +88 -0
  40. data/test/db/.gitignore +1 -0
  41. data/test/distributed_blocking_commands_test.rb +53 -0
  42. data/test/distributed_commands_on_hashes_test.rb +12 -0
  43. data/test/distributed_commands_on_lists_test.rb +24 -0
  44. data/test/distributed_commands_on_sets_test.rb +85 -0
  45. data/test/distributed_commands_on_strings_test.rb +50 -0
  46. data/test/distributed_commands_on_value_types_test.rb +73 -0
  47. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  48. data/test/distributed_connection_handling_test.rb +25 -0
  49. data/test/distributed_internals_test.rb +27 -0
  50. data/test/distributed_key_tags_test.rb +53 -0
  51. data/test/distributed_persistence_control_commands_test.rb +24 -0
  52. data/test/distributed_publish_subscribe_test.rb +101 -0
  53. data/test/distributed_remote_server_control_commands_test.rb +43 -0
  54. data/test/distributed_sorting_test.rb +21 -0
  55. data/test/distributed_test.rb +59 -0
  56. data/test/distributed_transactions_test.rb +34 -0
  57. data/test/encoding_test.rb +16 -0
  58. data/test/error_replies_test.rb +53 -0
  59. data/test/helper.rb +145 -0
  60. data/test/internals_test.rb +160 -0
  61. data/test/lint/hashes.rb +114 -0
  62. data/test/lint/internals.rb +37 -0
  63. data/test/lint/lists.rb +93 -0
  64. data/test/lint/sets.rb +66 -0
  65. data/test/lint/sorted_sets.rb +167 -0
  66. data/test/lint/strings.rb +137 -0
  67. data/test/lint/value_types.rb +84 -0
  68. data/test/persistence_control_commands_test.rb +22 -0
  69. data/test/pipelining_commands_test.rb +123 -0
  70. data/test/publish_subscribe_test.rb +158 -0
  71. data/test/redis_mock.rb +80 -0
  72. data/test/remote_server_control_commands_test.rb +82 -0
  73. data/test/sorting_test.rb +44 -0
  74. data/test/synchrony_driver.rb +57 -0
  75. data/test/test.conf +8 -0
  76. data/test/thread_safety_test.rb +30 -0
  77. data/test/transactions_test.rb +100 -0
  78. data/test/unknown_commands_test.rb +14 -0
  79. data/test/url_param_test.rb +60 -0
  80. metadata +130 -22
  81. data/README.markdown +0 -120
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ nohup.out
2
+ redis/*
3
+ rdsrv
4
+ pkg/*
5
+ coverage/*
6
+ .idea
7
+ *.rdb
8
+ *.swp
data/CHANGELOG.md ADDED
@@ -0,0 +1,49 @@
1
+ # 2.2.2 (unreleased)
2
+
3
+ # 2.2.1
4
+
5
+ * Internal API: Client#call and family are now called with a single array
6
+ argument, since splatting a large number of arguments (100K+) results in a
7
+ stack overflow on 1.9.2.
8
+
9
+ * The `INFO` command can optionally take a subcommand. When the subcommand is
10
+ `COMMANDSTATS`, the client will properly format the returned statistics per
11
+ command. Subcommands for `INFO` are available since Redis v2.3.0 (unstable).
12
+
13
+ * Change `IO#syswrite` back to the buffered `IO#write` since some Rubies do
14
+ short writes for large (1MB+) buffers and some don't (see issue #108).
15
+
16
+ # 2.2.0
17
+
18
+ * Added method `Redis#without_reconnect` that ensures the client will not try
19
+ to reconnect when running the code inside the specified block.
20
+
21
+ * Thread-safe by default. Thread safety can be explicitly disabled by passing
22
+ `:thread_safe => false` as argument.
23
+
24
+ * Commands called inside a MULTI/EXEC no longer raise error replies, since a
25
+ successful EXEC means the commands inside the block were executed.
26
+
27
+ * MULTI/EXEC blocks are pipelined.
28
+
29
+ * Don't disconnect on error replies.
30
+
31
+ * Use `IO#syswrite` instead of `IO#write` because write buffering is not
32
+ necessary.
33
+
34
+ * Connect to a unix socket by passing the `:path` option as argument.
35
+
36
+ * The timeout value is coerced into a float, allowing sub-second timeouts.
37
+
38
+ * Accept both `:with_scores` _and_ `:withscores` as argument to sorted set
39
+ commands.
40
+
41
+ * Use [hiredis](https://github.com/pietern/hiredis-rb) (v0.3 or higher) by
42
+ requiring "redis/connection/hiredis".
43
+
44
+ * Use [em-synchrony](https://github.com/igrigorik/em-synchrony) by requiring
45
+ "redis/connection/synchrony".
46
+
47
+ # 2.1.1
48
+
49
+ See commit log.
data/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # redis-rb
2
+
3
+ A Ruby client library for the [Redis](http://redis.io) key-value store.
4
+
5
+ ## A note about versions
6
+
7
+ Versions *1.0.x* target all versions of Redis. You have to use this one if you are using Redis < 1.2.
8
+
9
+ Version *2.0* is a big refactoring of the previous version and makes little effort to be
10
+ backwards-compatible when it shouldn't. It does not support Redis' original protocol, favoring the
11
+ new, binary-safe one. You should be using this version if you're running Redis 1.2+.
12
+
13
+ ## Information about Redis
14
+
15
+ Redis is a key-value store with some interesting features:
16
+
17
+ 1. It's fast.
18
+ 2. Keys are strings but values are typed. Currently Redis supports strings, lists, sets, sorted sets and hashes. [Atomic operations](http://redis.io/commands) can be done on all of these types.
19
+
20
+ See [the Redis homepage](http://redis.io) for more information.
21
+
22
+ ## Getting started
23
+
24
+ You can connect to Redis by instantiating the `Redis` class:
25
+
26
+ require "redis"
27
+
28
+ redis = Redis.new
29
+
30
+ This assumes Redis was started with default values listening on `localhost`, port 6379. If you need to connect to a remote server or a different port, try:
31
+
32
+ redis = Redis.new(:host => "10.0.1.1", :port => 6380)
33
+
34
+ To connect to Redis listening on a unix socket, try:
35
+
36
+ redis = Redis.new(:path => "/tmp/redis.sock")
37
+
38
+ Once connected, you can start running commands against Redis:
39
+
40
+ >> redis.set "foo", "bar"
41
+ => "OK"
42
+
43
+ >> redis.get "foo"
44
+ => "bar"
45
+
46
+ >> redis.sadd "users", "albert"
47
+ => true
48
+
49
+ >> redis.sadd "users", "bernard"
50
+ => true
51
+
52
+ >> redis.sadd "users", "charles"
53
+ => true
54
+
55
+ How many users?
56
+
57
+ >> redis.scard "users"
58
+ => 3
59
+
60
+ Is `albert` a user?
61
+
62
+ >> redis.sismember "users", "albert"
63
+ => true
64
+
65
+ Is `isabel` a user?
66
+
67
+ >> redis.sismember "users", "isabel"
68
+ => false
69
+
70
+ Handle groups:
71
+
72
+ >> redis.sadd "admins", "albert"
73
+ => true
74
+
75
+ >> redis.sadd "admins", "isabel"
76
+ => true
77
+
78
+ Users who are also admins:
79
+
80
+ >> redis.sinter "users", "admins"
81
+ => ["albert"]
82
+
83
+ Users who are not admins:
84
+
85
+ >> redis.sdiff "users", "admins"
86
+ => ["bernard", "charles"]
87
+
88
+ Admins who are not users:
89
+
90
+ >> redis.sdiff "admins", "users"
91
+ => ["isabel"]
92
+
93
+ All users and admins:
94
+
95
+ >> redis.sunion "admins", "users"
96
+ => ["albert", "bernard", "charles", "isabel"]
97
+
98
+
99
+ ## Storing objects
100
+
101
+ Redis only stores strings as values. If you want to store an object inside a key, you can use a serialization/deseralization mechanism like JSON:
102
+
103
+ >> redis.set "foo", [1, 2, 3].to_json
104
+ => OK
105
+
106
+ >> JSON.parse(redis.get("foo"))
107
+ => [1, 2, 3]
108
+
109
+ ## Executing multiple commands atomically
110
+
111
+ You can use `MULTI/EXEC` to run arbitrary commands in an atomic fashion:
112
+
113
+ redis.multi do
114
+ redis.set "foo", "bar"
115
+ redis.incr "baz"
116
+ end
117
+
118
+ ## Multithreaded Operation
119
+
120
+ Starting with redis-rb 2.2.0, the client is thread-safe by default. To use
121
+ earlier versions safely in a multithreaded environment, be sure to initialize
122
+ the client with `:thread_safe => true`. Thread-safety can be explicitly
123
+ disabled for versions 2.2 and up by initializing the client with `:thread_safe
124
+ => false`.
125
+
126
+ See the tests and benchmarks for examples.
127
+
128
+ ## Alternate drivers
129
+
130
+ Non-default connection drivers are only used when they are explicitly required.
131
+ By default, redis-rb uses Ruby's socket library to talk with Redis.
132
+
133
+ ### hiredis
134
+
135
+ Using redis-rb with hiredis-rb (v0.3 or higher) as backend is done by requiring
136
+ `redis/connection/hiredis` before requiring `redis`. This will make redis-rb
137
+ pick up hiredis as default driver automatically. This driver optimizes for
138
+ speed, at the cost of portability. Since hiredis is a C extension, JRuby is not
139
+ supported (by default). Use hiredis when you have large array replies (think
140
+ `LRANGE`, `SMEMBERS`, `ZRANGE`, etc.) and/or large pipelines of commands.
141
+
142
+ Using redis-rb with hiredis from a Gemfile:
143
+
144
+ gem "hiredis", "~> 0.3.1"
145
+ gem "redis", "~> 2.2.0", :require => ["redis/connection/hiredis", "redis"]
146
+
147
+ ### synchrony
148
+
149
+ This driver adds support for
150
+ [em-synchrony](https://github.com/igrigorik/em-synchrony). Using the synchrony
151
+ backend from redis-rb is done by requiring `redis/connection/synchrony` before
152
+ requiring `redis`. This driver makes redis-rb work with EventMachine's
153
+ asynchronous I/O, while not changing the exposed API. The hiredis gem needs to
154
+ be available as well, because the synchrony driver uses hiredis for parsing the
155
+ Redis protocol.
156
+
157
+ Using redis-rb with synchrony from a Gemfile:
158
+
159
+ gem "hiredis", "~> 0.3.1"
160
+ gem "em-synchrony"
161
+ gem "redis", "~> 2.2.0", :require => ["redis/connection/synchrony", "redis"]
162
+
163
+ ## Testing
164
+
165
+ This library (v2.2) is tested against the following interpreters:
166
+
167
+ * MRI 1.8.7 (drivers: Ruby, hiredis)
168
+ * MRI 1.9.2 (drivers: Ruby, hiredis, em-synchrony)
169
+ * JRuby 1.6 (drivers: Ruby)
170
+ * Rubinius 1.2 (drivers: Ruby, hiredis)
171
+
172
+ ## Known issues
173
+
174
+ * Ruby 1.9 doesn't raise on socket timeouts in `IO#read` but rather retries the
175
+ read operation. This means socket timeouts don't work on 1.9 when using the
176
+ pure Ruby I/O code. Use hiredis when you want use socket timeouts on 1.9.
177
+
178
+ * Ruby 1.8 *does* raise on socket timeouts in `IO#read`, but prints a warning
179
+ that using `IO#read` for non blocking reads is obsolete. This is wrong, since
180
+ the read is in fact blocking, but `EAGAIN` (which is returned on socket
181
+ timeouts) is interpreted as if the read was non blocking. Use hiredis to
182
+ prevent seeing this warning.
183
+
184
+ ## More info
185
+
186
+ Check the [Redis Command Reference](http://redis.io/commands) or check the tests to find out how to use this client.
187
+
188
+ ## Contributors
189
+
190
+ (ordered chronologically with more than 5 commits, see `git shortlog -sn` for
191
+ all contributors)
192
+
193
+ * Ezra Zygmuntowicz
194
+ * Taylor Weibley
195
+ * Matthew Clark
196
+ * Brian McKinney
197
+ * Luca Guidi
198
+ * Salvatore Sanfillipo
199
+ * Chris Wanstrath
200
+ * Damian Janowski
201
+ * Michel Martens
202
+ * Nick Quaranto
203
+ * Pieter Noordhuis
204
+ * Ilya Grigorik
205
+
206
+ ## Contributing
207
+
208
+ [Fork the project](http://github.com/ezmobius/redis-rb) and send pull requests. You can also ask for help at `#redis-rb` on Freenode.
data/Rakefile CHANGED
@@ -3,31 +3,7 @@ require 'rake/gempackagetask'
3
3
  require 'rake/testtask'
4
4
 
5
5
  $:.unshift File.join(File.dirname(__FILE__), 'lib')
6
- require 'redis'
7
-
8
- GEM = 'redis'
9
- GEM_NAME = 'redis'
10
- GEM_VERSION = Redis::VERSION
11
- AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley', 'Matthew Clark', 'Brian McKinney', 'Salvatore Sanfilippo', 'Luca Guidi', 'Michel Martens', 'Damian Janowski']
12
- EMAIL = "ez@engineyard.com"
13
- HOMEPAGE = "http://github.com/ezmobius/redis-rb"
14
- SUMMARY = "Ruby client library for Redis, the key value storage server"
15
-
16
- spec = Gem::Specification.new do |s|
17
- s.name = GEM
18
- s.version = GEM_VERSION
19
- s.platform = Gem::Platform::RUBY
20
- s.has_rdoc = true
21
- s.extra_rdoc_files = ["LICENSE"]
22
- s.summary = SUMMARY
23
- s.description = s.summary
24
- s.authors = AUTHORS
25
- s.email = EMAIL
26
- s.homepage = HOMEPAGE
27
- s.require_path = 'lib'
28
- s.autorequire = GEM
29
- s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,tasks,spec}/**/*")
30
- end
6
+ require 'redis/version'
31
7
 
32
8
  REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__)
33
9
  REDIS_CNF = File.join(REDIS_DIR, "test.conf")
@@ -40,76 +16,204 @@ task :run => [:start, :test, :stop]
40
16
 
41
17
  desc "Start the Redis server"
42
18
  task :start do
43
- unless File.exists?(REDIS_PID)
44
- system "redis-server #{REDIS_CNF}"
19
+ redis_running = \
20
+ begin
21
+ File.exists?(REDIS_PID) && Process.kill(0, File.read(REDIS_PID).to_i)
22
+ rescue Errno::ESRCH
23
+ FileUtils.rm REDIS_PID
24
+ false
45
25
  end
26
+
27
+ system "redis-server #{REDIS_CNF}" unless redis_running
46
28
  end
47
29
 
48
30
  desc "Stop the Redis server"
49
31
  task :stop do
50
32
  if File.exists?(REDIS_PID)
51
- system "kill #{File.read(REDIS_PID)}"
52
- system "rm #{REDIS_PID}"
33
+ Process.kill "INT", File.read(REDIS_PID).to_i
34
+ FileUtils.rm REDIS_PID
53
35
  end
54
36
  end
55
37
 
56
- Rake::TestTask.new(:test) do |t|
57
- t.pattern = 'test/**/*_test.rb'
38
+ def isolated(&block)
39
+ pid = fork { yield }
40
+ Process.wait(pid)
58
41
  end
59
42
 
60
- Rake::GemPackageTask.new(spec) do |pkg|
61
- pkg.gem_spec = spec
62
- end
43
+ desc "Run the test suite"
44
+ task :test => ["test:ruby", "test:hiredis", "test:synchrony"]
63
45
 
64
- desc "install the gem locally"
65
- task :install => [:package] do
66
- sh %{gem install pkg/#{GEM}-#{GEM_VERSION}}
67
- end
46
+ namespace :test do
47
+ desc "Run tests against the Ruby driver"
48
+ task :ruby do
49
+ require "cutest"
68
50
 
69
- desc "create a gemspec file"
70
- task :gemspec do
71
- File.open("#{GEM}.gemspec", "w") do |file|
72
- file.puts spec.to_ruby
51
+ isolated do
52
+ Cutest.run(Dir["./test/**/*_test.rb"])
53
+ end
73
54
  end
74
- end
75
55
 
76
- desc "Generate YARDoc"
77
- task :yardoc do
78
- require "yard"
56
+ desc "Run tests against the hiredis driver"
57
+ task :hiredis do
58
+ require "cutest"
59
+
60
+ isolated do
61
+ begin
62
+ require "redis/connection/hiredis"
63
+
64
+ puts
65
+ puts "Running tests against hiredis v#{Hiredis::VERSION}"
79
66
 
80
- opts = ["--title", "A Ruby client for Redis"]
67
+ Cutest.run(Dir["./test/**/*_test.rb"])
68
+ rescue LoadError
69
+ puts "Skipping tests against hiredis"
70
+ end
71
+ end
72
+ end
73
+
74
+ desc "Run tests against the em-synchrony driver"
75
+ task :synchrony do
76
+ require "cutest"
81
77
 
82
- YARD::CLI::Yardoc.run(*opts)
78
+ # Synchrony needs 1.9
79
+ next if RUBY_VERSION < "1.9"
80
+
81
+ isolated do
82
+ begin
83
+ require "redis/connection/synchrony"
84
+
85
+ puts
86
+ puts "Running tests against em-synchrony"
87
+
88
+ threaded_tests = ['./test/thread_safety_test.rb']
89
+ Cutest.run(Dir['./test/**/*_test.rb'] - threaded_tests)
90
+ rescue LoadError
91
+ puts "Skipping tests against em-synchrony"
92
+ end
93
+ end
94
+ end
83
95
  end
84
96
 
85
- namespace :commands do
86
- def redis_commands
87
- $redis_commands ||= begin
88
- require "nokogiri"
97
+ task :doc => ["doc:generate", "doc:prepare"]
98
+
99
+ namespace :doc do
100
+ task :generate do
101
+ require "shellwords"
102
+
103
+ `rm -rf doc`
104
+
105
+ current_branch = `git branch`[/^\* (.*)$/, 1]
106
+
107
+ begin
108
+ tags = `git tag -l`.split("\n").sort.reverse
89
109
 
90
- doc = Nokogiri::HTML(open("http://code.google.com/p/redis/wiki/CommandReference"))
110
+ tags.each do |tag|
111
+ `git checkout -q #{tag} 2>/dev/null`
91
112
 
92
- commands = {}
113
+ unless $?.success?
114
+ $stderr.puts "Need a clean working copy. Please git-stash away."
115
+ exit 1
116
+ end
117
+
118
+ puts tag
119
+
120
+ `mkdir -p doc/#{tag}`
121
+
122
+ files = `git ls-tree -r HEAD lib`.split("\n").map do |line|
123
+ line[/\t(.*)$/, 1]
124
+ end
125
+
126
+ opts = [
127
+ "--title", "A Ruby client for Redis",
128
+ "--output", "doc/#{tag}",
129
+ "--no-cache",
130
+ "--no-save",
131
+ "-q",
132
+ *files
133
+ ]
93
134
 
94
- doc.xpath("//ul/li").each do |node|
95
- node.at_xpath("./a").text.split("/").each do |name|
96
- if name =~ /^[A-Z]+$/
97
- commands[name.downcase] = node.at_xpath("./tt").text
135
+ `yardoc #{Shellwords.shelljoin opts}`
136
+ end
137
+ ensure
138
+ `git checkout -q #{current_branch}`
139
+ end
140
+ end
141
+
142
+ task :prepare do
143
+ versions = `git tag -l`.split("\n").grep(/^v/).sort
144
+ latest_version = versions.last
145
+
146
+ File.open("doc/.htaccess", "w") do |file|
147
+ file.puts "RedirectMatch 302 ^/?$ /#{latest_version}"
148
+ end
149
+
150
+ File.open("doc/robots.txt", "w") do |file|
151
+ file.puts "User-Agent: *"
152
+
153
+ (versions - [latest_version]).each do |version|
154
+ file.puts "Disallow: /#{version}"
155
+ end
156
+ end
157
+
158
+ google_analytics = <<-EOS
159
+ <script type="text/javascript">
160
+
161
+ var _gaq = _gaq || [];
162
+ _gaq.push(['_setAccount', 'UA-11356145-2']);
163
+ _gaq.push(['_trackPageview']);
164
+
165
+ (function() {
166
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
167
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
168
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
169
+ })();
170
+
171
+ </script>
172
+ EOS
173
+
174
+ Dir["doc/**/*.html"].each do |path|
175
+ lines = IO.readlines(path)
176
+
177
+ File.open(path, "w") do |file|
178
+ lines.each do |line|
179
+ if line.include?("</head>")
180
+ file.write(google_analytics)
98
181
  end
182
+
183
+ file.write(line)
99
184
  end
100
185
  end
186
+ end
187
+ end
188
+
189
+ task :deploy do
190
+ system "rsync --del -avz doc/ redis-rb.keyvalue.org:deploys/redis-rb.keyvalue.org/"
191
+ end
192
+ end
193
+
194
+ namespace :commands do
195
+ def redis_commands
196
+ $redis_commands ||= doc.keys.map do |key|
197
+ key.split(" ").first.downcase
198
+ end.uniq
199
+ end
101
200
 
102
- commands
201
+ def doc
202
+ $doc ||= begin
203
+ require "open-uri"
204
+ require "json"
205
+
206
+ JSON.parse(open("https://github.com/antirez/redis-doc/raw/master/commands.json").read)
103
207
  end
104
208
  end
105
209
 
106
- task :doc do
107
- source = File.read("lib/redis.rb")
210
+ def document(file)
211
+ source = File.read(file)
108
212
 
109
- redis_commands.each do |name, text|
110
- source.sub!(/(?:^ *#.*\n)*^( *)def #{name}(\(|$)/) do
111
- indent, extra_args = $1, $2
112
- comment = "#{indent}# #{text.strip}"
213
+ doc.each do |name, command|
214
+ source.sub!(/(?:^ *# .*\n)*(^ *#\n(^ *# .+?\n)*)*^( *)def #{name.downcase}(\(|$)/) do
215
+ extra_comments, indent, extra_args = $1, $3, $4
216
+ comment = "#{indent}# #{command["summary"].strip}."
113
217
 
114
218
  IO.popen("par p#{2 + indent.size} 80", "r+") do |io|
115
219
  io.puts comment
@@ -117,32 +221,38 @@ namespace :commands do
117
221
  comment = io.read
118
222
  end
119
223
 
120
- "#{comment}#{indent}def #{name}#{extra_args}"
224
+ "#{comment}#{extra_comments}#{indent}def #{name.downcase}#{extra_args}"
121
225
  end
122
226
  end
123
227
 
124
- File.open("lib/redis.rb", "w") { |f| f.write(source) }
228
+ File.open(file, "w") { |f| f.write(source) }
229
+ end
230
+
231
+ task :doc do
232
+ document "lib/redis.rb"
233
+ document "lib/redis/distributed.rb"
125
234
  end
126
235
 
127
236
  task :verify do
128
237
  require "redis"
238
+ require "stringio"
129
239
 
130
- Dir["test/**/*_test.rb"].each { |f| require f }
240
+ require "./test/helper"
131
241
 
132
- log = StringIO.new
242
+ OPTIONS[:logger] = Logger.new("./tmp/log")
133
243
 
134
- RedisTest::OPTIONS[:logger] = Logger.new(log)
244
+ Rake::Task["test:ruby"].invoke
135
245
 
136
246
  redis = Redis.new
137
247
 
138
- Test::Unit::AutoRunner.run
139
-
140
248
  report = ["Command", "\033[0mDefined?\033[0m", "\033[0mTested?\033[0m"]
141
249
 
142
250
  yes, no = "\033[1;32mYes\033[0m", "\033[1;31mNo\033[0m"
143
251
 
252
+ log = File.read("./tmp/log")
253
+
144
254
  redis_commands.sort.each do |name, _|
145
- defined, tested = redis.respond_to?(name), log.string[">> #{name.upcase}"]
255
+ defined, tested = redis.respond_to?(name), log[">> #{name.upcase}"]
146
256
 
147
257
  next if defined && tested
148
258
 
data/TODO.md ADDED
@@ -0,0 +1,4 @@
1
+ # 2.3
2
+
3
+ * Remove the splat from the prototypes of `Client#call`, `Client#call_loop`,
4
+ `Pipeline#call` and `Subscription#call`.
@@ -0,0 +1,62 @@
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 = Redis.new
35
+
36
+ logging_redises = [
37
+ Redis.new(:logger => log(:DEBUG)),
38
+ Redis.new(:logger => log(:INFO)),
39
+ ]
40
+
41
+ begin
42
+ require "log4r"
43
+
44
+ logging_redises += [
45
+ Redis.new(:logger => log(:DEBUG, Log4r)),
46
+ Redis.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
+ benchmark "#{redis.client.logger.class} on #{Logger::SEV_LABEL[redis.client.logger.level]}" do
58
+ stress(redis)
59
+ end
60
+ end
61
+
62
+ run 10