redis 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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