arql 0.3.31 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.vscode/launch.json +1 -1
- data/Gemfile.lock +1 -1
- data/README-zh_CN.org +706 -429
- data/auto-set-id-before-save-zh_CN.org +1 -1
- data/custom-configurations-zh_CN.org +40 -3
- data/define-associations-zh_CN.org +31 -17
- data/initializer-structure-zh_CN.org +22 -4
- data/lib/arql/app.rb +98 -71
- data/lib/arql/cli.rb +31 -15
- data/lib/arql/commands/info.rb +41 -28
- data/lib/arql/commands/models.rb +106 -61
- data/lib/arql/commands/reconnect.rb +8 -4
- data/lib/arql/commands/redefine.rb +3 -1
- data/lib/arql/commands/sandbox.rb +6 -4
- data/lib/arql/commands.rb +0 -2
- data/lib/arql/concerns/global_data_definition.rb +40 -6
- data/lib/arql/concerns/model_extension.rb +168 -0
- data/lib/arql/concerns/table_data_definition.rb +20 -20
- data/lib/arql/concerns.rb +1 -0
- data/lib/arql/definition.rb +169 -317
- data/lib/arql/ext/active_record/relation.rb +29 -0
- data/lib/arql/ext/active_record/result.rb +29 -0
- data/lib/arql/ext/array.rb +40 -1
- data/lib/arql/ext/kernel.rb +70 -61
- data/lib/arql/ext/object.rb +14 -0
- data/lib/arql/ext/ransack/search.rb +29 -0
- data/lib/arql/mysqldump.rb +0 -1
- data/lib/arql/ssh_proxy.rb +25 -22
- data/lib/arql/version.rb +1 -1
- data/lib/arql.rb +11 -7
- data/sql-log-zh_CN.org +8 -3
- metadata +6 -5
- data/lib/arql/commands/table.rb +0 -55
- data/lib/arql/commands/vd.rb +0 -46
- data/lib/arql/connection.rb +0 -16
data/lib/arql/commands/models.rb
CHANGED
@@ -3,84 +3,129 @@ require 'terminal-table'
|
|
3
3
|
module Arql::Commands
|
4
4
|
module Models
|
5
5
|
class << self
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
if column_regexp.nil?
|
18
|
-
Terminal::Table.new do |t|
|
19
|
-
models.each_with_index { |row, idx| t << (row || :separator) if row.nil? ||
|
20
|
-
table_regexp.nil? ||
|
21
|
-
idx.zero? ||
|
22
|
-
row.any? { |e| e =~ table_regexp }
|
23
|
-
}
|
6
|
+
def filter_tables(env_name, definition, format, table_regexp=nil)
|
7
|
+
result = ''
|
8
|
+
result << '-- ' if format == 'sql'
|
9
|
+
result << "Environment: #{env_name}\n"
|
10
|
+
result << "------------------------------\n\n"
|
11
|
+
if format == 'sql'
|
12
|
+
definition.models.each do |model|
|
13
|
+
if table_regexp? || ( model[:table] =~ table_regexp || model[:comment] =~ table_regexp )
|
14
|
+
result << "-- Table: #{table_name}\n\n"
|
15
|
+
result << definition.connection.exec_query("show create table `#{table_name}`").rows.last.last + ';'
|
16
|
+
end
|
24
17
|
end
|
25
18
|
else
|
26
|
-
|
27
|
-
|
28
|
-
t << ['
|
19
|
+
Terminal::Table.new do |t|
|
20
|
+
t.style = table_style_for_format(format)
|
21
|
+
t << ['Table Name', 'Model Class', 'Abbr', 'Comment']
|
29
22
|
t << :separator
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
next if matched_columns.empty?
|
34
|
-
matched_columns.each do |column|
|
35
|
-
pk = if [connection.primary_key(definition[:table])].flatten.include?(column.name)
|
36
|
-
'Y'
|
37
|
-
else
|
38
|
-
''
|
39
|
-
end
|
40
|
-
t << [pk, definition[:table], model_class.name, column.name, column.sql_type,
|
41
|
-
column.sql_type_metadata.type, column.sql_type_metadata.limit || '',
|
42
|
-
column.sql_type_metadata.precision || '', column.sql_type_metadata.scale || '', column.default || '',
|
43
|
-
column.null, "#{definition[:comment]} - #{column.comment}"]
|
23
|
+
definition.models.each do |model|
|
24
|
+
if table_regexp.nil? || ( model[:table] =~ table_regexp || model[:comment] =~ table_regexp )
|
25
|
+
t << [model[:table], model[:model].name, model[:abbr] || '', model[:comment] || '']
|
44
26
|
end
|
45
27
|
end
|
28
|
+
end.try { |e|
|
29
|
+
case format
|
30
|
+
when 'md'
|
31
|
+
result << e.to_s.lines.map { |l| ' ' + l }.join
|
32
|
+
when 'org'
|
33
|
+
result << e.to_s.lines.map { |l| ' ' + l.gsub(/^\+|\+$/, '|') }.join
|
34
|
+
else
|
35
|
+
result << e.to_s
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
def filter_columns(env_name, definition, format, column_regexp=nil)
|
43
|
+
result = ''
|
44
|
+
result << '-- ' if format == 'sql'
|
45
|
+
result << "Environment: #{env_name}\n"
|
46
|
+
result << "------------------------------\n\n"
|
47
|
+
Terminal::Table.new do |t|
|
48
|
+
t.style = table_style_for_format(format)
|
49
|
+
t << ['Table', 'Model', 'Name', 'SQL Type', 'Ruby Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
|
50
|
+
t << :separator
|
51
|
+
definition.models.each do |model_def|
|
52
|
+
model_class = model_def[:model]
|
53
|
+
matched_columns = model_class.columns.select { |column| column.name =~ column_regexp || column.comment =~ column_regexp }
|
54
|
+
next if matched_columns.empty?
|
55
|
+
matched_columns.each do |column|
|
56
|
+
t << [model_def[:table], model_class.name, column.name, column.sql_type,
|
57
|
+
column.sql_type_metadata.type, column.sql_type_metadata.limit || '',
|
58
|
+
column.sql_type_metadata.precision || '', column.sql_type_metadata.scale || '', column.default || '',
|
59
|
+
column.null, "#{model_def[:comment]} - #{column.comment}"]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end.try { |e|
|
63
|
+
case format
|
64
|
+
when 'md'
|
65
|
+
result << e.to_s.lines.map { |l| ' ' + l }.join
|
66
|
+
when 'org'
|
67
|
+
result << e.to_s.lines.map { |l| ' ' + l.gsub(/^\+|\+$/, '|') }.join
|
68
|
+
else
|
69
|
+
result << e.to_s
|
46
70
|
end
|
47
|
-
|
71
|
+
}
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
def table_style_for_format(format)
|
76
|
+
case format
|
77
|
+
when 'md'
|
78
|
+
{
|
79
|
+
border_top: false,
|
80
|
+
border_bottom: false,
|
81
|
+
border_i: '|'
|
82
|
+
}
|
83
|
+
when 'org'
|
84
|
+
{
|
85
|
+
border_top: false,
|
86
|
+
border_bottom: false,
|
87
|
+
}
|
88
|
+
else
|
89
|
+
{}
|
48
90
|
end
|
49
91
|
end
|
50
92
|
end
|
51
93
|
end
|
52
94
|
|
53
|
-
Pry.commands.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
95
|
+
Pry.commands.create_command 'm' do
|
96
|
+
description 'List models or columns (specified by `-c`): m [-e env_name_regexp] -c [column_regexp] [table_regexp]'
|
97
|
+
|
98
|
+
def options(opt)
|
99
|
+
opt.on '-e', '--env', 'Environment name regexp', argument: true, as: String, required: false, default: nil
|
100
|
+
opt.on '-f', '--format', 'Table format, available: terminal(default), md, org, sql', argument: true, as: String, required: false, default: 'terminal'
|
101
|
+
opt.on '-c', '--column', 'Column name regexp', argument: true, as: String, required: false, default: nil
|
60
102
|
end
|
61
|
-
end
|
62
103
|
|
63
|
-
|
64
|
-
end
|
104
|
+
def process
|
65
105
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
106
|
+
if opts[:format] == 'sql' && opts[:column]
|
107
|
+
output.puts 'SQL format is not supported for column listing'
|
108
|
+
return
|
109
|
+
end
|
70
110
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
111
|
+
env_names = opts[:env].try {|e| [e]}.presence || Arql::App.environments
|
112
|
+
env_names = env_names.map { |e| e.start_with?('/') ? eval(e) : Regexp.new(e) }
|
74
113
|
|
75
|
-
|
76
|
-
|
77
|
-
end
|
114
|
+
Arql::App.instance.definitions.each do |env_name, definition|
|
115
|
+
next unless env_names.any? { |e| env_name =~ e }
|
78
116
|
|
79
|
-
|
80
|
-
|
81
|
-
|
117
|
+
output.puts
|
118
|
+
if opts[:column]
|
119
|
+
column_regexp = opts[:column]
|
120
|
+
output.puts Models::filter_columns(env_name, definition, opts[:format], column_regexp.try { |e| e.start_with?('/') ? eval(e) : Regexp.new(e) })
|
121
|
+
else
|
122
|
+
table_regexp = args&.first
|
123
|
+
output.puts Models::filter_tables(env_name, definition, opts[:format], table_regexp.try { |e| e.start_with?('/') ? eval(e) : Regexp.new(e) })
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
82
127
|
|
83
|
-
def model_names
|
84
|
-
models[2..-1].map(&:second)
|
85
128
|
end
|
129
|
+
|
130
|
+
Pry.commands.alias_command 'l', 'm'
|
86
131
|
end
|
@@ -2,13 +2,17 @@ module Arql::Commands
|
|
2
2
|
module Reconnect
|
3
3
|
class << self
|
4
4
|
def reconnect
|
5
|
-
Arql::
|
6
|
-
|
5
|
+
Arql::App.instance.definitions.each do |_, definition|
|
6
|
+
definition.ssh_proxy.reconnect if definition.options[:ssh].present?
|
7
|
+
definition.connection.reconnect! unless definition.connection.active?
|
8
|
+
end
|
7
9
|
end
|
8
10
|
|
9
11
|
def reconnect!
|
10
|
-
Arql::
|
11
|
-
|
12
|
+
Arql::App.instance.definitions.each do |_, definition|
|
13
|
+
definition.ssh_proxy.reconnect if definition.options[:ssh].present?
|
14
|
+
definition.connection.reconnect!
|
15
|
+
end
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
@@ -9,16 +9,18 @@ module Arql::Commands
|
|
9
9
|
|
10
10
|
def enter
|
11
11
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.set_callback(:checkout, :after, &@sandbox_callback)
|
12
|
-
|
12
|
+
Arql::App.instance.definitions.each do |_, definition|
|
13
|
+
definition.connection.begin_transaction(joinable: false)
|
14
|
+
end
|
13
15
|
@enabled = true
|
14
16
|
end
|
15
17
|
|
16
18
|
def quit
|
17
19
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.skip_callback(:checkout, :after, &@sandbox_callback)
|
20
|
+
Arql::App.instance.definitions.each do |_, definition|
|
21
|
+
definition.connection.rollback_transaction
|
22
|
+
end
|
18
23
|
@enabled = false
|
19
|
-
|
20
|
-
puts "begin_transaction callbacks removed."
|
21
|
-
puts "You still have open %d transactions open, don't forget commit or rollback them." % ActiveRecord::Base.connection.open_transactions if ActiveRecord::Base.connection.open_transactions > 0
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
data/lib/arql/commands.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'arql/commands/info'
|
2
2
|
require 'arql/commands/models'
|
3
|
-
require 'arql/commands/table'
|
4
3
|
require 'arql/commands/reconnect'
|
5
4
|
require 'arql/commands/redefine'
|
6
5
|
require 'arql/commands/show_sql'
|
7
6
|
require 'arql/commands/sandbox'
|
8
|
-
require 'arql/commands/vd'
|
9
7
|
|
10
8
|
module Arql::Commands
|
11
9
|
end
|
@@ -159,7 +159,14 @@ module Arql
|
|
159
159
|
#
|
160
160
|
# See also TableDefinition#column for details on how to create columns.
|
161
161
|
def create_table(table_name, **options, &blk)
|
162
|
-
|
162
|
+
env_name = options[:env]
|
163
|
+
unless env_name
|
164
|
+
raise ArgumentError, ':env option is required' if Arql::App.instance.environments.size > 1
|
165
|
+
|
166
|
+
env_name = Arql::App.instance.environments.first
|
167
|
+
end
|
168
|
+
options = options.except(:env)
|
169
|
+
Arql::App.instance.definitions[env_name].connection.create_table(table_name, **options, &blk)
|
163
170
|
end
|
164
171
|
|
165
172
|
# Creates a new join table with the name created using the lexical order of the first two
|
@@ -201,7 +208,14 @@ module Arql
|
|
201
208
|
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
202
209
|
#
|
203
210
|
def create_join_table(table_1, table_2, column_options: {}, **options)
|
204
|
-
|
211
|
+
env_name = options[:env]
|
212
|
+
unless env_name
|
213
|
+
raise ArgumentError, ':env option is required' if Arql::App.instance.environments.size > 1
|
214
|
+
|
215
|
+
env_name = Arql::App.instance.environments.first
|
216
|
+
end
|
217
|
+
options = options.except(:env)
|
218
|
+
Arql::App.instance.definitions[env_name].connection.create_join_table(table_1, table_2, column_options, **options)
|
205
219
|
end
|
206
220
|
|
207
221
|
# Drops a table from the database.
|
@@ -217,7 +231,14 @@ module Arql
|
|
217
231
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
218
232
|
# In that case, +options+ and the block will be used by #create_table.
|
219
233
|
def drop_table(table_name, **options)
|
220
|
-
|
234
|
+
env_name = options[:env]
|
235
|
+
unless env_name
|
236
|
+
raise ArgumentError, ':env option is required' if Arql::App.instance.environments.size > 1
|
237
|
+
|
238
|
+
env_name = Arql::App.instance.environments.first
|
239
|
+
end
|
240
|
+
options = options.except(:env)
|
241
|
+
Arql::App.instance.definitions[env_name].connection.drop_table(table_name, **options)
|
221
242
|
end
|
222
243
|
|
223
244
|
# Drops the join table specified by the given arguments.
|
@@ -227,7 +248,14 @@ module Arql
|
|
227
248
|
# to provide one in a migration's +change+ method so it can be reverted.
|
228
249
|
# In that case, the block will be used by #create_join_table.
|
229
250
|
def drop_join_table(table_1, table_2, **options)
|
230
|
-
|
251
|
+
env_name = options[:env]
|
252
|
+
unless env_name
|
253
|
+
raise ArgumentError, ':env option is required' if Arql::App.instance.environments.size > 1
|
254
|
+
|
255
|
+
env_name = Arql::App.instance.environments.first
|
256
|
+
end
|
257
|
+
options = options.except(:env)
|
258
|
+
Arql::App.instance.definitions[env_name].connection.drop_join_table(table_1, table_2, **options)
|
231
259
|
end
|
232
260
|
|
233
261
|
# Renames a table.
|
@@ -235,9 +263,15 @@ module Arql
|
|
235
263
|
# rename_table('octopuses', 'octopi')
|
236
264
|
#
|
237
265
|
def rename_table(table_name, new_name)
|
238
|
-
|
239
|
-
|
266
|
+
env_name = options[:env]
|
267
|
+
unless env_name
|
268
|
+
raise ArgumentError, ':env option is required' if Arql::App.instance.environments.size > 1
|
240
269
|
|
270
|
+
env_name = Arql::App.instance.environments.first
|
271
|
+
end
|
272
|
+
options = options.except(:env)
|
273
|
+
Arql::App.instance.definitions[env_name].connection.rename_table(table_name, new_name)
|
274
|
+
end
|
241
275
|
end
|
242
276
|
end
|
243
277
|
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module Arql
|
2
|
+
module Extension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def v(compact: false)
|
6
|
+
t = []
|
7
|
+
t << ['Attribute Name', 'Attribute Value', 'SQL Type', 'Comment']
|
8
|
+
t << nil
|
9
|
+
self.class.connection.columns(self.class.table_name).each do |column|
|
10
|
+
value = read_attribute(column.name)
|
11
|
+
if compact && value.blank?
|
12
|
+
next
|
13
|
+
end
|
14
|
+
t << [column.name, value, column.sql_type, column.comment || '']
|
15
|
+
end
|
16
|
+
t
|
17
|
+
end
|
18
|
+
|
19
|
+
def t(compact: false, format: :terminal)
|
20
|
+
puts Terminal::Table.new { |t|
|
21
|
+
t.style = self.class.table_style_for_format(format)
|
22
|
+
v(compact: compact).each { |row| t << (row || :separator) }
|
23
|
+
}.try { |e|
|
24
|
+
case format
|
25
|
+
when :md
|
26
|
+
e.to_s.lines.map { |l| ' ' + l }.join
|
27
|
+
when :org
|
28
|
+
e.to_s.lines.map { |l| ' ' + l.gsub(/^\+|\+$/, '|') }.join
|
29
|
+
else
|
30
|
+
e.to_s
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def vd(compact: false)
|
36
|
+
VD.new do |vd|
|
37
|
+
vd << ['Attribute Name', 'Attribute Value', 'SQL Type', 'Comment']
|
38
|
+
self.class.connection.columns(self.class.table_name).each do |column|
|
39
|
+
value = read_attribute(column.name)
|
40
|
+
next if compact && value.blank?
|
41
|
+
|
42
|
+
vd << [column.name, read_attribute(column.name), column.sql_type, column.comment || '']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_insert_sql
|
48
|
+
self.class.to_insert_sql([self])
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_upsert_sql
|
52
|
+
self.class.to_upsert_sql([self])
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_csv(filename, *fields, **options)
|
56
|
+
[self].write_csv(filename, *fields, **options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def write_excel(filename, *fields, **options)
|
60
|
+
[self].write_excel(filename, *fields, **options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def dump(filename, batch_size=500)
|
64
|
+
[self].dump(filename, batch_size)
|
65
|
+
end
|
66
|
+
|
67
|
+
included do
|
68
|
+
end
|
69
|
+
|
70
|
+
class_methods do
|
71
|
+
|
72
|
+
def v
|
73
|
+
t = [['PK', 'Name', 'SQL Type', 'Ruby Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']]
|
74
|
+
t << nil
|
75
|
+
columns.each do |column|
|
76
|
+
pk = if [connection.primary_key(table_name)].flatten.include?(column.name)
|
77
|
+
'Y'
|
78
|
+
else
|
79
|
+
''
|
80
|
+
end
|
81
|
+
t << [pk, column.name, column.sql_type,
|
82
|
+
column.sql_type_metadata.type, column.sql_type_metadata.limit || '',
|
83
|
+
column.sql_type_metadata.precision || '', column.sql_type_metadata.scale || '', column.default || '',
|
84
|
+
column.null, column.comment || '']
|
85
|
+
end
|
86
|
+
t
|
87
|
+
end
|
88
|
+
|
89
|
+
def t(format: :terminal)
|
90
|
+
heading_prefix = case format
|
91
|
+
when :md
|
92
|
+
'# '
|
93
|
+
when :org
|
94
|
+
'* '
|
95
|
+
when :sql
|
96
|
+
'-- '
|
97
|
+
end
|
98
|
+
puts "\n#{heading_prefix}Table: #{table_name}\n\n"
|
99
|
+
if format == :sql
|
100
|
+
puts to_create_sql + ';'
|
101
|
+
else
|
102
|
+
puts(Terminal::Table.new { |t|
|
103
|
+
t.style = self.table_style_for_format(format)
|
104
|
+
v.each { |row| t << (row || :separator) }
|
105
|
+
}.try { |e|
|
106
|
+
case format
|
107
|
+
when :md
|
108
|
+
e.to_s.lines.map { |l| ' ' + l }.join
|
109
|
+
when :org
|
110
|
+
e.to_s.lines.map { |l| ' ' + l.gsub(/^\+|\+$/, '|') }.join
|
111
|
+
else
|
112
|
+
e.to_s
|
113
|
+
end
|
114
|
+
})
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def vd
|
119
|
+
VD.new do |vd|
|
120
|
+
v.each do |row|
|
121
|
+
vd << row if row
|
122
|
+
end
|
123
|
+
end
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_insert_sql(records, batch_size=1)
|
128
|
+
to_sql(records, :skip, batch_size)
|
129
|
+
end
|
130
|
+
|
131
|
+
def to_upsert_sql(records, batch_size=1)
|
132
|
+
to_sql(records, :update, batch_size)
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_sql(records, on_duplicate, batch_size)
|
136
|
+
records.in_groups_of(batch_size, false).map do |group|
|
137
|
+
ActiveRecord::InsertAll.new(self, group.map(&:attributes), on_duplicate: on_duplicate).send(:to_sql) + ';'
|
138
|
+
end.join("\n")
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_create_sql
|
142
|
+
superclass.definition.connection.exec_query("show create table `#{table_name}`").rows.last.last
|
143
|
+
end
|
144
|
+
|
145
|
+
def dump(filename, no_create_table=false)
|
146
|
+
Arql::Mysqldump.new(superclass.definition.options).dump_table(filename, table_name, no_create_table)
|
147
|
+
end
|
148
|
+
|
149
|
+
def table_style_for_format(format)
|
150
|
+
case format.to_s
|
151
|
+
when 'md'
|
152
|
+
{
|
153
|
+
border_top: false,
|
154
|
+
border_bottom: false,
|
155
|
+
border_i: '|'
|
156
|
+
}
|
157
|
+
when 'org'
|
158
|
+
{
|
159
|
+
border_top: false,
|
160
|
+
border_bottom: false,
|
161
|
+
}
|
162
|
+
else
|
163
|
+
{}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|