redis 2.1.1 → 2.2.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 (79) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +190 -0
  4. data/Rakefile +194 -79
  5. data/benchmarking/logging.rb +62 -0
  6. data/benchmarking/pipeline.rb +51 -0
  7. data/benchmarking/speed.rb +21 -0
  8. data/benchmarking/suite.rb +24 -0
  9. data/benchmarking/thread_safety.rb +38 -0
  10. data/benchmarking/worker.rb +71 -0
  11. data/examples/basic.rb +15 -0
  12. data/examples/dist_redis.rb +43 -0
  13. data/examples/incr-decr.rb +17 -0
  14. data/examples/list.rb +26 -0
  15. data/examples/pubsub.rb +31 -0
  16. data/examples/sets.rb +36 -0
  17. data/examples/unicorn/config.ru +3 -0
  18. data/examples/unicorn/unicorn.rb +20 -0
  19. data/lib/redis.rb +612 -156
  20. data/lib/redis/client.rb +98 -57
  21. data/lib/redis/connection.rb +9 -134
  22. data/lib/redis/connection/command_helper.rb +45 -0
  23. data/lib/redis/connection/hiredis.rb +49 -0
  24. data/lib/redis/connection/registry.rb +12 -0
  25. data/lib/redis/connection/ruby.rb +131 -0
  26. data/lib/redis/connection/synchrony.rb +125 -0
  27. data/lib/redis/distributed.rb +161 -5
  28. data/lib/redis/pipeline.rb +6 -0
  29. data/lib/redis/version.rb +3 -0
  30. data/redis.gemspec +24 -0
  31. data/test/commands_on_hashes_test.rb +32 -0
  32. data/test/commands_on_lists_test.rb +60 -0
  33. data/test/commands_on_sets_test.rb +78 -0
  34. data/test/commands_on_sorted_sets_test.rb +109 -0
  35. data/test/commands_on_strings_test.rb +80 -0
  36. data/test/commands_on_value_types_test.rb +88 -0
  37. data/test/connection_handling_test.rb +87 -0
  38. data/test/db/.gitignore +1 -0
  39. data/test/distributed_blocking_commands_test.rb +53 -0
  40. data/test/distributed_commands_on_hashes_test.rb +12 -0
  41. data/test/distributed_commands_on_lists_test.rb +24 -0
  42. data/test/distributed_commands_on_sets_test.rb +85 -0
  43. data/test/distributed_commands_on_strings_test.rb +50 -0
  44. data/test/distributed_commands_on_value_types_test.rb +73 -0
  45. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  46. data/test/distributed_connection_handling_test.rb +25 -0
  47. data/test/distributed_internals_test.rb +18 -0
  48. data/test/distributed_key_tags_test.rb +53 -0
  49. data/test/distributed_persistence_control_commands_test.rb +24 -0
  50. data/test/distributed_publish_subscribe_test.rb +101 -0
  51. data/test/distributed_remote_server_control_commands_test.rb +31 -0
  52. data/test/distributed_sorting_test.rb +21 -0
  53. data/test/distributed_test.rb +60 -0
  54. data/test/distributed_transactions_test.rb +34 -0
  55. data/test/encoding_test.rb +16 -0
  56. data/test/error_replies_test.rb +53 -0
  57. data/test/helper.rb +145 -0
  58. data/test/internals_test.rb +157 -0
  59. data/test/lint/hashes.rb +114 -0
  60. data/test/lint/internals.rb +41 -0
  61. data/test/lint/lists.rb +93 -0
  62. data/test/lint/sets.rb +66 -0
  63. data/test/lint/sorted_sets.rb +167 -0
  64. data/test/lint/strings.rb +137 -0
  65. data/test/lint/value_types.rb +84 -0
  66. data/test/persistence_control_commands_test.rb +22 -0
  67. data/test/pipelining_commands_test.rb +123 -0
  68. data/test/publish_subscribe_test.rb +158 -0
  69. data/test/redis_mock.rb +80 -0
  70. data/test/remote_server_control_commands_test.rb +63 -0
  71. data/test/sorting_test.rb +44 -0
  72. data/test/synchrony_driver.rb +57 -0
  73. data/test/test.conf +8 -0
  74. data/test/thread_safety_test.rb +30 -0
  75. data/test/transactions_test.rb +100 -0
  76. data/test/unknown_commands_test.rb +14 -0
  77. data/test/url_param_test.rb +60 -0
  78. metadata +128 -19
  79. data/README.markdown +0 -129
