arql 0.3.31 → 0.4.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/.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
|