pry-helper 0.1.0

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