@@ -0,0 +1,8 @@
1
+ nohup.out
2
+ redis/*
3
+ rdsrv
4
+ pkg/*
5
+ coverage/*
6
+ .idea
7
+ *.rdb
8
+ *.swp
@@ -0,0 +1,34 @@
1
+ # 2.2.0 (unreleased)
2
+
3
+ * Added method `Redis#without_reconnect` that ensures the client will not try
4
+ to reconnect when running the code inside the specified block.
5
+
6
+ * Thread-safe by default. Thread safety can be explicitly disabled by passing
7
+ `:thread_safe => false` as argument.
8
+
9
+ * Commands called inside a MULTI/EXEC no longer raise error replies, since a
10
+ successful EXEC means the commands inside the block were executed.
11
+
12
+ * MULTI/EXEC blocks are pipelined.
13
+
14
+ * Don't disconnect on error replies.
15
+
16
+ * Use `IO#syswrite` instead of `IO#write` because write buffering is not
17
+ necessary.
18
+
19
+ * Connect to a unix socket by passing the `:path` option as argument.
20
+
21
+ * The timeout value is coerced into a float, allowing sub-second timeouts.
22
+
23
+ * Accept both `:with_scores` _and_ `:withscores` as argument to sorted set
24
+ commands.
25
+
26
+ * Use [hiredis](https://github.com/pietern/hiredis-rb) (v0.3 or higher) by
27
+ requiring "redis/connection/hiredis".
28
+
29
+ * Use [em-synchrony](https://github.com/igrigorik/em-synchrony) by requiring
30
+ "redis/connection/synchrony".
31
+
32
+ # 2.1.1
33
+
34
+ See commit log.
@@ -0,0 +1,190 @@
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
+ ## Contributing
189
+
190
+ [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")
@@ -41,12 +17,12 @@ task :run => [:start, :test, :stop]
41
17
  desc "Start the Redis server"
42
18
  task :start do
43
19
  redis_running = \
44
- begin
45
- File.exists?(REDIS_PID) && Process.kill(0, File.read(REDIS_PID).to_i)
46
- rescue Errno::ESRCH
47
- FileUtils.rm REDIS_PID
48
- false
49
- end
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
25
+ end
50
26
 
51
27
  system "redis-server #{REDIS_CNF}" unless redis_running
52
28
  end
@@ -59,53 +35,187 @@ task :stop do
59
35
  end
60
36
  end
61
37
 
38
+ def isolated(&block)
39
+ pid = fork { yield }
40
+ Process.wait(pid)
41
+ end
42
+
62
43
  desc "Run the test suite"
63
- task :test do
64
- require 'cutest'
44
+ task :test => ["test:ruby", "test:hiredis", "test:synchrony"]
65
45
 
66
- Cutest.run(Dir['./test/**/*_test.rb'])
67
- end
46
+ namespace :test do
47
+ desc "Run tests against the Ruby driver"
48
+ task :ruby do
49
+ require "cutest"
68
50
 
69
- Rake::GemPackageTask.new(spec) do |pkg|
70
- pkg.gem_spec = spec
71
- end
51
+ isolated do
52
+ Cutest.run(Dir["./test/**/*_test.rb"])
53
+ end
54
+ end
72
55
 
73
- desc "install the gem locally"
74
- task :install => [:package] do
75
- sh %{gem install pkg/#{GEM}-#{GEM_VERSION}}
76
- end
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}"
66
+
67
+ Cutest.run(Dir["./test/**/*_test.rb"])
68
+ rescue
69
+ puts "Skipping tests against hiredis"
70
+ end
71
+ end
72
+ end
77
73
 
