pry-helper 0.1.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.
@@ -0,0 +1,246 @@
1
+ require 'pry-helper/concerns'
2
+ require 'pry-helper/vd'
3
+
4
+ module PryHelper
5
+ module Extension
6
+ extend ActiveSupport::Concern
7
+
8
+ def t
9
+ puts Terminal::Table.new { |t|
10
+ v.each { |row| t << (row || :separator) }
11
+ }
12
+ end
13
+
14
+ def vd
15
+ VD.new do |vd|
16
+ vd << ['Attribute Name', 'Attribute Value', 'SQL Type', 'Comment']
17
+ self.class.connection.columns(self.class.table_name).each do |column|
18
+ vd << [column.name, read_attribute(column.name), column.sql_type, column.comment || '']
19
+ end
20
+ end
21
+ end
22
+
23
+ def v
24
+ t = []
25
+ t << ['Attribute Name', 'Attribute Value', 'SQL Type', 'Comment']
26
+ t << nil
27
+ self.class.connection.columns(self.class.table_name).each do |column|
28
+ t << [column.name, read_attribute(column.name), column.sql_type, column.comment || '']
29
+ end
30
+ t
31
+ end
32
+
33
+ def to_insert_sql
34
+ self.class.to_insert_sql([self])
35
+ end
36
+
37
+ def to_upsert_sql
38
+ self.class.to_upsert_sql([self])
39
+ end
40
+
41
+ def write_csv(filename, *fields, **options)
42
+ [self].write_csv(filename, *fields, **options)
43
+ end
44
+
45
+ def write_excel(filename, *fields, **options)
46
+ [self].write_excel(filename, *fields, **options)
47
+ end
48
+
49
+ def dump(filename, batch_size=500)
50
+ [self].dump(filename, batch_size)
51
+ end
52
+
53
+ included do
54
+ end
55
+
56
+ class_methods do
57
+ def t
58
+ table_name = Commands::Table::get_table_name(name)
59
+ puts "\nTable: #{table_name}"
60
+ puts Commands::Table::table_info_table(table_name)
61
+ end
62
+
63
+ def vd
64
+ table_name = Commands::Table::get_table_name(name)
65
+ Commands::VD::table_info_vd(table_name)
66
+ nil
67
+ end
68
+
69
+ def v
70
+ table_name = Commands::Table::get_table_name(name)
71
+ Commands::Table::table_info(table_name)
72
+ end
73
+ def to_insert_sql(records, batch_size=1)
74
+ to_sql(records, :skip, batch_size)
75
+ end
76
+
77
+ def to_upsert_sql(records, batch_size=1)
78
+ to_sql(records, :update, batch_size)
79
+ end
80
+
81
+ def to_sql(records, on_duplicate, batch_size)
82
+ records.in_groups_of(batch_size, false).map do |group|
83
+ ActiveRecord::InsertAll.new(self, group.map(&:attributes), on_duplicate: on_duplicate).send(:to_sql) + ';'
84
+ end.join("\n")
85
+ end
86
+
87
+ def to_create_sql
88
+ ActiveRecord::Base.connection.exec_query("show create table #{table_name}").rows.last.last
89
+ end
90
+
91
+ def dump(filename, no_create_table=false)
92
+ PryHelper::Mysqldump.new.dump_table(filename, table_name, no_create_table)
93
+ end
94
+ end
95
+ end
96
+
97
+ class Definition
98
+ class << self
99
+ def models
100
+ @@models ||= []
101
+ end
102
+ end
103
+
104
+ def initialize
105
+ @@models = []
106
+
107
+ ActiveRecord::Base.connection.tap do |conn|
108
+ defined_models = ::ApplicationRecord.descendants
109
+ tables = conn.tables
110
+ if conn.adapter_name == 'Mysql2'
111
+ conn.define_singleton_method(:dump) do |filename, no_create_db=false|
112
+ PryHelper::Mysqldump.new.dump_database(filename, no_create_db)
113
+ end
114
+ require 'pry-helper/ext/active_record/connection_adapters/abstract_mysql_adapter'
115
+ comments = conn.table_comment_of_tables(tables)
116
+ primary_keys = conn.primary_keys_of_tables(tables)
117
+ else
118
+ comments = tables.map { |t| [t, conn.table_comment(t)] }.to_h
119
+ primary_keys = tables.map { |t| [t, conn.primary_keys(t)] }.to_h
120
+ end
121
+
122
+ ApplicationRecord.include(PryHelper::Extension)
123
+
124
+ tables.each do |table_name|
125
+ table_comment = comments[table_name]
126
+ primary_keys[table_name].tap do |pkey|
127
+ table_name.camelize.tap do |const_name|
128
+ const_name = 'Modul' if const_name == 'Module'
129
+ const_name = 'Clazz' if const_name == 'Class'
130
+ if model_class = defined_models.find { |m| m.table_name == table_name }
131
+ model_class.tap do |clazz|
132
+ model_class.name.gsub(/[a-z]*/, '').tap do |bare_abbr|
133
+ abbr_const = nil
134
+ 9.times do |idx|
135
+ abbr = idx.zero? ? bare_abbr : "#{bare_abbr}#{idx+1}"
136
+ unless Object.const_defined?(abbr)
137
+ Object.const_set abbr, model_class
138
+ abbr_const = abbr
139
+ break
140
+ end
141
+ end
142
+
143
+ @@models << {
144
+ model: model_class,
145
+ abbr: abbr_const,
146
+ table: table_name,
147
+ comment: table_comment
148
+ }
149
+ end
150
+ end
151
+ else
152
+ Class.new(::ApplicationRecord) do
153
+ if pkey.is_a?(Array) && pkey.size > 1
154
+ self.primary_keys = pkey
155
+ else
156
+ self.primary_key = pkey&.first
157
+ end
158
+ self.table_name = table_name
159
+ self.inheritance_column = nil
160
+ ActiveRecord.default_timezone = :local
161
+ end.tap do |clazz|
162
+ Object.const_set(const_name, clazz).tap do |const|
163
+ const_name.gsub(/[a-z]*/, '').tap do |bare_abbr|
164
+ abbr_const = nil
165
+ 9.times do |idx|
166
+ abbr = idx.zero? ? bare_abbr : "#{bare_abbr}#{idx+1}"
167
+ unless Object.const_defined?(abbr)
168
+ Object.const_set abbr, const
169
+ abbr_const = abbr
170
+ break
171
+ end
172
+ end
173
+
174
+ @@models << {
175
+ model: const,
176
+ abbr: abbr_const,
177
+ table: table_name,
178
+ comment: table_comment
179
+ }
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ ::ActiveRecord::Relation.class_eval do
191
+ def t(*attrs, **options)
192
+ records.t(*attrs, **options)
193
+ end
194
+
195
+ def vd(*attrs, **options)
196
+ records.vd(*attrs, **options)
197
+ end
198
+
199
+ def v
200
+ records.v
201
+ end
202
+
203
+ def a
204
+ to_a
205
+ end
206
+
207
+ def write_csv(filename, *fields, **options)
208
+ records.write_csv(filename, *fields, **options)
209
+ end
210
+
211
+ def write_excel(filename, *fields, **options)
212
+ records.write_excel(filename, *fields, **options)
213
+ end
214
+
215
+ def dump(filename, batch_size=500)
216
+ records.dump(filename, batch_size)
217
+ end
218
+ end
219
+
220
+ ::ActiveRecord::Result.class_eval do
221
+ def t(*attrs, **options)
222
+ to_a.t(*attrs, **options)
223
+ end
224
+
225
+ def vd(*attrs, **options)
226
+ to_a.vd(*attrs, **options)
227
+ end
228
+
229
+ def v
230
+ to_a.v
231
+ end
232
+
233
+ def a
234
+ to_a
235
+ end
236
+
237
+ def write_csv(filename, *fields, **options)
238
+ to_a.write_csv(filename, *fields, **options)
239
+ end
240
+
241
+ def write_excel(filename, *fields, **options)
242
+ to_a.write_excel(filename, *fields, **options)
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,53 @@
1
+ require 'active_record/connection_adapters/abstract_mysql_adapter'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class AbstractMysqlAdapter
6
+
7
+ def extract_schema_qualified_name_of_tables(table_names)
8
+ table_names.map do |string|
9
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
10
+ schema, name = nil, schema unless name
11
+ [schema, name]
12
+ end
13
+ end
14
+
15
+ def quoted_scope_of_tables(names = nil)
16
+ extract_schema_qualified_name_of_tables(names).map do |(schema, name)|
17
+ scope = {}
18
+ scope[:schema] = schema ? quote(schema) : "database()"
19
+ scope[:name] = quote(name) if name
20
+ scope
21
+ end
22
+ end
23
+
24
+ def primary_keys_of_tables(table_names) # :nodoc:
25
+ raise ArgumentError unless table_names.present?
26
+
27
+ scopes = quoted_scope_of_tables(table_names)
28
+
29
+ res = query(<<~SQL, "SCHEMA")
30
+ SELECT table_name, column_name
31
+ FROM information_schema.statistics
32
+ WHERE index_name = 'PRIMARY'
33
+ AND (table_schema, table_name) in
34
+ (#{scopes.map { |scope| "(#{scope[:schema]}, #{scope[:name]})" }.join(', ')})
35
+ ORDER BY seq_in_index
36
+ SQL
37
+
38
+ res.group_by(&:first).map { |table, vlaues| [table, vlaues.map(&:last)] }.to_h
39
+ end
40
+
41
+ def table_comment_of_tables(table_names) # :nodoc:
42
+ scopes = quoted_scope_of_tables(table_names)
43
+
44
+ query(<<~SQL, "SCHEMA").presence.try(&:to_h)
45
+ SELECT table_name, table_comment
46
+ FROM information_schema.tables
47
+ WHERE (table_schema, table_name) in
48
+ (#{scopes.map { |scope| "(#{scope[:schema]}, #{scope[:name]})" }.join(', ')})
49
+ SQL
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,8 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module SchemaStatements
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,168 @@
1
+ require 'pry-helper/vd'
2
+
3
+ class Array
4
+ def to_insert_sql(batch_size=500)
5
+ raise 'All element should be an ActiveRecord instance object' unless all? { |e| e.is_a?(ActiveRecord::Base) }
6
+ group_by(&:class).map do |(klass, records)|
7
+ klass.to_insert_sql(records, batch_size)
8
+ end.join("\n")
9
+ end
10
+
11
+ def to_upsert_sql(batch_size=500)
12
+ raise 'All element should be an ActiveRecord instance object' unless all? { |e| e.is_a?(ActiveRecord::Base) }
13
+ group_by(&:class).map do |(klass, records)|
14
+ klass.to_upsert_sql(records, batch_size)
15
+ end.join("\n")
16
+ end
17
+
18
+ def t(*attrs, **options)
19
+ if (attrs.present? || options.present? && options[:except]) && present? && first.is_a?(ActiveRecord::Base)
20
+ column_names = first.attribute_names.map(&:to_sym)
21
+ attrs = attrs.flat_map { |e| e.is_a?(Regexp) ? column_names.grep(e) : e }.uniq
22
+ if options.present? && options[:except]
23
+ attrs = column_names if attrs.empty?
24
+ if options[:except].is_a?(Regexp)
25
+ attrs.reject! { |e| e =~ options[:except] }
26
+ else
27
+ attrs -= [options[:except]].flatten
28
+ end
29
+ end
30
+ puts Terminal::Table.new { |t|
31
+ t << attrs
32
+ t << :separator
33
+ each do |e|
34
+ t << e.attributes.values_at(*attrs.map(&:to_s))
35
+ end
36
+ }
37
+ else
38
+ table = Terminal::Table.new { |t|
39
+ v.each { |row| t << (row || :separator)}
40
+ }.to_s
41
+
42
+ terminal_width = `tput cols`.to_i
43
+ if table.lines.first.size > terminal_width
44
+ table = table.lines.map(&:chomp)
45
+ puts table[0..2].join("\n")
46
+ puts table[3..-1].join("\n#{'-' * terminal_width}\n")
47
+ else
48
+ puts table
49
+ end
50
+ end
51
+ end
52
+
53
+ def vd(*attrs, **options)
54
+ if (attrs.present? || options.present? && options[:except]) && present? && first.is_a?(ActiveRecord::Base)
55
+ column_names = first.attribute_names.map(&:to_sym)
56
+ attrs = attrs.flat_map { |e| e.is_a?(Regexp) ? column_names.grep(e) : e }.uniq
57
+ if options.present? && options[:except]
58
+ attrs = column_names if attrs.empty?
59
+ if options[:except].is_a?(Regexp)
60
+ attrs.reject! { |e| e =~ options[:except] }
61
+ else
62
+ attrs -= [options[:except]].flatten
63
+ end
64
+ end
65
+
66
+ PryHelper::VD.new do |vd|
67
+ vd << attrs
68
+ each do |e|
69
+ vd << e.attributes.values_at(*attrs.map(&:to_s))
70
+ end
71
+ end
72
+ else
73
+ PryHelper::VD.new do |vd|
74
+ v.each { |row| vd << row if row }
75
+ end
76
+ end
77
+ nil
78
+ end
79
+
80
+ def v
81
+ return self unless present?
82
+ t = []
83
+ if map(&:class).uniq.size == 1
84
+ if first.is_a?(ActiveRecord::Base)
85
+ t << first.attribute_names
86
+ t << nil
87
+ each do |e|
88
+ t << e.attributes.values_at(*first.attribute_names).map(&:as_json)
89
+ end
90
+ elsif first.is_a?(Array)
91
+ t = map { |a| a.map(&:as_json) }
92
+ elsif first.is_a?(Hash) || first.is_a?(ActiveSupport::HashWithIndifferentAccess)
93
+ t << first.keys
94
+ t << nil
95
+ each do |e|
96
+ t << e.values_at(*first.keys).map(&:as_json)
97
+ end
98
+ else
99
+ return self
100
+ end
101
+ end
102
+ t
103
+ end
104
+
105
+ def write_csv(filename, *fields, **options)
106
+ generate_csv(filename, **options) do |csv|
107
+ if size > 0 && first.is_a?(ActiveRecord::Base)
108
+ if fields.empty?
109
+ fields = first.attributes.keys
110
+ else
111
+ fields = fields.map(&:to_s)
112
+ end
113
+ csv << fields
114
+ end
115
+ if size > 0 && first.is_a?(Hash)
116
+ if fields.empty?
117
+ fields = first.keys
118
+ end
119
+ csv << fields
120
+ end
121
+ each do |row|
122
+ if row.is_a?(Array)
123
+ csv << row.map(&:to_s)
124
+ else
125
+ csv << row.slice(*fields).values.map(&:to_s)
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def write_excel(filename, *fields, **options)
132
+ sheet_name = options[:sheet_name] || 'Sheet1'
133
+ generate_excel(filename) do |workbook|
134
+ workbook.add_worksheet(name: sheet_name) do |sheet|
135
+ if size > 0 && first.is_a?(ActiveRecord::Base)
136
+ if fields.empty?
137
+ fields = first.attributes.keys
138
+ else
139
+ fields = fields.map(&:to_s)
140
+ end
141
+ sheet.add_row(fields, types: [:string] * fields.size)
142
+ end
143
+ if size > 0 && first.is_a?(Hash)
144
+ if fields.empty?
145
+ fields = first.keys
146
+ end
147
+ sheet.add_row(fields, types: [:string] * fields.size)
148
+ end
149
+ each do |row|
150
+ if row.is_a?(Array)
151
+ sheet.add_row(row.map(&:to_s), types: [:string] * row.size)
152
+ else
153
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def dump(filename, batch_size=500)
161
+ File.open(File.expand_path(filename), 'w') do |file|
162
+ group_by(&:class).each do |(klass, records)|
163
+ file.puts(klass.to_upsert_sql(records, batch_size))
164
+ end
165
+ end
166
+ {size: size, file: File.expand_path(filename)}
167
+ end
168
+ end
@@ -0,0 +1,41 @@
1
+ class Hash
2
+ def write_excel(filename)
3
+ generate_excel(filename) do |workbook|
4
+ each do |sheet_name, sheet_data|
5
+ workbook.add_worksheet(name: sheet_name) do |sheet|
6
+ if sheet_data.is_a?(Hash)
7
+ fields = sheet_data[:fields].map(&:to_s)
8
+ sheet.add_row(fields, types: [:string] * fields.size)
9
+ sheet_data[:data].each do |row|
10
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
11
+ end
12
+ end
13
+
14
+ if sheet_data.is_a?(Array)
15
+ if sheet_data.size > 0 && sheet_data.first.is_a?(ActiveModel::Base)
16
+ fields = sheet_data.first.attributes.keys
17
+ sheet.add_row(fields, types: [:string] * fields.size)
18
+ sheet_data.each do |row|
19
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
20
+ end
21
+ end
22
+
23
+ if sheet_data.size > 0 && sheet_data.first.is_a?(Hash)
24
+ fields = sheet_data.first.keys
25
+ sheet.add_row(fields, types: [:string] * fields.size)
26
+ sheet_data.each do |row|
27
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
28
+ end
29
+ end
30
+
31
+ if sheet_data.size > 0 && sheet_data.first.is_a?(Array)
32
+ sheet_data.each do |row|
33
+ sheet.add_row(row.map(&:to_s), types: [:string] * fields.size)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,111 @@
1
+ require 'pry-helper/concerns'
2
+ module Kernel
3
+ CSV_BOM = "\xef\xbb\xbf"
4
+
5
+ include ::PryHelper::Concerns::GlobalDataDefinition
6
+
7
+ def sql(sql)
8
+ ActiveRecord::Base.connection.exec_query(sql)
9
+ end
10
+
11
+ def print_tables(format = :md)
12
+
13
+ tables = ActiveRecord::Base.connection.tables.map do |table_name|
14
+ {
15
+ table: table_name,
16
+ table_comment: ActiveRecord::Base.connection.table_comment(table_name) || '',
17
+ columns: ::ActiveRecord::Base.connection.columns(table_name)
18
+ }
19
+ end
20
+
21
+ outputs = tables.map do |table|
22
+ table_name = table[:table]
23
+ table_comment = table[:table_comment]
24
+ case format
25
+ when :md
26
+ "# #{table_name} #{table_comment}\n\n" +
27
+ Terminal::Table.new { |t|
28
+ t.headings = ['PK', 'Name', 'SQL Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
29
+ t.rows = table[:columns].map { |column|
30
+ pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column)
31
+ 'Y'
32
+ else
33
+ ''
34
+ end
35
+ [pk, "`#{column.name}`", column.sql_type, column.sql_type_metadata.limit || '', column.sql_type_metadata.precision || '',
36
+ column.sql_type_metadata.scale || '', column.default || '', column.null, column.comment || '']
37
+ }
38
+ t.style = {
39
+ border_top: false,
40
+ border_bottom: false,
41
+ border_i: '|'
42
+ }
43
+ }.to_s.lines.map { |l| ' ' + l }.join
44
+ when :org
45
+ "* #{table_name} #{table_comment}\n\n" +
46
+ Terminal::Table.new { |t|
47
+ t.headings = ['PK', 'Name', 'SQL Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
48
+ t.rows = table[:columns].map { |column|
49
+ pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column)
50
+ 'Y'
51
+ else
52
+ ''
53
+ end
54
+ [pk, "=#{column.name}=", column.sql_type, column.sql_type_metadata.limit || '', column.sql_type_metadata.precision || '',
55
+ column.sql_type_metadata.scale || '', column.default || '', column.null, column.comment || '']
56
+ }
57
+ t.style = {
58
+ border_top: false,
59
+ border_bottom: false,
60
+ }
61
+ }.to_s.lines.map { |l| ' ' + l.gsub(/^\+|\+$/, '|') }.join
62
+ when :sql
63
+ "-- Table: #{table_name}\n\n" + ActiveRecord::Base.connection.exec_query("show create table `#{table_name}`").rows.last.last + ';'
64
+ end
65
+ end
66
+
67
+ outputs.each { |out| puts out; puts }
68
+ end
69
+
70
+ def generate_csv(filename, **options, &block)
71
+ opts = {
72
+ col_sep: "\t",
73
+ row_sep: "\r\n"
74
+ }
75
+ opts.merge!(options.except(:encoding))
76
+ encoding = options[:encoding] || 'UTF-16LE'
77
+ File.open(File.expand_path(filename), "w:#{encoding}") do |file|
78
+ file.write(CSV_BOM)
79
+ file.write CSV.generate(**opts, &block)
80
+ end
81
+ end
82
+
83
+ def parse_csv(filename, **options)
84
+ encoding = options[:encoding] || 'UTF-16'
85
+ opts = {
86
+ headers: false,
87
+ col_sep: "\t",
88
+ row_sep: "\r\n"
89
+ }
90
+ opts.merge!(options.except(:encoding))
91
+ CSV.parse(IO.read(File.expand_path(filename), encoding: encoding, binmode: true).encode('UTF-8'), **opts).to_a
92
+ end
93
+
94
+ def generate_excel(filename)
95
+ Axlsx::Package.new do |package|
96
+ yield(package.workbook)
97
+ package.serialize(File.expand_path(filename))
98
+ end
99
+ end
100
+
101
+ def parse_excel(filename)
102
+ xlsx = Roo::Excelx.new(File.expand_path(filename))
103
+ xlsx.sheets.each_with_object({}) do |sheet_name, result|
104
+ begin
105
+ result[sheet_name] = xlsx.sheet(sheet_name).to_a
106
+ rescue
107
+ end
108
+ end
109
+ end
110
+
111
+ end
@@ -0,0 +1,22 @@
1
+ class Object
2
+
3
+ def j
4
+ to_json
5
+ end
6
+
7
+ def jj
8
+ JSON.pretty_generate(JSON.parse(to_json))
9
+ end
10
+
11
+ def jp
12
+ puts j
13
+ end
14
+
15
+ def jjp
16
+ puts jj
17
+ end
18
+
19
+ def a
20
+ [self]
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ class String
2
+ def p
3
+ puts self
4
+ end
5
+
6
+ def expa
7
+ File.expand_path(self)
8
+ end
9
+
10
+ def f
11
+ expa
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_support/core_ext/time/conversions'
2
+ require 'active_support/core_ext/object/json'
3
+
4
+ class Time
5
+ DATE_FORMATS ||= {}
6
+ DATE_FORMATS[:default] = '%Y-%m-%d %H:%M:%S'
7
+
8
+ def inspect
9
+ to_s
10
+ end
11
+
12
+ def as_json(*args)
13
+ to_s
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ require 'pry-helper/ext/object'
2
+ require 'pry-helper/ext/array'
3
+ require 'pry-helper/ext/time'
4
+ require 'pry-helper/ext/string'
5
+ require 'pry-helper/ext/kernel'