redispot 0.1.1 → 0.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.
- checksums.yaml +4 -4
- data/.envrc +4 -0
- data/.gitignore +2 -0
- data/.travis.yml +3 -0
- data/README.md +42 -4
- data/lib/redispot/refinements.rb +25 -0
- data/lib/redispot/server.rb +120 -96
- data/lib/redispot/version.rb +1 -1
- data/lib/redispot/working_directory.rb +43 -0
- data/lib/redispot.rb +3 -1
- data/redispot.gemspec +2 -1
- metadata +20 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ab5ec4bc687538bb74c69596ff14e72f99c6332
|
4
|
+
data.tar.gz: 67b3382aa1d1f44b5c124a3809ea81dcbea49e96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 605045c45c3c2478589896f31c169e03c6c96261e12cacc34fa4d160a043622b4f7881ebf0de2b544c86bf87e5d77860fd7f943a72f1cb67e503193be22035e7
|
7
|
+
data.tar.gz: 2c4be779c3fbe2916c0098963ce6cb49ac63323a4a056d030c706f922cab0b180bfbbef56f2c2c79b1d5b47b04861cd36498f2db128e9926eb24b62ce0e4e654
|
data/.envrc
ADDED
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,9 +2,11 @@
|
|
2
2
|
[](http://badge.fury.io/rb/redispot)
|
3
3
|
[](https://travis-ci.org/hatyuki/redispot-rb)
|
4
4
|
[](https://codeclimate.com/github/hatyuki/redispot-rb)
|
5
|
+
[](https://codeclimate.com/github/hatyuki/redispot-rb/coverage)
|
5
6
|
|
6
|
-
Launching the redis-server instance which is available only within a
|
7
|
+
Launching the redis-server instance which is available only within a scope.
|
7
8
|
It is useful when you want to test your code.
|
9
|
+
|
8
10
|
It is a Ruby clone of [Test::RedisServer](https://github.com/typester/Test-RedisServer).
|
9
11
|
|
10
12
|
|
@@ -13,10 +15,22 @@ It is a Ruby clone of [Test::RedisServer](https://github.com/typester/Test-Redis
|
|
13
15
|
require 'redis'
|
14
16
|
require 'redispot'
|
15
17
|
|
18
|
+
# Using redis-server instance within a block
|
19
|
+
redis = nil
|
16
20
|
Redispot::Server.new do |connect_info|
|
17
21
|
redis = Redis.new(connect_info)
|
18
22
|
redis.ping # => "PONG"
|
19
23
|
end
|
24
|
+
|
25
|
+
redis.ping # => Error!
|
26
|
+
|
27
|
+
# or start it manually
|
28
|
+
|
29
|
+
redispot = Redispot::Server.new
|
30
|
+
redis = Redis.new(redispot.start)
|
31
|
+
redis.ping # => "PONG"
|
32
|
+
redispot.stop
|
33
|
+
redis.ping # => Error!
|
20
34
|
```
|
21
35
|
|
22
36
|
|
@@ -25,7 +39,7 @@ end
|
|
25
39
|
Create a new instance, and start redis-server if block given.
|
26
40
|
|
27
41
|
```ruby
|
28
|
-
|
42
|
+
redispot = Redispot::Server.new(options)
|
29
43
|
|
30
44
|
# or
|
31
45
|
|
@@ -72,11 +86,35 @@ Available options are:
|
|
72
86
|
Start redis-server instance manually.
|
73
87
|
|
74
88
|
```ruby
|
75
|
-
|
76
|
-
|
89
|
+
redispot = Redispot::Server.new
|
90
|
+
|
91
|
+
redispot.start do |connect_info|
|
77
92
|
redis = Redis.new(connect_info)
|
78
93
|
redis.ping # => "PONG"
|
79
94
|
end
|
95
|
+
|
96
|
+
# or
|
97
|
+
|
98
|
+
redis = Redis.new(redispot.start)
|
99
|
+
# ... do anything
|
100
|
+
redispot.stop
|
101
|
+
```
|
102
|
+
|
103
|
+
|
104
|
+
### Redispot::Server#stop
|
105
|
+
Stop redis-server instance.
|
106
|
+
|
107
|
+
This method is automatically called from object destructor.
|
108
|
+
|
109
|
+
|
110
|
+
### Redispot::Server#connect_info
|
111
|
+
Return connection info for client library to connect this redis-server instance.
|
112
|
+
|
113
|
+
This parameter is designed to pass directly to Redis module.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
redispot = Redispot::Server.new
|
117
|
+
redis = Redis.new(redispot.connect_info)
|
80
118
|
```
|
81
119
|
|
82
120
|
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Redispot
|
2
|
+
module Refinements
|
3
|
+
refine Object do
|
4
|
+
def blank?
|
5
|
+
respond_to?(:empty?) ? !!empty? : !self
|
6
|
+
end
|
7
|
+
|
8
|
+
def present?
|
9
|
+
!blank?
|
10
|
+
end
|
11
|
+
|
12
|
+
def presence
|
13
|
+
self if present?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
refine Hash do
|
18
|
+
def symbolize_keys
|
19
|
+
each_key.each_with_object(Hash.new) do |key, memo|
|
20
|
+
memo[key.to_sym] = fetch(key)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/redispot/server.rb
CHANGED
@@ -1,27 +1,8 @@
|
|
1
|
-
require 'fileutils'
|
2
1
|
require 'timeout'
|
3
|
-
require 'tmpdir'
|
4
2
|
|
5
3
|
module Redispot
|
6
4
|
class Server
|
7
|
-
|
8
|
-
# Destractor
|
9
|
-
#
|
10
|
-
# @private
|
11
|
-
def self.destroy (pid, owner_pid, timeout)
|
12
|
-
Proc.new do
|
13
|
-
return if pid.nil? || owner_pid != Process.pid
|
14
|
-
|
15
|
-
signals = [:TERM, :INT, :KILL]
|
16
|
-
|
17
|
-
begin
|
18
|
-
Process.kill(signals.shift, pid)
|
19
|
-
Timeout.timeout(timeout) { Process.waitpid(pid) }
|
20
|
-
rescue Timeout::Error
|
21
|
-
retry
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
5
|
+
using Refinements
|
25
6
|
|
26
7
|
# Create a new instance, and start redis-server if block given.
|
27
8
|
#
|
@@ -40,10 +21,18 @@ module Redispot
|
|
40
21
|
@executable = 'redis-server'
|
41
22
|
@owner_pid = Process.pid
|
42
23
|
@pid = nil
|
43
|
-
@
|
44
|
-
@config = config
|
24
|
+
@config = config.symbolize_keys
|
45
25
|
@timeout = timeout
|
46
|
-
@
|
26
|
+
@workdir = WorkingDirectory.new(tmpdir)
|
27
|
+
|
28
|
+
if @config[:port].nil? && @config[:unixsocket].nil?
|
29
|
+
@config[:unixsocket] = "#{@workdir}/redis.sock"
|
30
|
+
@config[:port] = 0
|
31
|
+
end
|
32
|
+
|
33
|
+
if @config[:dir].nil?
|
34
|
+
@config[:dir] = @workdir
|
35
|
+
end
|
47
36
|
|
48
37
|
if @config[:loglevel].to_s == 'warning'
|
49
38
|
$stderr.puts 'Redispot::Server does not support "loglevel warning", using "notice" instead.'
|
@@ -53,117 +42,152 @@ module Redispot
|
|
53
42
|
start(&block) if block
|
54
43
|
end
|
55
44
|
|
56
|
-
# Start redis-server instance
|
45
|
+
# Start redis-server instance manually.
|
46
|
+
# If block given, the redis instance is available only within a block.
|
47
|
+
# Users must call Redispot::Server#stop they have called Redispot::Server#start without block.
|
57
48
|
#
|
58
49
|
# @example
|
59
|
-
#
|
60
|
-
# redis_server.start do |connect_info|
|
50
|
+
# redispot.start do |connect_info|
|
61
51
|
# redis = Redis.new(connect_info)
|
62
52
|
# assert_equal('PONG', redis.ping)
|
63
53
|
# end
|
64
54
|
#
|
65
|
-
#
|
66
|
-
#
|
55
|
+
# # or
|
56
|
+
#
|
57
|
+
# connect_info = redispot.start
|
58
|
+
#
|
59
|
+
# @overload start { ... }
|
60
|
+
# @yield [connect_info] Connection info for client library to connect this redis-server instance.
|
61
|
+
# @yieldparam connect_info [Hash] This parameter is designed to pass directly to Redis module.
|
62
|
+
# @overload start
|
63
|
+
# @return connect_info [Hash] This parameter is designed to pass directly to Redis module.
|
67
64
|
def start
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
return if @pid
|
66
|
+
start_process
|
67
|
+
|
68
|
+
if block_given?
|
69
|
+
yield connect_info
|
71
70
|
stop
|
71
|
+
else
|
72
|
+
connect_info
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
# Stop redis-server.
|
77
|
+
# This method is automatically called from object destructor.
|
78
|
+
#
|
79
|
+
def stop
|
80
|
+
return unless @pid
|
78
81
|
|
79
|
-
|
80
|
-
loop do
|
81
|
-
if !Process.waitpid(@pid, Process::WNOHANG).nil?
|
82
|
-
@pid = nil
|
83
|
-
raise RuntimeError, "failed to launch redis-server\n#{File.read(logfile)}"
|
84
|
-
else
|
85
|
-
if File.read(logfile) =~ /The server is now ready to accept connections/
|
86
|
-
ObjectSpace.define_finalizer(self, proc { stop })
|
87
|
-
return connect_info
|
88
|
-
end
|
89
|
-
end
|
82
|
+
signals = [:TERM, :INT, :KILL]
|
90
83
|
|
91
|
-
|
92
|
-
|
84
|
+
begin
|
85
|
+
Process.kill(signals.shift, @pid)
|
86
|
+
Timeout.timeout(@timeout) { Process.waitpid(@pid) }
|
87
|
+
rescue Timeout::Error => error
|
88
|
+
retry unless signals.empty?
|
89
|
+
raise error
|
93
90
|
end
|
94
|
-
rescue Timeout::Error
|
95
|
-
stop if @pid
|
96
|
-
raise RuntimeError, "failed to launch redis-server\n#{File.read(logfile)}"
|
97
|
-
end
|
98
91
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
begin
|
103
|
-
exec @executable, config_file, out: fh, err: fh
|
104
|
-
rescue => error
|
105
|
-
$stderr.puts "exec failed: #{error}"
|
106
|
-
exit error.errno
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
92
|
+
@pid = nil
|
93
|
+
|
94
|
+
ObjectSpace.undefine_finalizer(self)
|
110
95
|
end
|
111
96
|
|
97
|
+
# Return connection info for client library to connect this redis-server instance.
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# redispot = Redispot::Server.new
|
101
|
+
# redis = Redis.new(redispot.connect_info)
|
102
|
+
#
|
103
|
+
# @return [String] This parameter is designed to pass directly to Redis module.
|
112
104
|
def connect_info
|
113
|
-
|
114
|
-
|
115
|
-
host = config[:bind].nil? ? '0.0.0.0' : config[:bind]
|
116
|
-
port = config[:port]
|
105
|
+
host = @config[:bind].presence || '0.0.0.0'
|
106
|
+
port = @config[:port]
|
117
107
|
|
118
108
|
if port.is_a?(Fixnum) && port > 0
|
119
109
|
{ url: "redis://#{host}:#{port}/" }
|
120
110
|
else
|
121
|
-
{ path: config[:unixsocket] }
|
111
|
+
{ path: @config[:unixsocket] }
|
122
112
|
end
|
123
113
|
end
|
124
114
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
115
|
+
private
|
116
|
+
#
|
117
|
+
def start_process
|
118
|
+
logfile = "#{@workdir}/redis-server.log"
|
129
119
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
retry
|
135
|
-
end
|
120
|
+
execute_redis_server(logfile)
|
121
|
+
wait_redis_server(logfile)
|
122
|
+
ObjectSpace.define_finalizer(self, Killer.new(@pid, @timeout))
|
123
|
+
end
|
136
124
|
|
137
|
-
|
138
|
-
|
125
|
+
#
|
126
|
+
# @param logfile [Pathname]
|
127
|
+
def execute_redis_server (logfile)
|
128
|
+
File.open(logfile, 'a') do |fh|
|
129
|
+
@pid = Process.fork do
|
130
|
+
configfile = "#{@workdir}/redis.conf"
|
131
|
+
File.write(configfile, config_string)
|
139
132
|
|
140
|
-
|
133
|
+
begin
|
134
|
+
Kernel.exec(@executable, configfile, out: fh, err: fh)
|
135
|
+
rescue SystemCallError => error
|
136
|
+
$stderr.puts "exec failed: #{error}"
|
137
|
+
exit error.errno
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
rescue SystemCallError => error
|
142
|
+
$stderr.puts "failed to create log file: #{error}"
|
141
143
|
end
|
142
144
|
|
143
|
-
|
144
|
-
|
145
|
+
#
|
146
|
+
# @param logfile [Pathname]
|
147
|
+
def wait_redis_server (logfile)
|
148
|
+
Timeout.timeout(@timeout) do
|
149
|
+
while Process.waitpid(@pid, Process::WNOHANG).nil?
|
150
|
+
return if File.read(logfile) =~ /the server is now ready to accept connections/i
|
151
|
+
sleep 0.1
|
152
|
+
end
|
153
|
+
|
154
|
+
@pid = nil
|
155
|
+
raise Timeout::Error
|
156
|
+
end
|
157
|
+
rescue Timeout::Error
|
158
|
+
raise RuntimeError, "failed to launch redis-server\n#{File.read(logfile)}"
|
145
159
|
end
|
146
160
|
|
147
|
-
|
148
|
-
|
161
|
+
#
|
162
|
+
# @return [String]
|
163
|
+
def config_string
|
164
|
+
@config.each_with_object(String.new) do |(key, value), memo|
|
149
165
|
next if value.to_s.empty?
|
150
|
-
memo
|
166
|
+
memo << "#{key} #{value}\n"
|
151
167
|
end
|
168
|
+
end
|
152
169
|
|
153
|
-
|
154
|
-
|
170
|
+
|
171
|
+
class Killer # :nodoc
|
172
|
+
def initialize (redis_pid, timeout = 3)
|
173
|
+
@owner_pid = Process.pid
|
174
|
+
@redis_pid = redis_pid
|
175
|
+
@timeout = timeout
|
155
176
|
end
|
156
|
-
end
|
157
177
|
|
158
|
-
|
159
|
-
|
160
|
-
|
178
|
+
def call (*args)
|
179
|
+
return if @owner_pid != Process.pid
|
180
|
+
|
181
|
+
signals = [:TERM, :INT, :KILL]
|
161
182
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
183
|
+
begin
|
184
|
+
Process.kill(signals.shift, pid)
|
185
|
+
Timeout.timeout(timeout) { Process.waitpid(pid) }
|
186
|
+
rescue Timeout::Error => error
|
187
|
+
retry unless signals.empty?
|
188
|
+
raise error
|
189
|
+
end
|
190
|
+
end
|
167
191
|
end
|
168
192
|
|
169
193
|
end
|
data/lib/redispot/version.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module Redispot
|
5
|
+
class WorkingDirectory
|
6
|
+
|
7
|
+
# Create a temporary directory
|
8
|
+
#
|
9
|
+
# @param basedir [String]
|
10
|
+
def initialize (basedir = nil)
|
11
|
+
@directory = File.expand_path(Dir.mktmpdir(nil, basedir))
|
12
|
+
ObjectSpace.define_finalizer(self, Remover.new(@directory))
|
13
|
+
end
|
14
|
+
|
15
|
+
# Delete the temporary directory
|
16
|
+
#
|
17
|
+
def delete
|
18
|
+
FileUtils.remove_entry_secure(@directory)
|
19
|
+
ObjectSpace.undefine_finalizer(self)
|
20
|
+
rescue Errno::ENOENT
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns path to the temporary directory
|
24
|
+
#
|
25
|
+
# @return [String]
|
26
|
+
def to_s
|
27
|
+
@directory
|
28
|
+
end
|
29
|
+
|
30
|
+
class Remover # :nodoc
|
31
|
+
def initialize (directory)
|
32
|
+
@pid = Process.pid
|
33
|
+
@directory = directory
|
34
|
+
end
|
35
|
+
|
36
|
+
def call (*args)
|
37
|
+
return if @pid != Process.pid
|
38
|
+
FileUtils.remove_entry_secure(@directory)
|
39
|
+
rescue Errno::ENOENT
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/redispot.rb
CHANGED
data/redispot.gemspec
CHANGED
@@ -17,10 +17,11 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
18
|
spec.require_paths = ['lib']
|
19
19
|
|
20
|
-
spec.required_ruby_version = '>= 2.
|
20
|
+
spec.required_ruby_version = '>= 2.1.0'
|
21
21
|
|
22
22
|
spec.add_development_dependency 'bundler', '~> 1.10'
|
23
23
|
spec.add_development_dependency 'rake', '~> 10.0'
|
24
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
24
25
|
spec.add_development_dependency 'pry'
|
25
26
|
spec.add_development_dependency 'redis'
|
26
27
|
spec.add_development_dependency 'test-unit'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redispot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hatyuki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: codeclimate-test-reporter
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: pry
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,6 +116,7 @@ executables: []
|
|
102
116
|
extensions: []
|
103
117
|
extra_rdoc_files: []
|
104
118
|
files:
|
119
|
+
- ".envrc"
|
105
120
|
- ".gitignore"
|
106
121
|
- ".travis.yml"
|
107
122
|
- Gemfile
|
@@ -111,8 +126,10 @@ files:
|
|
111
126
|
- bin/console
|
112
127
|
- bin/setup
|
113
128
|
- lib/redispot.rb
|
129
|
+
- lib/redispot/refinements.rb
|
114
130
|
- lib/redispot/server.rb
|
115
131
|
- lib/redispot/version.rb
|
132
|
+
- lib/redispot/working_directory.rb
|
116
133
|
- redispot.gemspec
|
117
134
|
homepage: https://github.com/hatyuki/redispot-rb
|
118
135
|
licenses:
|
@@ -126,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
143
|
requirements:
|
127
144
|
- - ">="
|
128
145
|
- !ruby/object:Gem::Version
|
129
|
-
version: 2.
|
146
|
+
version: 2.1.0
|
130
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
148
|
requirements:
|
132
149
|
- - ">="
|