78
- desc "create a gemspec file"
79
- task :gemspec do
80
- File.open("#{GEM}.gemspec", "w") do |file|
81
- file.puts spec.to_ruby
74
+ desc "Run tests against the em-synchrony driver"
75
+ task :synchrony do
76
+ require "cutest"
77
+
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 Exception => e
91
+ puts e
92
+ puts e.backtrace.join("\n")
93
+ puts "Skipping tests against em-synchrony"
94
+ end
95
+ end
82
96
  end
83
97
  end
84
98
 
85
- desc "Generate YARDoc"
86
- task :yardoc do
87
- require "yard"
99
+ task :doc => ["doc:generate", "doc:prepare"]
100
+
101
+ namespace :doc do
102
+ task :generate do
103
+ require "shellwords"
104
+
105
+ `rm -rf doc`
106
+
107
+ current_branch = `git branch`[/^\* (.*)$/, 1]
108
+
109
+ begin
110
+ tags = `git tag -l`.split("\n").sort.reverse
111
+
112
+ tags.each do |tag|
113
+ `git checkout -q #{tag} 2>/dev/null`
114
+
115
+ unless $?.success?
116
+ $stderr.puts "Need a clean working copy. Please git-stash away."
117
+ exit 1
118
+ end
119
+
120
+ puts tag
121
+
122
+ `mkdir -p doc/#{tag}`
123
+
124
+ files = `git ls-tree -r HEAD lib`.split("\n").map do |line|
125
+ line[/\t(.*)$/, 1]
126
+ end
127
+
128
+ opts = [
129
+ "--title", "A Ruby client for Redis",
130
+ "--output", "doc/#{tag}",
131
+ "--no-cache",
132
+ "--no-save",
133
+ "-q",
134
+ *files
135
+ ]
136
+
137
+ `yardoc #{Shellwords.shelljoin opts}`
138
+ end
139
+ ensure
140
+ `git checkout -q #{current_branch}`
141
+ end
142
+ end
143
+
144
+ task :prepare do
145
+ versions = `git tag -l`.split("\n").grep(/^v/).sort
146
+ latest_version = versions.last
147
+
148
+ File.open("doc/.htaccess", "w") do |file|
149
+ file.puts "RedirectMatch 302 ^/?$ /#{latest_version}"
150
+ end
151
+
152
+ File.open("doc/robots.txt", "w") do |file|
153
+ file.puts "User-Agent: *"
154
+
155
+ (versions - [latest_version]).each do |version|
156
+ file.puts "Disallow: /#{version}"
157
+ end
158
+ end
159
+
160
+ google_analytics = <<-EOS
161
+ <script type="text/javascript">
162
+
163
+ var _gaq = _gaq || [];
164
+ _gaq.push(['_setAccount', 'UA-11356145-2']);
165
+ _gaq.push(['_trackPageview']);
166
+
167
+ (function() {
168
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
169
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
170
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
171
+ })();
172
+
173
+ </script>
174
+ EOS
175
+
176
+ Dir["doc/**/*.html"].each do |path|
177
+ lines = IO.readlines(path)
178
+
179
+ File.open(path, "w") do |file|
180
+ lines.each do |line|
181
+ if line.include?("</head>")
182
+ file.write(google_analytics)
183
+ end
88
184
 
89
- opts = ["--title", "A Ruby client for Redis"]
185
+ file.write(line)
186
+ end
187
+ end
188
+ end
189
+ end
90
190
 
91
- YARD::CLI::Yardoc.run(*opts)
191
+ task :deploy do
192
+ system "rsync --del -avz doc/* redis-rb.keyvalue.org:deploys/redis-rb.keyvalue.org/"
193
+ end
92
194
  end
93
195
 
94
196
  namespace :commands do
95
197
  def redis_commands
