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