arql 0.1.30
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 +7 -0
- data/.gitignore +8 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +72 -0
- data/LICENSE.txt +21 -0
- data/README.md +456 -0
- data/Rakefile +2 -0
- data/arql.gemspec +40 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/arql +7 -0
- data/exe/arql_setsid_wrapper +5 -0
- data/lib/arql.rb +12 -0
- data/lib/arql/app.rb +137 -0
- data/lib/arql/cli.rb +141 -0
- data/lib/arql/commands.rb +9 -0
- data/lib/arql/commands/info.rb +52 -0
- data/lib/arql/commands/models.rb +40 -0
- data/lib/arql/commands/reconnect.rb +23 -0
- data/lib/arql/commands/redefine.rb +15 -0
- data/lib/arql/commands/show_sql.rb +25 -0
- data/lib/arql/commands/table.rb +55 -0
- data/lib/arql/connection.rb +10 -0
- data/lib/arql/definition.rb +180 -0
- data/lib/arql/ext.rb +5 -0
- data/lib/arql/ext/array.rb +65 -0
- data/lib/arql/ext/kernel.rb +5 -0
- data/lib/arql/ext/object.rb +18 -0
- data/lib/arql/ext/string.rb +5 -0
- data/lib/arql/ext/time.rb +15 -0
- data/lib/arql/id.rb +59 -0
- data/lib/arql/multi_io.rb +28 -0
- data/lib/arql/repl.rb +38 -0
- data/lib/arql/ssh_proxy.rb +31 -0
- data/lib/arql/ssh_proxy_patch.rb +88 -0
- data/lib/arql/version.rb +3 -0
- metadata +235 -0
data/Rakefile
ADDED
data/arql.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'lib/arql/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "arql"
|
5
|
+
spec.version = Arql::VERSION
|
6
|
+
spec.authors = ["Liu Xiang"]
|
7
|
+
spec.email = ["liuxiang921@gmail.com"]
|
8
|
+
|
9
|
+
spec.summary = %{Rails ActiveRecord + Pry is the best SQL query editor}
|
10
|
+
spec.description = %{Use ActiveRecord and Pry as your favorite SQL query editor.}
|
11
|
+
spec.homepage = "https://github.com/lululau/arql"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency 'mysql2', '~> 0.5.3'
|
27
|
+
# spec.add_dependency 'pg', '>= 0.18', '< 2.0'
|
28
|
+
spec.add_dependency 'sqlite3', '~> 1.4'
|
29
|
+
# spec.add_dependency 'activerecord-sqlserver-adapter'
|
30
|
+
# spec.add_dependency 'activerecord-oracle_enhanced-adapter'
|
31
|
+
spec.add_dependency 'activerecord', '~> 6.0.3'
|
32
|
+
spec.add_dependency 'activesupport', '~> 6.0.3'
|
33
|
+
spec.add_dependency 'net-ssh-gateway', '~> 2.0.0'
|
34
|
+
spec.add_dependency 'pry', '~> 0.13.1'
|
35
|
+
spec.add_dependency 'pry-byebug', '~> 3.9.0'
|
36
|
+
spec.add_dependency 'pry-doc', '~> 1.1.0'
|
37
|
+
spec.add_dependency 'rainbow', '~> 3.0.0'
|
38
|
+
spec.add_dependency 'terminal-table', '~> 1.8.0'
|
39
|
+
spec.add_dependency 'table_print', '~> 1.5.6'
|
40
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "arql"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/arql
ADDED
data/lib/arql.rb
ADDED
data/lib/arql/app.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'net/ssh/gateway'
|
2
|
+
|
3
|
+
module Arql
|
4
|
+
class App
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :log_io, :env
|
8
|
+
|
9
|
+
def config
|
10
|
+
@@effective_config
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(options)
|
15
|
+
require 'active_support/all'
|
16
|
+
require 'active_record'
|
17
|
+
require "arql/connection"
|
18
|
+
require "arql/definition"
|
19
|
+
@options = options
|
20
|
+
App.env = @options.env
|
21
|
+
Connection.open(connect_options)
|
22
|
+
@definition = Definition.new(effective_config)
|
23
|
+
load_initializer!
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect_options
|
27
|
+
connect_conf = effective_config.slice(:adapter, :host, :username,
|
28
|
+
:password, :database, :encoding,
|
29
|
+
:pool, :port, :socket)
|
30
|
+
if effective_config[:ssh].present?
|
31
|
+
connect_conf.merge!(start_ssh_proxy!)
|
32
|
+
end
|
33
|
+
|
34
|
+
connect_conf
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_initializer!
|
38
|
+
return unless effective_config[:initializer]
|
39
|
+
initializer_file = File.expand_path(effective_config[:initializer])
|
40
|
+
unless File.exists?(initializer_file)
|
41
|
+
STDERR.puts "Specified initializer file not found, #{effective_config[:initializer]}"
|
42
|
+
exit(1)
|
43
|
+
end
|
44
|
+
load(initializer_file)
|
45
|
+
end
|
46
|
+
|
47
|
+
def start_ssh_proxy!
|
48
|
+
ssh_config = effective_config[:ssh]
|
49
|
+
local_ssh_proxy_port = Arql::SSHProxy.connect(ssh_config.slice(:host, :user, :port, :password).merge(
|
50
|
+
forward_host: effective_config[:host],
|
51
|
+
forward_port: effective_config[:port],
|
52
|
+
local_port: ssh_config[:local_port]))
|
53
|
+
{
|
54
|
+
host: '127.0.0.1',
|
55
|
+
port: local_ssh_proxy_port
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def config
|
60
|
+
@config ||= YAML.load(IO.read(File.expand_path(@options.config_file))).with_indifferent_access
|
61
|
+
end
|
62
|
+
|
63
|
+
def selected_config
|
64
|
+
if @options.env.present? && !config[@options.env].present?
|
65
|
+
STDERR.puts "Specified ENV `#{@options.env}' not exists"
|
66
|
+
end
|
67
|
+
if env = @options.env
|
68
|
+
config[env]
|
69
|
+
else
|
70
|
+
{}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def effective_config
|
75
|
+
@@effective_config ||= nil
|
76
|
+
unless @@effective_config
|
77
|
+
@@effective_config = selected_config.deep_merge(@options.to_h)
|
78
|
+
if @@effective_config[:adapter].blank?
|
79
|
+
@@effective_config[:adapter] = 'sqlite3'
|
80
|
+
end
|
81
|
+
@@effective_config[:database] = File.expand_path(@@effective_config[:database]) if @@effective_config[:adapter] == 'sqlite3'
|
82
|
+
end
|
83
|
+
@@effective_config
|
84
|
+
end
|
85
|
+
|
86
|
+
def run!
|
87
|
+
show_sql if should_show_sql?
|
88
|
+
write_sql if should_write_sql?
|
89
|
+
append_sql if should_append_sql?
|
90
|
+
if effective_config[:code].present?
|
91
|
+
eval(effective_config[:code])
|
92
|
+
elsif effective_config[:args].present?
|
93
|
+
effective_config[:args].each { |rb| load(rb) }
|
94
|
+
elsif STDIN.isatty
|
95
|
+
run_repl!
|
96
|
+
else
|
97
|
+
eval(STDIN.read)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def run_repl!
|
102
|
+
Repl.new
|
103
|
+
end
|
104
|
+
|
105
|
+
def should_show_sql?
|
106
|
+
effective_config[:show_sql]
|
107
|
+
end
|
108
|
+
|
109
|
+
def should_write_sql?
|
110
|
+
effective_config[:write_sql]
|
111
|
+
end
|
112
|
+
|
113
|
+
def should_append_sql?
|
114
|
+
effective_config[:append_sql]
|
115
|
+
end
|
116
|
+
|
117
|
+
def show_sql
|
118
|
+
App.log_io ||= MultiIO.new
|
119
|
+
ActiveRecord::Base.logger = Logger.new(App.log_io)
|
120
|
+
App.log_io << STDOUT
|
121
|
+
end
|
122
|
+
|
123
|
+
def write_sql
|
124
|
+
write_sql_file = effective_config[:write_sql]
|
125
|
+
App.log_io ||= MultiIO.new
|
126
|
+
ActiveRecord::Base.logger = Logger.new(App.log_io)
|
127
|
+
App.log_io << File.new(write_sql_file, 'w')
|
128
|
+
end
|
129
|
+
|
130
|
+
def append_sql
|
131
|
+
write_sql_file = effective_config[:append_sql]
|
132
|
+
App.log_io ||= MultiIO.new
|
133
|
+
ActiveRecord::Base.logger = Logger.new(App.log_io)
|
134
|
+
App.log_io << File.new(write_sql_file, 'a')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/lib/arql/cli.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Arql
|
5
|
+
class Cli
|
6
|
+
class << self
|
7
|
+
def start
|
8
|
+
parse_options!
|
9
|
+
App.new(@options).run!
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_options!
|
13
|
+
@options = OpenStruct.new(config_file: default_config_file,
|
14
|
+
initializer: default_initializer,
|
15
|
+
ssh: {})
|
16
|
+
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = <<~EOF
|
20
|
+
Usage: arql [options] [ruby file]
|
21
|
+
|
22
|
+
If neither [ruby file] nor -e option specified, and STDIN is not a tty, a Pry REPL will be launched,
|
23
|
+
otherwise the specified ruby file or -e option value or ruby code read from STDIN will be run, and no REPL launched
|
24
|
+
|
25
|
+
EOF
|
26
|
+
|
27
|
+
opts.on('-cCONFIG_FILE', '--conf=CONFIG_FILE', 'Specify config file, default is $HOME/.arql.yml, or $HOME/.arql.d/init.yml.') do |config_file|
|
28
|
+
@options.config_file = config_file
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('-iINITIALIZER', '--initializer=INITIALIZER', 'Specify initializer ruby file, default is $HOME/.arql.rb, or $HOME/.arql.d/init.rb.') do |initializer|
|
32
|
+
@options.initializer = initializer
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on('-eENVIRON', '--env=ENVIRON', 'Specify config environment.') do |env|
|
36
|
+
@options.env = env
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on('-aDB_ADAPTER', '--db-adapter=DB_ADAPTER', 'Specify database Adapter, default is mysql2') do |db_adapter|
|
40
|
+
@options.dapter = db_adapter
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-hDB_HOST', '--db-host=DB_HOST', 'Specify database host') do |db_host|
|
44
|
+
@options.host = db_host
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-pDB_PORT', '--db-port=DB_PORT', 'Specify database port') do |db_port|
|
48
|
+
@options.port = db_port.to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-dDB_NAME', '--db-name=DB_NAME', 'Specify database name') do |db_name|
|
52
|
+
@options.database = db_name
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-uDB_USER', '--db-user=DB_USER', 'Specify database user') do |db_user|
|
56
|
+
@options.username = db_user
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('-PDB_PASSWORD', '--db-password=DB_PASSWORD', 'Specify database password') do |db_password|
|
60
|
+
@options.password = db_password
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('-n', '--db-encoding=DB_ENCODING', 'Specify database encoding, default is utf8') do |db_encoding|
|
64
|
+
@options.encoding = db_encoding
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on('-o', '--db-pool=DB_POOL', 'Specify database pool size, default is 5') do |db_pool|
|
68
|
+
@options.pool = db_pool
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on('-HSSH_HOST', '--ssh-host=SSH_HOST', 'Specify SSH host') do |ssh_host|
|
72
|
+
@options.ssh[:host] = ssh_host
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on('-OSSH_PORT', '--ssh-port=SSH_PORT', 'Specify SSH port') do |ssh_port|
|
76
|
+
@options.ssh[:port] = ssh_port.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
opts.on('-USSH_USER', '--ssh-user=SSH_USER', 'Specify SSH user') do |ssh_user|
|
80
|
+
@options.ssh[:user] = ssh_user
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.on('-WSSH_PASSWORD', '--ssh-password=SSH_PASSWORD', 'Specify SSH password') do |ssh_password|
|
84
|
+
@options.ssh[:password] = ssh_password
|
85
|
+
end
|
86
|
+
|
87
|
+
opts.on('-LSSH_LOCAL_PORT', '--ssh-local-port=SSH_LOCAL_PORT', 'Specify local SSH proxy port') do |local_port|
|
88
|
+
@options.ssh[:local_port] = local_port.to_i
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on('-ECODE', '--eval=CODE', 'evaluate CODE') do |code|
|
92
|
+
@options.code = code
|
93
|
+
end
|
94
|
+
|
95
|
+
opts.on('-S', '--show-sql', 'Show SQL on STDOUT') do
|
96
|
+
@options.show_sql = true
|
97
|
+
end
|
98
|
+
|
99
|
+
opts.on('-wOUTOUT', '--write-sql=OUTPUT', 'Write SQL to OUTPUT file') do |file|
|
100
|
+
@options.write_sql = file
|
101
|
+
end
|
102
|
+
|
103
|
+
opts.on('-AOUTPUT', '--append-sql=OUTPUT', 'Append SQL to OUTPUT file') do |file|
|
104
|
+
@options.append_sql = file
|
105
|
+
end
|
106
|
+
|
107
|
+
opts.on('-V', '--version', 'Prints version') do
|
108
|
+
puts "ARQL #{Arql::VERSION}"
|
109
|
+
exit
|
110
|
+
end
|
111
|
+
|
112
|
+
opts.on('', '--help', 'Prints this help') do
|
113
|
+
puts opts
|
114
|
+
exit
|
115
|
+
end
|
116
|
+
|
117
|
+
end.parse!
|
118
|
+
|
119
|
+
@options.args = ARGV
|
120
|
+
end
|
121
|
+
|
122
|
+
def default_config_file
|
123
|
+
conf = File.expand_path('~/.arql.yml')
|
124
|
+
return conf if File.file?(conf)
|
125
|
+
conf = File.expand_path('~/.arql.yaml')
|
126
|
+
return conf if File.file?(conf)
|
127
|
+
conf = File.expand_path('~/.arql.d/init.yml')
|
128
|
+
return conf if File.file?(conf)
|
129
|
+
conf = File.expand_path('~/.arql.d/init.yaml')
|
130
|
+
return conf if File.file?(conf)
|
131
|
+
end
|
132
|
+
|
133
|
+
def default_initializer
|
134
|
+
conf = File.expand_path('~/.arql.rb')
|
135
|
+
return conf if File.file?(conf)
|
136
|
+
conf = File.expand_path('~/.arql.d/init.rb')
|
137
|
+
return conf if File.file?(conf)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rainbow'
|
2
|
+
|
3
|
+
module Arql::Commands
|
4
|
+
module Info
|
5
|
+
class << self
|
6
|
+
def db_info
|
7
|
+
<<~EOF
|
8
|
+
|
9
|
+
Database Connection Information:
|
10
|
+
|
11
|
+
Active: #{color_boolean(ActiveRecord::Base.connection.active?)}
|
12
|
+
Host: #{Arql::App.config[:host]}
|
13
|
+
Port: #{Arql::App.config[:port]}
|
14
|
+
Username: #{Arql::App.config[:username]}
|
15
|
+
Password: #{(Arql::App.config[:password] || '').gsub(/./, '*')}
|
16
|
+
Database: #{Arql::App.config[:database]}
|
17
|
+
Adapter: #{Arql::App.config[:adapter]}
|
18
|
+
Encoding: #{Arql::App.config[:encoding]}
|
19
|
+
Pool Size: #{Arql::App.config[:pool]}
|
20
|
+
EOF
|
21
|
+
end
|
22
|
+
|
23
|
+
def ssh_info
|
24
|
+
<<~EOF
|
25
|
+
|
26
|
+
SSH Connection Information:
|
27
|
+
|
28
|
+
Active: #{color_boolean(Arql::SSHProxy.active?)}
|
29
|
+
Host: #{Arql::App.config[:ssh][:host]}
|
30
|
+
Port: #{Arql::App.config[:ssh][:port]}
|
31
|
+
Username: #{Arql::App.config[:ssh][:user]}
|
32
|
+
Password: #{(Arql::App.config[:ssh][:password] || '').gsub(/./, '*')}
|
33
|
+
Local Port: #{Arql::SSHProxy.local_ssh_proxy_port}
|
34
|
+
EOF
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def color_boolean(bool)
|
39
|
+
if bool
|
40
|
+
Rainbow('TRUE').green
|
41
|
+
else
|
42
|
+
Rainbow('FALSE').red
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Pry.commands.block_command 'info' do
|
48
|
+
puts Info::db_info
|
49
|
+
puts Info::ssh_info if Arql::App.config[:ssh].present?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'terminal-table'
|
2
|
+
|
3
|
+
module Arql::Commands
|
4
|
+
module Models
|
5
|
+
class << self
|
6
|
+
def models
|
7
|
+
t = []
|
8
|
+
t << ['Table Name', 'Model Class', 'Abbr']
|
9
|
+
t << nil
|
10
|
+
Arql::Definition.models.each do |definition|
|
11
|
+
t << [definition[:table], definition[:model].name, definition[:abbr] || '']
|
12
|
+
end
|
13
|
+
t
|
14
|
+
end
|
15
|
+
|
16
|
+
def models_table
|
17
|
+
Terminal::Table.new do |t|
|
18
|
+
models.each { |row| t << (row || :separator) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Pry.commands.block_command 'm' do
|
25
|
+
puts
|
26
|
+
puts Models::models_table
|
27
|
+
end
|
28
|
+
|
29
|
+
Pry.commands.alias_command 'l', 'm'
|
30
|
+
end
|
31
|
+
|
32
|
+
module Kernel
|
33
|
+
def models
|
34
|
+
Arql::Commands::Models::models
|
35
|
+
end
|
36
|
+
|
37
|
+
def tables
|
38
|
+
models
|
39
|
+
end
|
40
|
+
end
|