prize 0.2.1 → 0.3.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/.gitignore +0 -3
- data/CODE_OF_CONDUCT.md +3 -3
- data/Gemfile +1 -1
- data/Gemfile.lock +15 -1
- data/LICENSE.txt +1 -1
- data/exe/prize +3 -1
- data/exe/prize_setsid_wrapper +5 -0
- data/lib/prize.rb +8 -0
- data/lib/prize/app.rb +72 -0
- data/lib/prize/cli.rb +92 -44
- data/lib/prize/commands.rb +5 -0
- data/lib/prize/commands/info.rb +48 -0
- data/lib/prize/commands/reconnect.rb +23 -0
- data/lib/prize/ext.rb +3 -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/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/prize.gemspec +6 -4
- metadata +70 -16
- 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: e5d5d94d02356ee18c3dad96ac30d013a3cc20e26d0aa64614bf11935c927524
|
4
|
+
data.tar.gz: abccbe946f44b040a02c85df0b5a1c9decceabed42455f6953bba5d5304afbf7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d4dc34770bec250fa5fbf281ca6c6512585b2a7dc938e63a495e9ca6724a2bfdd8c6db5cda05f677add58255319d25c7aadc9536fe491b1f4ed261696dbc073
|
7
|
+
data.tar.gz: 81d5e92ae7c568501f31e8f7ffb62d0110252e1b8a6c5e37ef51141509be3d527b692130f0a7d22089af6ecc7ff9fd1fed92bc07dc0fc0ebd4c5cb2e43d82f3e
|
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,10 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
prize (0.
|
4
|
+
prize (0.3.0)
|
5
5
|
activesupport (~> 6.0.3)
|
6
6
|
hiredis (~> 0.6.0)
|
7
|
+
net-ssh-gateway (~> 2.0.0)
|
7
8
|
pry (~> 0.13.1)
|
9
|
+
pry-byebug (~> 3.9.0)
|
10
|
+
pry-doc (~> 1.1.0)
|
8
11
|
rainbow (~> 3.0.0)
|
9
12
|
redis (~> 4.0.0)
|
10
13
|
thor (~> 0.20.0)
|
@@ -18,6 +21,7 @@ GEM
|
|
18
21
|
minitest (~> 5.1)
|
19
22
|
tzinfo (~> 1.1)
|
20
23
|
zeitwerk (~> 2.2, >= 2.2.2)
|
24
|
+
byebug (11.1.3)
|
21
25
|
coderay (1.1.3)
|
22
26
|
concurrent-ruby (1.1.7)
|
23
27
|
hiredis (0.6.3)
|
@@ -25,9 +29,18 @@ GEM
|
|
25
29
|
concurrent-ruby (~> 1.0)
|
26
30
|
method_source (1.0.0)
|
27
31
|
minitest (5.14.1)
|
32
|
+
net-ssh (6.1.0)
|
33
|
+
net-ssh-gateway (2.0.0)
|
34
|
+
net-ssh (>= 4.0.0)
|
28
35
|
pry (0.13.1)
|
29
36
|
coderay (~> 1.1)
|
30
37
|
method_source (~> 1.0)
|
38
|
+
pry-byebug (3.9.0)
|
39
|
+
byebug (~> 11.0)
|
40
|
+
pry (~> 0.13.0)
|
41
|
+
pry-doc (1.1.0)
|
42
|
+
pry (~> 0.11)
|
43
|
+
yard (~> 0.9.11)
|
31
44
|
rainbow (3.0.0)
|
32
45
|
rake (12.3.3)
|
33
46
|
redis (4.0.3)
|
@@ -35,6 +48,7 @@ GEM
|
|
35
48
|
thread_safe (0.3.6)
|
36
49
|
tzinfo (1.2.7)
|
37
50
|
thread_safe (~> 0.1)
|
51
|
+
yard (0.9.25)
|
38
52
|
zeitwerk (2.4.0)
|
39
53
|
|
40
54
|
PLATFORMS
|
data/LICENSE.txt
CHANGED
data/exe/prize
CHANGED
data/lib/prize.rb
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,52 +1,100 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'hiredis'
|
4
|
-
require 'pry'
|
5
|
-
require 'rainbow'
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
6
3
|
|
7
4
|
module Prize
|
8
|
-
class Cli
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
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.'
|
14
|
-
class_option :host, type: :string, aliases: ['-H'], required: false, desc: 'Server hostname (default: 127.0.0.1)'
|
15
|
-
class_option :port, type: :numeric, aliases: ['-p'], required: false, desc: 'Server port (default: 6379)'
|
16
|
-
class_option :path, type: :string, aliases: ['-s', '--socket'], required: false, desc: 'Server socket (overrides hostname and port)'
|
17
|
-
class_option :timeout, type: :numeric, required: false, desc: 'Timeout in seconds (default: 5.0)'
|
18
|
-
class_option :connect_timeout, type: :numeric, required: false, desc: 'Timeout for initial connect in seconds (default: same as timeout)'
|
19
|
-
class_option :password, type: :string, aliases: ['-a'], required: false, desc: 'Password to authenticate against server'
|
20
|
-
class_option :db, type: :numeric, aliases: ['-n'], required: false, desc: 'Database number (default: 0)'
|
21
|
-
class_option :replica, type: :boolean, required: false, desc: 'Whether to use readonly replica nodes in Redis Cluster or not'
|
22
|
-
class_option :cluster, type: :string, required: false, desc: 'List of cluster nodes to contact, format: URL1,URL2,URL3...'
|
23
|
-
def main
|
24
|
-
@redis = Redis.new(build_options)
|
25
|
-
Pry.config.prompt = Pry::Prompt.new("", "", prompt)
|
26
|
-
if File.exists?(File.expand_path('~/.prizerc'))
|
27
|
-
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!
|
28
10
|
end
|
29
|
-
Pry.start(@redis)
|
30
|
-
end
|
31
11
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
opts
|
37
|
-
|
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!
|
38
96
|
|
39
|
-
|
40
|
-
opts = @redis.instance_variable_get('@client').options
|
41
|
-
host = opts[:url] || opts[:path] || "#{opts[:host]}:#{opts[:port]}/#{opts[:db]}"
|
42
|
-
[proc do |obj, nest_level, _|
|
43
|
-
if obj == @redis && nest_level == 0
|
44
|
-
nest_level_prompt = ''
|
45
|
-
else
|
46
|
-
nest_level_prompt = "(#{obj}:#{nest_level})"
|
47
|
-
end
|
48
|
-
"%s#{Rainbow('@').green}%s#{nest_level_prompt} %s " % [Rainbow('PRIZE').red, Rainbow(host).yellow, Rainbow('❯').green]
|
49
|
-
end]
|
97
|
+
@options.args = ARGV
|
50
98
|
end
|
51
99
|
end
|
52
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
|
data/lib/prize/ext.rb
ADDED
@@ -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/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/prize.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative 'lib/prize/version'
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = "prize"
|
@@ -12,7 +12,6 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.license = "MIT"
|
13
13
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
14
|
|
15
|
-
|
16
15
|
# Specify which files should be added to the gem when it is released.
|
17
16
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
17
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
@@ -23,9 +22,12 @@ Gem::Specification.new do |spec|
|
|
23
22
|
spec.require_paths = ["lib"]
|
24
23
|
|
25
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.1.0'
|
29
|
+
spec.add_dependency 'rainbow', '~> 3.0.0'
|
26
30
|
spec.add_dependency 'redis', '~> 4.0.0'
|
27
31
|
spec.add_dependency 'hiredis', '~> 0.6.0'
|
28
32
|
spec.add_dependency 'thor', '~> 0.20.0'
|
29
|
-
spec.add_dependency 'pry', '~> 0.13.1'
|
30
|
-
spec.add_dependency 'rainbow', '~> 3.0.0'
|
31
33
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Liu Xiang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -25,61 +25,61 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
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:
|
75
|
+
version: 1.1.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:
|
82
|
+
version: 1.1.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rainbow
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,17 +94,58 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 3.0.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: redis
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 4.0.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 4.0.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: hiredis
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.6.0
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::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
|
97
139
|
description: Simple Redis CLI client with pry loaded
|
98
140
|
email:
|
99
141
|
- liuxiang921@gmail.com
|
100
142
|
executables:
|
101
143
|
- prize
|
144
|
+
- prize_setsid_wrapper
|
102
145
|
extensions: []
|
103
146
|
extra_rdoc_files: []
|
104
147
|
files:
|
105
148
|
- ".gitignore"
|
106
|
-
- ".rspec"
|
107
|
-
- ".travis.yml"
|
108
149
|
- CODE_OF_CONDUCT.md
|
109
150
|
- Gemfile
|
110
151
|
- Gemfile.lock
|
@@ -114,8 +155,21 @@ files:
|
|
114
155
|
- bin/console
|
115
156
|
- bin/setup
|
116
157
|
- exe/prize
|
158
|
+
- exe/prize_setsid_wrapper
|
117
159
|
- lib/prize.rb
|
160
|
+
- lib/prize/app.rb
|
118
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
|
119
173
|
- lib/prize/version.rb
|
120
174
|
- prize.gemspec
|
121
175
|
homepage: https://github.com/lululau/prize
|
data/.rspec
DELETED