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.
@@ -3,84 +3,129 @@ require 'terminal-table'
3
3
  module Arql::Commands
4
4
  module Models
5
5
  class << self
6
- def models
7
- t = []
8
- t << ['Table Name', 'Model Class', 'Abbr', 'Comment']
9
- t << nil
10
- Arql::Definition.models.each do |definition|
11
- t << [definition[:table], definition[:model].name, definition[:abbr] || '', definition[:comment] || '']
12
- end
13
- t
14
- end
15
-
16
- def models_table(table_regexp=nil, column_regexp=nil)
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
- connection = ::ActiveRecord::Base.connection
27
- table = Terminal::Table.new do |t|
28
- t << ['PK', 'Table', 'Model', 'Name', 'SQL Type', 'Ruby Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
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
- Arql::Definition.models.each do |definition|
31
- model_class = definition[:model]
32
- matched_columns = model_class.columns.select { |column| column.name =~ column_regexp || column.comment =~ column_regexp }
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
- puts table
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.block_command 'm' do |arg|
54
- puts
55
- if arg.start_with?('c=') or arg.start_with?('column=')
56
- column_regexp = arg.sub(/^c(olumn)?=/, '')
57
- Models::models_table(nil, column_regexp.try { |e| e.start_with?('/') ? eval(e) : Regexp.new(e) })
58
- else
59
- puts Models::models_table(arg.try { |e| e.start_with?('/') ? eval(e) : Regexp.new(e) }, nil)
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
- Pry.commands.alias_command 'l', 'm'
64
- end
104
+ def process
65
105
 
66
- module Kernel
67
- def models
68
- Arql::Commands::Models::models
69
- end
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
- def tables
72
- models
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
- def model_classes
76
- ::ArqlModel.subclasses
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
- def table_names
80
- models[2..-1].map(&:first)
81
- end
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::SSHProxy.reconnect if Arql::App.config[:ssh].present?
6
- ActiveRecord::Base.connection.reconnect! unless ActiveRecord::Base.connection.active?
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::SSHProxy.reconnect! if Arql::App.config[:ssh].present?
11
- ActiveRecord::Base.connection.reconnect!
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
 
@@ -2,7 +2,9 @@ module Arql::Commands
2
2
  module Redefine
3
3
  class << self
4
4
  def redefine
5
- Arql::Definition.redefine
5
+ Arql::App.instance.definitions.each do |_, definition|
6
+ definition.redefine
7
+ end
6
8
  end
7
9
  end
8
10
 
@@ -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
- ActiveRecord::Base.connection.begin_transaction(joinable: false)
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
- ActiveRecord::Base.connection.create_table(table_name, **options, &blk)
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
- ActiveRecord::Base.connection.create_join_table(table_1, table_2, column_options, **options)
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
- ActiveRecord::Base.connection.drop_table(table_name, **options)
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
- ActiveRecord::Base.connection.drop_join_table(table_1, table_2, **options)
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
- ActiveRecord::Base.connection.rename_table(table_name, new_name)
239
- end
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