prize 0.1.3 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +0 -3
- data/CODE_OF_CONDUCT.md +3 -3
- data/Gemfile +2 -2
- data/Gemfile.lock +39 -37
- data/LICENSE.txt +1 -1
- data/Rakefile +0 -4
- data/exe/prize +3 -1
- data/exe/prize_setsid_wrapper +5 -0
- data/lib/prize/app.rb +72 -0
- data/lib/prize/cli.rb +92 -50
- data/lib/prize/commands/info.rb +48 -0
- data/lib/prize/commands/reconnect.rb +23 -0
- data/lib/prize/commands.rb +5 -0
- data/lib/prize/ext/object.rb +18 -0
- data/lib/prize/ext/string.rb +5 -0
- data/lib/prize/ext/time.rb +15 -0
- data/lib/prize/ext.rb +3 -0
- data/lib/prize/id.rb +59 -0
- data/lib/prize/repl.rb +31 -0
- data/lib/prize/ssh_proxy.rb +31 -0
- data/lib/prize/ssh_proxy_patch.rb +88 -0
- data/lib/prize/version.rb +1 -1
- data/lib/prize.rb +8 -0
- data/prize.gemspec +10 -13
- metadata +67 -41
- data/.rspec +0 -3
- data/.travis.yml +0 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d04ff48aa10ea8ecf7c4d3883752b6082e14e9dbe395d1ca15aa5079a388f76
|
|
4
|
+
data.tar.gz: 503e2b620eaa9d999da44c1d3b4a4be599e07b1285bde96d2a2bb89faa5af7b7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a0c33a24361053e8d42fbcc3e12e046c750412ddae22bac7e682962e7c25b6808726abd130b7d96b8d70dfb5fb3e5410a098f8e7cdd774dad54945399da36e0
|
|
7
|
+
data.tar.gz: 773e13e85c2b228f89510808958ee3194272fcc8847a03606ec015fc0023ca6e22f0b9f86f89f905fbcf141377458d5dcc965bba5002ed79a113503c2f123234
|
data/.gitignore
CHANGED
data/CODE_OF_CONDUCT.md
CHANGED
|
@@ -68,7 +68,7 @@ members of the project's leadership.
|
|
|
68
68
|
## Attribution
|
|
69
69
|
|
|
70
70
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
71
|
-
available at [
|
|
71
|
+
available at [https://contributor-covenant.org/version/1/4][version]
|
|
72
72
|
|
|
73
|
-
[homepage]:
|
|
74
|
-
[version]:
|
|
73
|
+
[homepage]: https://contributor-covenant.org
|
|
74
|
+
[version]: https://contributor-covenant.org/version/1/4/
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,62 +1,64 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
prize (0.1
|
|
5
|
-
activesupport
|
|
6
|
-
hiredis (
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
prize (0.3.1)
|
|
5
|
+
activesupport (~> 6.0.3)
|
|
6
|
+
hiredis (~> 0.6.0)
|
|
7
|
+
net-ssh-gateway (~> 2.0.0)
|
|
8
|
+
pry (~> 0.13.1)
|
|
9
|
+
pry-byebug (~> 3.9.0)
|
|
10
|
+
pry-doc (>= 1.0.0)
|
|
11
|
+
rainbow (~> 3.0.0)
|
|
12
|
+
redis (~> 4.0.0)
|
|
9
13
|
thor (~> 0.20.0)
|
|
10
14
|
|
|
11
15
|
GEM
|
|
12
16
|
remote: https://rubygems.org/
|
|
13
17
|
specs:
|
|
14
|
-
activesupport (6.0.
|
|
18
|
+
activesupport (6.0.4.4)
|
|
15
19
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
16
20
|
i18n (>= 0.7, < 2)
|
|
17
21
|
minitest (~> 5.1)
|
|
18
22
|
tzinfo (~> 1.1)
|
|
19
|
-
zeitwerk (~> 2.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
|
24
|
+
byebug (11.1.3)
|
|
25
|
+
coderay (1.1.3)
|
|
26
|
+
concurrent-ruby (1.1.9)
|
|
23
27
|
hiredis (0.6.3)
|
|
24
|
-
i18n (1.
|
|
28
|
+
i18n (1.8.11)
|
|
25
29
|
concurrent-ruby (~> 1.0)
|
|
26
|
-
method_source (0.
|
|
27
|
-
minitest (5.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
44
|
-
rspec-support (~> 3.8.0)
|
|
45
|
-
rspec-support (3.8.2)
|
|
30
|
+
method_source (1.0.0)
|
|
31
|
+
minitest (5.15.0)
|
|
32
|
+
net-ssh (6.1.0)
|
|
33
|
+
net-ssh-gateway (2.0.0)
|
|
34
|
+
net-ssh (>= 4.0.0)
|
|
35
|
+
pry (0.13.1)
|
|
36
|
+
coderay (~> 1.1)
|
|
37
|
+
method_source (~> 1.0)
|
|
38
|
+
pry-byebug (3.9.0)
|
|
39
|
+
byebug (~> 11.0)
|
|
40
|
+
pry (~> 0.13.0)
|
|
41
|
+
pry-doc (1.3.0)
|
|
42
|
+
pry (~> 0.11)
|
|
43
|
+
yard (~> 0.9.11)
|
|
44
|
+
rainbow (3.0.0)
|
|
45
|
+
rake (12.3.3)
|
|
46
|
+
redis (4.0.3)
|
|
46
47
|
thor (0.20.3)
|
|
47
48
|
thread_safe (0.3.6)
|
|
48
|
-
tzinfo (1.2.
|
|
49
|
+
tzinfo (1.2.9)
|
|
49
50
|
thread_safe (~> 0.1)
|
|
50
|
-
|
|
51
|
+
webrick (1.7.0)
|
|
52
|
+
yard (0.9.27)
|
|
53
|
+
webrick (~> 1.7.0)
|
|
54
|
+
zeitwerk (2.5.3)
|
|
51
55
|
|
|
52
56
|
PLATFORMS
|
|
53
57
|
ruby
|
|
54
58
|
|
|
55
59
|
DEPENDENCIES
|
|
56
|
-
bundler (~> 1.17)
|
|
57
60
|
prize!
|
|
58
|
-
rake (~>
|
|
59
|
-
rspec (~> 3.0)
|
|
61
|
+
rake (~> 12.0)
|
|
60
62
|
|
|
61
63
|
BUNDLED WITH
|
|
62
|
-
1.
|
|
64
|
+
2.1.2
|
data/LICENSE.txt
CHANGED
data/Rakefile
CHANGED
data/exe/prize
CHANGED
data/lib/prize/app.rb
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'net/ssh/gateway'
|
|
2
|
+
require 'redis'
|
|
3
|
+
|
|
4
|
+
module Prize
|
|
5
|
+
class App
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :options, :redis, :redis_client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(options)
|
|
12
|
+
require 'active_support/all'
|
|
13
|
+
@options = options
|
|
14
|
+
load_initializer!
|
|
15
|
+
@redis = Redis.new(connect_options)
|
|
16
|
+
App.options = @options
|
|
17
|
+
App.redis = @redis
|
|
18
|
+
App.redis_client = @redis.instance_variable_get('@client')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def connect_options
|
|
22
|
+
opts = @options.to_h.slice(:url, :host, :port, :path, :db, :password, :timeout,
|
|
23
|
+
:connect_timeout, :replica, :cluster)
|
|
24
|
+
if @options.ssh_host
|
|
25
|
+
proxy = start_ssh_proxy!
|
|
26
|
+
opts.merge!(proxy)
|
|
27
|
+
end
|
|
28
|
+
opts
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def load_initializer!
|
|
32
|
+
if File.exists?(File.expand_path('~/.prizerc'))
|
|
33
|
+
load(File.expand_path('~/.prizerc'))
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def start_ssh_proxy!
|
|
38
|
+
ssh_config = {
|
|
39
|
+
host: @options.ssh_host,
|
|
40
|
+
forward_host: @options.host || '127.0.0.1',
|
|
41
|
+
forward_port: @options.port || 6379
|
|
42
|
+
}
|
|
43
|
+
@options.ssh_user.try { |e| ssh_config.merge!(user: e) }
|
|
44
|
+
@options.ssh_port.try { |e| ssh_config.merge!(port: e) }
|
|
45
|
+
@options.ssh_password.try { |e| ssh_config.merge!(password: e) }
|
|
46
|
+
@options.ssh_local_port.try { |e| ssh_config.merge!(local_port: e) }
|
|
47
|
+
local_ssh_proxy_port = Prize::SSHProxy.connect(ssh_config)
|
|
48
|
+
{
|
|
49
|
+
host: '127.0.0.1',
|
|
50
|
+
port: local_ssh_proxy_port
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def run!
|
|
55
|
+
if @options.code.present?
|
|
56
|
+
@redis.instance_eval(@options.code)
|
|
57
|
+
elsif @options.args.present?
|
|
58
|
+
@options.args.each do |rb|
|
|
59
|
+
@redis.instance_eval(IO.read(rb))
|
|
60
|
+
end
|
|
61
|
+
elsif STDIN.isatty
|
|
62
|
+
run_repl!
|
|
63
|
+
else
|
|
64
|
+
@redis.instance_eval(STDIN.read)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def run_repl!
|
|
69
|
+
Repl.new
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/prize/cli.rb
CHANGED
|
@@ -1,58 +1,100 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
require 'hiredis'
|
|
4
|
-
require 'pry'
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'ostruct'
|
|
5
3
|
|
|
6
4
|
module Prize
|
|
7
|
-
class Cli
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class_option :url, type: :string, aliases: ['-u'], required: false, desc: 'Server URL, for a TCP connection: `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket connection: `unix://[path to Redis socket]`. This overrides all other options.'
|
|
13
|
-
class_option :host, type: :string, aliases: ['-h'], required: false, desc: 'Server hostname (default: 127.0.0.1)'
|
|
14
|
-
class_option :port, type: :numeric, aliases: ['-p'], required: false, desc: 'Server port (default: 6379)'
|
|
15
|
-
class_option :path, type: :string, aliases: ['-s', '--socket'], required: false, desc: 'Server socket (overrides hostname and port)'
|
|
16
|
-
class_option :timeout, type: :numeric, required: false, desc: 'Timeout in seconds (default: 5.0)'
|
|
17
|
-
class_option :connect_timeout, type: :numeric, required: false, desc: 'Timeout for initial connect in seconds (default: same as timeout)'
|
|
18
|
-
class_option :password, type: :string, aliases: ['-a'], required: false, desc: 'Password to authenticate against server'
|
|
19
|
-
class_option :db, type: :numeric, aliases: ['-n'], required: false, desc: 'Database number (default: 0)'
|
|
20
|
-
class_option :replica, type: :boolean, required: false, desc: 'Whether to use readonly replica nodes in Redis Cluster or not'
|
|
21
|
-
class_option :cluster, type: :string, required: false, desc: 'List of cluster nodes to contact, format: URL1,URL2,URL3...'
|
|
22
|
-
def main
|
|
23
|
-
redis = Redis.new(build_options)
|
|
24
|
-
Pry.config.prompt = build_prompt(redis)
|
|
25
|
-
if File.exists?(File.expand_path('~/.prizerc'))
|
|
26
|
-
Pry.load_file_at_toplevel(File.expand_path('~/.prizerc'))
|
|
5
|
+
class Cli
|
|
6
|
+
class << self
|
|
7
|
+
def start
|
|
8
|
+
parse_options!
|
|
9
|
+
App.new(@options).run!
|
|
27
10
|
end
|
|
28
|
-
Pry.start(redis)
|
|
29
|
-
end
|
|
30
11
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
opts
|
|
36
|
-
|
|
12
|
+
def parse_options!
|
|
13
|
+
@options = OpenStruct.new
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
OptionParser.new do |opts|
|
|
17
|
+
opts.banner = <<~EOF
|
|
18
|
+
Usage: prize [options]
|
|
19
|
+
EOF
|
|
20
|
+
|
|
21
|
+
opts.on('-uURL', '--url=URL', 'Server URL, for a TCP connection: `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket connection: `unix://[path to Redis socket]`. This overrides all other options.') do |url|
|
|
22
|
+
@options.url = url
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
opts.on('-hHOST', '--host=HOST', 'Server hostname (default: 127.0.0.1)') do |host|
|
|
26
|
+
@options.host = host
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
opts.on('-pPORT', '--port=PORT', 'Server port (default: 6379)') do |port|
|
|
30
|
+
@options.port = port.to_i
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
opts.on('-sPATH', '--sock=PATH', 'Server socket (overrides hostname and port)') do |path|
|
|
34
|
+
@options.path = path
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
opts.on('-dDB', '--db=DB', 'Specify database') do |db|
|
|
38
|
+
@options.db = db
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
opts.on('-PPASSWORD', '--password=PASSWORD', 'Specify password') do |password|
|
|
42
|
+
@options.password = password
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
opts.on('', '--timeout=TIMEOUT', 'Timeout in seconds (default: 5.0)') do |timeout|
|
|
46
|
+
@options.timeout = timeout.to_i
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
opts.on('', '--connect-timeout=TIMEOUT', 'Timeout for initial connect in seconds (default: same as timeout)') do |timeout|
|
|
50
|
+
@options.connect_timeout = timeout.to_i
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
opts.on('', '--replica', 'Whether to use readonly replica nodes in Redis Cluster or not') do
|
|
54
|
+
@options.replica = true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
opts.on('', '--cluster=CLUSTER_URL', 'List of cluster nodes to contact, format: URL1,URL2,URL3...') do |cluster|
|
|
58
|
+
@options.cluster = cluster.split(',')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
opts.on('-HSSH_HOST', '--ssh-host=SSH_HOST', 'Specify SSH host') do |ssh_host|
|
|
62
|
+
@options.ssh_host = ssh_host
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
opts.on('-OSSH_PORT', '--ssh-port=SSH_PORT', 'Specify SSH port') do |ssh_port|
|
|
66
|
+
@options.ssh_port = ssh_port.to_i
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
opts.on('-USSH_USER', '--ssh-user=SSH_USER', 'Specify SSH user') do |ssh_user|
|
|
70
|
+
@options.ssh_user = ssh_user
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
opts.on('-WSSH_PASSWORD', '--ssh-password=SSH_PASSWORD', 'Specify SSH password') do |ssh_password|
|
|
74
|
+
@options.ssh_password = ssh_password
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
opts.on('-LSSH_LOCAL_PORT', '--ssh-local-port=SSH_LOCAL_PORT', 'Specify local SSH proxy port') do |local_port|
|
|
78
|
+
@options.ssh_local_port = local_port.to_i
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
opts.on('-ECODE', '--eval=CODE', 'evaluate CODE') do |code|
|
|
82
|
+
@options.code = code
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
opts.on('-V', '--version', 'Prints version') do
|
|
86
|
+
puts "PRIZE #{Prize::VERSION}"
|
|
87
|
+
exit
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
opts.on('', '--help', 'Prints this help') do
|
|
91
|
+
puts opts
|
|
92
|
+
exit
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end.parse!
|
|
37
96
|
|
|
38
|
-
|
|
39
|
-
opts = redis.instance_variable_get('@client').options
|
|
40
|
-
host = opts[:url] || opts[:path] || "#{opts[:host]}:#{opts[:port]}/#{opts[:db]}"
|
|
41
|
-
prompt_fn = proc do |obj, nest_level, _|
|
|
42
|
-
p = ""
|
|
43
|
-
p += if obj == redis
|
|
44
|
-
"Redis<#{host}>:"
|
|
45
|
-
else
|
|
46
|
-
"#{obj}:"
|
|
47
|
-
end
|
|
48
|
-
p += "#{nest_level}> "
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
[prompt_fn,
|
|
52
|
-
proc do |obj, nest_level, other|
|
|
53
|
-
"*#{prompt_fn.call obj, nest_level, other}"
|
|
54
|
-
end
|
|
55
|
-
]
|
|
97
|
+
@options.args = ARGV
|
|
56
98
|
end
|
|
57
99
|
end
|
|
58
100
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'rainbow'
|
|
2
|
+
|
|
3
|
+
module Prize::Commands
|
|
4
|
+
module Info
|
|
5
|
+
class << self
|
|
6
|
+
def db_info
|
|
7
|
+
<<~EOF
|
|
8
|
+
|
|
9
|
+
Redis Connection Information:
|
|
10
|
+
|
|
11
|
+
Active: #{color_boolean(Prize::App.redis_client.connected?)}
|
|
12
|
+
Host: #{Prize::App.options.host || '127.0.0.1'}
|
|
13
|
+
Port: #{Prize::App.options.port || 6379}
|
|
14
|
+
Password: #{(Prize::App.options.password || '').gsub(/./, '*')}
|
|
15
|
+
Database: #{Prize::App.options.db || 0}
|
|
16
|
+
EOF
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ssh_info
|
|
20
|
+
<<~EOF
|
|
21
|
+
|
|
22
|
+
SSH Connection Information:
|
|
23
|
+
|
|
24
|
+
Active: #{color_boolean(Prize::SSHProxy.active?)}
|
|
25
|
+
Host: #{Prize::App.options.ssh_host}
|
|
26
|
+
Port: #{Prize::App.options.ssh_port || ''}
|
|
27
|
+
Username: #{Prize::App.options.ssh_user}
|
|
28
|
+
Password: #{(Prize::App.options.ssh_password || '').gsub(/./, '*')}
|
|
29
|
+
Local Port: #{Prize::SSHProxy.local_ssh_proxy_port}
|
|
30
|
+
EOF
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
def color_boolean(bool)
|
|
35
|
+
if bool
|
|
36
|
+
Rainbow('TRUE').green
|
|
37
|
+
else
|
|
38
|
+
Rainbow('FALSE').red
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Pry.commands.block_command 'inf' do
|
|
44
|
+
puts Info::db_info
|
|
45
|
+
puts Info::ssh_info if Prize::App.options.ssh_host.present?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Prize::Commands
|
|
2
|
+
module Reconnect
|
|
3
|
+
class << self
|
|
4
|
+
def reconnect
|
|
5
|
+
Prize::SSHProxy.reconnect if Prize::App.options.ssh_host.present?
|
|
6
|
+
Prize::App.redis_client.reconnect unless Prize::App.redis_client.connected?
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def reconnect!
|
|
10
|
+
Prize::SSHProxy.reconnect! if Prize::App.options.ssh_host.present?
|
|
11
|
+
Prize::App.redis_client.reconnect
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Pry.commands.block_command 'reconnect' do
|
|
16
|
+
Reconnect.reconnect
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Pry.commands.block_command 'reconnect!' do
|
|
20
|
+
Reconnect.reconnect!
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'active_support/core_ext/time/conversions'
|
|
2
|
+
require 'active_support/core_ext/object/json'
|
|
3
|
+
|
|
4
|
+
class Time
|
|
5
|
+
DATE_FORMATS ||= {}
|
|
6
|
+
DATE_FORMATS[:default] = '%Y-%m-%d %H:%M:%S'
|
|
7
|
+
|
|
8
|
+
def inspect
|
|
9
|
+
to_s
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def as_json(*args)
|
|
13
|
+
to_s
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/prize/ext.rb
ADDED
data/lib/prize/id.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Prize
|
|
2
|
+
class ID
|
|
3
|
+
@worker_id_bits = 5
|
|
4
|
+
@data_center_id_bits = 5
|
|
5
|
+
@max_worker_id = -1 ^ (-1 << @worker_id_bits)
|
|
6
|
+
@max_data_center_id = -1 ^ (-1 << @data_center_id_bits)
|
|
7
|
+
|
|
8
|
+
@sequence_bits = 12
|
|
9
|
+
@worker_id_shift = @sequence_bits
|
|
10
|
+
@data_center_id_shift = @sequence_bits + @worker_id_shift
|
|
11
|
+
@timestamp_left_shift = @sequence_bits + @worker_id_bits + @data_center_id_bits
|
|
12
|
+
@sequence_mask = -1 ^ (-1 << @sequence_bits)
|
|
13
|
+
|
|
14
|
+
@id_epoch = (Time.new(2018, 1, 1, 0, 0, 0).to_f * 1000).to_i
|
|
15
|
+
@worker_id = 0
|
|
16
|
+
@data_center_id = 0
|
|
17
|
+
@sequence = 0
|
|
18
|
+
|
|
19
|
+
@last_timestamp = -1
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def long
|
|
23
|
+
ts = (Time.now.to_f * 1000).to_i
|
|
24
|
+
if ts < @last_timestamp
|
|
25
|
+
raise 'Clock moved backwards.'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if ts == @last_timestamp
|
|
29
|
+
@sequence = (@sequence + 1) & @sequence_mask
|
|
30
|
+
if (@sequence == 0)
|
|
31
|
+
ts = til_next_millis(@last_timestamp)
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
@sequence = 0
|
|
35
|
+
end
|
|
36
|
+
@last_timestamp = ts
|
|
37
|
+
|
|
38
|
+
((ts - @id_epoch) << @timestamp_left_shift) | (@data_center_id << @data_center_id_shift) | (@worker_id << @worker_id_shift) | @sequence
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def uuid
|
|
42
|
+
require 'securerandom'
|
|
43
|
+
SecureRandom.uuid.gsub('-', '')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def til_next_millis(last_timestamp)
|
|
49
|
+
ts = (Time.now.to_f * 1000).to_i
|
|
50
|
+
while ts <= last_timestamp
|
|
51
|
+
ts = (Time.now.to_f * 1000).to_i
|
|
52
|
+
end
|
|
53
|
+
ts
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
::ID = Prize::ID
|
data/lib/prize/repl.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'pry'
|
|
2
|
+
require 'pry-byebug'
|
|
3
|
+
require 'prize/commands'
|
|
4
|
+
require 'rainbow'
|
|
5
|
+
|
|
6
|
+
module Prize
|
|
7
|
+
class Repl
|
|
8
|
+
def initialize
|
|
9
|
+
@redis = App.redis
|
|
10
|
+
Pry.config.prompt = Pry::Prompt.new("", "", prompt)
|
|
11
|
+
Pry.start(@redis)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def prompt
|
|
15
|
+
opts = @redis.instance_variable_get('@client').options
|
|
16
|
+
if App.options.ssh_host
|
|
17
|
+
host = "#{App.options.ssh_host}:#{App.options.host || '127.0.0.1'}:#{App.options.port || 6379}/#{opts[:db]}"
|
|
18
|
+
else
|
|
19
|
+
host = opts[:url] || opts[:path] || "#{opts[:host]}:#{opts[:port]}/#{opts[:db]}"
|
|
20
|
+
end
|
|
21
|
+
[proc do |obj, nest_level, _|
|
|
22
|
+
if obj == @redis && nest_level == 0
|
|
23
|
+
nest_level_prompt = ''
|
|
24
|
+
else
|
|
25
|
+
nest_level_prompt = "(#{obj}:#{nest_level})"
|
|
26
|
+
end
|
|
27
|
+
"%s#{Rainbow('@').green}%s#{nest_level_prompt} %s " % [Rainbow('PRIZE').red, Rainbow(host).yellow, Rainbow('❯').green]
|
|
28
|
+
end]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'net/ssh/gateway'
|
|
2
|
+
require 'prize/ssh_proxy_patch'
|
|
3
|
+
|
|
4
|
+
module Prize
|
|
5
|
+
class SSHProxy
|
|
6
|
+
class << self
|
|
7
|
+
|
|
8
|
+
attr_accessor :config, :ssh_gateway, :local_ssh_proxy_port
|
|
9
|
+
|
|
10
|
+
def connect(config)
|
|
11
|
+
@config = config
|
|
12
|
+
@ssh_gateway = Net::SSH::Gateway.new(config[:host], config[:user], config.slice(:port, :password).symbolize_keys.merge(keepalive: true, keepalive_interval: 30, loop_wait: 1))
|
|
13
|
+
@local_ssh_proxy_port = @ssh_gateway.open(config[:forward_host], config[:forward_port], config[:local_port])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def reconnect
|
|
17
|
+
reconnect! unless @ssh_gateway.active?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reconnect!
|
|
21
|
+
@ssh_gateway.shutdown!
|
|
22
|
+
@ssh_gateway = Net::SSH::Gateway.new(@config[:host], @config[:user], @config.slice(:port, :password).symbolize_keys)
|
|
23
|
+
@ssh_gateway.open(config[:forward_host], config[:forward_port], @local_ssh_proxy_port)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def active?
|
|
27
|
+
@ssh_gateway.active?
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'net/ssh/proxy/command'
|
|
2
|
+
|
|
3
|
+
module Net
|
|
4
|
+
module SSH
|
|
5
|
+
module Proxy
|
|
6
|
+
class Command
|
|
7
|
+
|
|
8
|
+
# Return a new socket connected to the given host and port via the
|
|
9
|
+
# proxy that was requested when the socket factory was instantiated.
|
|
10
|
+
def open(host, port, connection_options = nil)
|
|
11
|
+
command_line = @command_line_template.gsub(/%(.)/) {
|
|
12
|
+
case $1
|
|
13
|
+
when 'h'
|
|
14
|
+
host
|
|
15
|
+
when 'p'
|
|
16
|
+
port.to_s
|
|
17
|
+
when 'r'
|
|
18
|
+
remote_user = connection_options && connection_options[:remote_user]
|
|
19
|
+
if remote_user
|
|
20
|
+
remote_user
|
|
21
|
+
else
|
|
22
|
+
raise ArgumentError, "remote user name not available"
|
|
23
|
+
end
|
|
24
|
+
when '%'
|
|
25
|
+
'%'
|
|
26
|
+
else
|
|
27
|
+
raise ArgumentError, "unknown key: #{$1}"
|
|
28
|
+
end
|
|
29
|
+
}
|
|
30
|
+
command_line = '%s %s' % [PrizeSetsidWrrapper, command_line]
|
|
31
|
+
begin
|
|
32
|
+
io = IO.popen(command_line, "r+")
|
|
33
|
+
begin
|
|
34
|
+
if result = IO.select([io], nil, [io], @timeout)
|
|
35
|
+
if result.last.any? || io.eof?
|
|
36
|
+
raise "command failed"
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
raise "command timed out"
|
|
40
|
+
end
|
|
41
|
+
rescue StandardError
|
|
42
|
+
close_on_error(io)
|
|
43
|
+
raise
|
|
44
|
+
end
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
raise ConnectError, "#{e}: #{command_line}"
|
|
47
|
+
end
|
|
48
|
+
@command_line = command_line
|
|
49
|
+
if Gem.win_platform?
|
|
50
|
+
# read_nonblock and write_nonblock are not available on Windows
|
|
51
|
+
# pipe. Use sysread and syswrite as a replacement works.
|
|
52
|
+
def io.send(data, flag)
|
|
53
|
+
syswrite(data)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def io.recv(size)
|
|
57
|
+
sysread(size)
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
def io.send(data, flag)
|
|
61
|
+
begin
|
|
62
|
+
result = write_nonblock(data)
|
|
63
|
+
rescue IO::WaitWritable, Errno::EINTR
|
|
64
|
+
IO.select(nil, [self])
|
|
65
|
+
retry
|
|
66
|
+
end
|
|
67
|
+
result
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def io.recv(size)
|
|
71
|
+
begin
|
|
72
|
+
result = read_nonblock(size)
|
|
73
|
+
rescue IO::WaitReadable, Errno::EINTR
|
|
74
|
+
timeout_in_seconds = 20
|
|
75
|
+
if IO.select([self], nil, [self], timeout_in_seconds) == nil
|
|
76
|
+
raise "Unexpected spurious read wakeup"
|
|
77
|
+
end
|
|
78
|
+
retry
|
|
79
|
+
end
|
|
80
|
+
result
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
io
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
data/lib/prize/version.rb
CHANGED
data/lib/prize.rb
CHANGED
data/prize.gemspec
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require "prize/version"
|
|
1
|
+
require_relative 'lib/prize/version'
|
|
5
2
|
|
|
6
3
|
Gem::Specification.new do |spec|
|
|
7
4
|
spec.name = "prize"
|
|
@@ -13,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
|
13
10
|
spec.description = %{Simple Redis CLI client with pry loaded}
|
|
14
11
|
spec.homepage = "https://github.com/lululau/prize"
|
|
15
12
|
spec.license = "MIT"
|
|
16
|
-
|
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
|
17
14
|
|
|
18
15
|
# Specify which files should be added to the gem when it is released.
|
|
19
16
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
@@ -24,13 +21,13 @@ Gem::Specification.new do |spec|
|
|
|
24
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
25
22
|
spec.require_paths = ["lib"]
|
|
26
23
|
|
|
27
|
-
spec.add_dependency 'activesupport'
|
|
28
|
-
spec.add_dependency '
|
|
29
|
-
spec.add_dependency '
|
|
24
|
+
spec.add_dependency 'activesupport', '~> 6.0.3'
|
|
25
|
+
spec.add_dependency 'net-ssh-gateway', '~> 2.0.0'
|
|
26
|
+
spec.add_dependency 'pry', '~> 0.13.1'
|
|
27
|
+
spec.add_dependency 'pry-byebug', '~> 3.9.0'
|
|
28
|
+
spec.add_dependency 'pry-doc', '>= 1.0.0'
|
|
29
|
+
spec.add_dependency 'rainbow', '~> 3.0.0'
|
|
30
|
+
spec.add_dependency 'redis', '~> 4.0.0'
|
|
31
|
+
spec.add_dependency 'hiredis', '~> 0.6.0'
|
|
30
32
|
spec.add_dependency 'thor', '~> 0.20.0'
|
|
31
|
-
spec.add_dependency 'pry', '>= 0.12.0'
|
|
32
|
-
|
|
33
|
-
spec.add_development_dependency "bundler", "~> 1.17"
|
|
34
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
|
35
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
|
36
33
|
end
|
metadata
CHANGED
|
@@ -1,138 +1,151 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: prize
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Liu Xiang
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-01-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 6.0.3
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 6.0.3
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: net-ssh-gateway
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 2.0.0
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 2.0.0
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
42
|
+
name: pry
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.
|
|
47
|
+
version: 0.13.1
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.
|
|
54
|
+
version: 0.13.1
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
56
|
+
name: pry-byebug
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version:
|
|
61
|
+
version: 3.9.0
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
68
|
+
version: 3.9.0
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: pry
|
|
70
|
+
name: pry-doc
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - ">="
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 0.
|
|
75
|
+
version: 1.0.0
|
|
76
76
|
type: :runtime
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 0.
|
|
82
|
+
version: 1.0.0
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
|
-
name:
|
|
84
|
+
name: rainbow
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
90
|
-
type: :
|
|
89
|
+
version: 3.0.0
|
|
90
|
+
type: :runtime
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
96
|
+
version: 3.0.0
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
|
-
name:
|
|
98
|
+
name: redis
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
101
|
- - "~>"
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version:
|
|
104
|
-
type: :
|
|
103
|
+
version: 4.0.0
|
|
104
|
+
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
108
|
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version:
|
|
110
|
+
version: 4.0.0
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
|
-
name:
|
|
112
|
+
name: hiredis
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
114
114
|
requirements:
|
|
115
115
|
- - "~>"
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version:
|
|
118
|
-
type: :
|
|
117
|
+
version: 0.6.0
|
|
118
|
+
type: :runtime
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
121
|
requirements:
|
|
122
122
|
- - "~>"
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
|
-
version:
|
|
124
|
+
version: 0.6.0
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: thor
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: 0.20.0
|
|
132
|
+
type: :runtime
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: 0.20.0
|
|
125
139
|
description: Simple Redis CLI client with pry loaded
|
|
126
140
|
email:
|
|
127
141
|
- liuxiang921@gmail.com
|
|
128
142
|
executables:
|
|
129
143
|
- prize
|
|
144
|
+
- prize_setsid_wrapper
|
|
130
145
|
extensions: []
|
|
131
146
|
extra_rdoc_files: []
|
|
132
147
|
files:
|
|
133
148
|
- ".gitignore"
|
|
134
|
-
- ".rspec"
|
|
135
|
-
- ".travis.yml"
|
|
136
149
|
- CODE_OF_CONDUCT.md
|
|
137
150
|
- Gemfile
|
|
138
151
|
- Gemfile.lock
|
|
@@ -142,15 +155,28 @@ files:
|
|
|
142
155
|
- bin/console
|
|
143
156
|
- bin/setup
|
|
144
157
|
- exe/prize
|
|
158
|
+
- exe/prize_setsid_wrapper
|
|
145
159
|
- lib/prize.rb
|
|
160
|
+
- lib/prize/app.rb
|
|
146
161
|
- lib/prize/cli.rb
|
|
162
|
+
- lib/prize/commands.rb
|
|
163
|
+
- lib/prize/commands/info.rb
|
|
164
|
+
- lib/prize/commands/reconnect.rb
|
|
165
|
+
- lib/prize/ext.rb
|
|
166
|
+
- lib/prize/ext/object.rb
|
|
167
|
+
- lib/prize/ext/string.rb
|
|
168
|
+
- lib/prize/ext/time.rb
|
|
169
|
+
- lib/prize/id.rb
|
|
170
|
+
- lib/prize/repl.rb
|
|
171
|
+
- lib/prize/ssh_proxy.rb
|
|
172
|
+
- lib/prize/ssh_proxy_patch.rb
|
|
147
173
|
- lib/prize/version.rb
|
|
148
174
|
- prize.gemspec
|
|
149
175
|
homepage: https://github.com/lululau/prize
|
|
150
176
|
licenses:
|
|
151
177
|
- MIT
|
|
152
178
|
metadata: {}
|
|
153
|
-
post_install_message:
|
|
179
|
+
post_install_message:
|
|
154
180
|
rdoc_options: []
|
|
155
181
|
require_paths:
|
|
156
182
|
- lib
|
|
@@ -158,15 +184,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
158
184
|
requirements:
|
|
159
185
|
- - ">="
|
|
160
186
|
- !ruby/object:Gem::Version
|
|
161
|
-
version:
|
|
187
|
+
version: 2.3.0
|
|
162
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
189
|
requirements:
|
|
164
190
|
- - ">="
|
|
165
191
|
- !ruby/object:Gem::Version
|
|
166
192
|
version: '0'
|
|
167
193
|
requirements: []
|
|
168
|
-
rubygems_version: 3.
|
|
169
|
-
signing_key:
|
|
194
|
+
rubygems_version: 3.3.3
|
|
195
|
+
signing_key:
|
|
170
196
|
specification_version: 4
|
|
171
197
|
summary: Simple Redis CLI client with pry loaded
|
|
172
198
|
test_files: []
|
data/.rspec
DELETED