96
- $redis_commands ||= begin
198
+ $redis_commands ||= doc.keys.map do |key|
199
+ key.split(" ").first.downcase
200
+ end.uniq
201
+ end
202
+
203
+ def doc
204
+ $doc ||= begin
97
205
  require "open-uri"
98
- open("http://dimaion.com/redis/master").read.split
206
+ require "json"
207
+
208
+ JSON.parse(open("https://github.com/antirez/redis-doc/raw/master/commands.json").read)
99
209
  end
100
210
  end
101
211
 
102
- task :doc do
103
- source = File.read("lib/redis.rb")
212
+ def document(file)
213
+ source = File.read(file)
104
214
 
105
- redis_commands.each do |name, text|
106
- source.sub!(/(?:^ *#.*\n)*^( *)def #{name}(\(|$)/) do
107
- indent, extra_args = $1, $2
108
- comment = "#{indent}# #{text.strip}"
215
+ doc.each do |name, command|
216
+ source.sub!(/(?:^ *# .*\n)*(^ *#\n(^ *# .+?\n)*)*^( *)def #{name.downcase}(\(|$)/) do
217
+ extra_comments, indent, extra_args = $1, $3, $4
218
+ comment = "#{indent}# #{command["summary"].strip}."
109
219
 
110
220
  IO.popen("par p#{2 + indent.size} 80", "r+") do |io|
111
221
  io.puts comment
@@ -113,43 +223,48 @@ namespace :commands do
113
223
  comment = io.read
114
224
  end
115
225
 
116
- "#{comment}#{indent}def #{name}#{extra_args}"
226
+ "#{comment}#{extra_comments}#{indent}def #{name.downcase}#{extra_args}"
117
227
  end
118
228
  end
119
229
 
120
- File.open("lib/redis.rb", "w") { |f| f.write(source) }
230
+ File.open(file, "w") { |f| f.write(source) }
231
+ end
232
+
233
+ task :doc do
234
+ document "lib/redis.rb"
235
+ document "lib/redis/distributed.rb"
121
236
  end
122
237
 
123
238
  task :verify do
124
239
  require "redis"
125
240
  require "stringio"
126
241
 
127
- log = StringIO.new
242
+ require "./test/helper"
128
243
 
129
- at_exit do
130
- redis = Redis.new
244
+ OPTIONS[:logger] = Logger.new("./tmp/log")
131
245
 
132
- report = ["Command", "\033[0mDefined?\033[0m", "\033[0mTested?\033[0m"]
246
+ Rake::Task["test:ruby"].invoke
133
247
 
134
- yes, no = "\033[1;32mYes\033[0m", "\033[1;31mNo\033[0m"
248
+ redis = Redis.new
135
249
 
136
- redis_commands.sort.each do |name, _|
137
- defined, tested = redis.respond_to?(name), log.string[">> #{name.upcase}"]
250
+ report = ["Command", "\033[0mDefined?\033[0m", "\033[0mTested?\033[0m"]
138
251
 
139
- next if defined && tested
252
+ yes, no = "\033[1;32mYes\033[0m", "\033[1;31mNo\033[0m"
140
253
 
141
- report << name
142
- report << (defined ? yes : no)
143
- report << (tested ? yes : no)
144
- end
254
+ log = File.read("./tmp/log")
145
255
 
146
- IO.popen("rs 0 3", "w") do |io|
147
- io.puts report.join("\n")
148
- end
149
- end
256
+ redis_commands.sort.each do |name, _|
257
+ defined, tested = redis.respond_to?(name), log[">> #{name.upcase}"]
258
+
259
+ next if defined && tested
150
260
 
151
- Dir["test/**/redis_test.rb"].each { |f| require "./#{f}" }
261
+ report << name
262
+ report << (defined ? yes : no)
263
+ report << (tested ? yes : no)
264
+ end
152
265
 
153
- RedisTest::OPTIONS[:logger] = Logger.new(log)
266
+ IO.popen("rs 0 3", "w") do |io|
267
+ io.puts report.join("\n")
268
+ end
154
269
  end
155
270
  end