crudboy 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,10 @@
1
+ module Crudboy
2
+ class Connection
3
+ class << self
4
+ def open(options)
5
+ ActiveRecord::Base.establish_connection(options)
6
+ $C = ActiveRecord::Base.connection
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,123 @@
1
+ require 'crudboy/concerns'
2
+ module Crudboy
3
+ module Extension
4
+ extend ActiveSupport::Concern
5
+
6
+ def t
7
+ puts Terminal::Table.new { |t|
8
+ v.each { |row| t << (row || :separator) }
9
+ }
10
+ end
11
+
12
+ def v
13
+ t = []
14
+ t << ['Attribute Name', 'Attribute Value', 'SQL Type', 'Comment']
15
+ t << nil
16
+ self.class.connection.columns(self.class.table_name).each do |column|
17
+ t << [column.name, read_attribute(column.name), column.sql_type, column.comment || '']
18
+ end
19
+ t
20
+ end
21
+
22
+ def to_insert_sql
23
+ self.class.to_insert_sql([self])
24
+ end
25
+
26
+ def to_upsert_sql
27
+ self.class.to_upsert_sql([self])
28
+ end
29
+
30
+ def write_csv(filename, *fields, **options)
31
+ [self].write_csv(filename, *fields, **options)
32
+ end
33
+
34
+ def write_excel(filename, *fields, **options)
35
+ [self].write_excel(filename, *fields, **options)
36
+ end
37
+
38
+ class_methods do
39
+ def t
40
+ table_name = Commands::Table::get_table_name(name)
41
+ puts "\nTable: #{table_name}"
42
+ puts Commands::Table::table_info_table(table_name)
43
+ end
44
+
45
+ def v
46
+ table_name = Commands::Table::get_table_name(name)
47
+ Commands::Table::table_info(table_name)
48
+ end
49
+
50
+ def to_insert_sql(records, batch_size = 1)
51
+ to_sql(records, :skip, batch_size)
52
+ end
53
+
54
+ def to_upsert_sql(records, batch_size = 1)
55
+ to_sql(records, :update, batch_size)
56
+ end
57
+ end
58
+ end
59
+
60
+ class Definition
61
+
62
+ attr_accessor :table_name, :model, :model_name
63
+
64
+ def initialize(options)
65
+ @@options = options
66
+ @table_name = @@options[:table_name]
67
+ @model_name = @@options[:model_name]
68
+ ActiveRecord::Base.connection.tap do |conn|
69
+ Object.const_set('CrudboyModel', Class.new(ActiveRecord::Base) do
70
+ include ::Crudboy::Concerns::TableDataDefinition
71
+ self.abstract_class = true
72
+ end)
73
+
74
+ raise "Table not exist: #{@table_name}" unless conn.tables.include?(@table_name)
75
+
76
+ table_comment = conn.table_comment(@table_name)
77
+ conn.primary_key(@table_name).tap do |pkey|
78
+ Class.new(::CrudboyModel) do
79
+ include Crudboy::Extension
80
+ self.table_name = options[:table_name]
81
+ if pkey.is_a?(Array)
82
+ self.primary_keys = pkey
83
+ else
84
+ self.primary_key = pkey
85
+ end
86
+ self.inheritance_column = nil
87
+ self.default_timezone = :local
88
+ if options[:created_at].present?
89
+ define_singleton_method :timestamp_attributes_for_create do
90
+ options[:created_at]
91
+ end
92
+ end
93
+
94
+ if options[:updated_at].present?
95
+ define_singleton_method :timestamp_attributes_for_update do
96
+ options[:updated_at]
97
+ end
98
+ end
99
+ end.tap do |clazz|
100
+ Object.const_set(@model_name, clazz).tap do |const|
101
+ @model = Model.new(const, @table_name, table_comment)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ ::ActiveRecord::Relation.class_eval do
109
+ def t(*attrs, **options)
110
+ records.t(*attrs, **options)
111
+ end
112
+
113
+ def v
114
+ records.v
115
+ end
116
+
117
+ def a
118
+ to_a
119
+ end
120
+
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,5 @@
1
+ require 'crudboy/ext/object'
2
+ require 'crudboy/ext/array'
3
+ require 'crudboy/ext/time'
4
+ require 'crudboy/ext/string'
5
+ require 'crudboy/ext/kernel'
@@ -0,0 +1,139 @@
1
+ class Array
2
+ def to_insert_sql(batch_size=500)
3
+ raise 'All element should be an ActiveRecord instance object' unless all? { |e| e.is_a?(ActiveRecord::Base) }
4
+ group_by(&:class).map do |(klass, records)|
5
+ klass.to_insert_sql(records, batch_size)
6
+ end.join("\n")
7
+ end
8
+
9
+ def to_upsert_sql(batch_size=500)
10
+ raise 'All element should be an ActiveRecord instance object' unless all? { |e| e.is_a?(ActiveRecord::Base) }
11
+ group_by(&:class).map do |(klass, records)|
12
+ klass.to_upsert_sql(records, batch_size)
13
+ end.join("\n")
14
+ end
15
+
16
+ def t(*attrs, **options)
17
+ if (attrs.present? || options.present? && options[:except]) && present? && first.is_a?(ActiveRecord::Base)
18
+ column_names = first.attribute_names.map(&:to_sym)
19
+ attrs = attrs.flat_map { |e| e.is_a?(Regexp) ? column_names.grep(e) : e }.uniq
20
+ if options.present? && options[:except]
21
+ attrs = column_names if attrs.empty?
22
+ if options[:except].is_a?(Regexp)
23
+ attrs.reject! { |e| e =~ options[:except] }
24
+ else
25
+ attrs -= [options[:except]].flatten
26
+ end
27
+ end
28
+ puts Terminal::Table.new { |t|
29
+ t << attrs
30
+ t << :separator
31
+ each do |e|
32
+ t << e.attributes.values_at(*attrs.map(&:to_s))
33
+ end
34
+ }
35
+ else
36
+ table = Terminal::Table.new { |t|
37
+ v.each { |row| t << (row || :separator)}
38
+ }.to_s
39
+
40
+ terminal_width = `tput cols`.to_i
41
+ if table.lines.first.size > terminal_width
42
+ table = table.lines.map(&:chomp)
43
+ puts table[0..2].join("\n")
44
+ puts table[3..-1].join("\n#{'-' * terminal_width}\n")
45
+ else
46
+ puts table
47
+ end
48
+ end
49
+ end
50
+
51
+ def v
52
+ return self unless present?
53
+ t = []
54
+ if map(&:class).uniq.size == 1
55
+ if first.is_a?(ActiveRecord::Base)
56
+ t << first.attribute_names
57
+ t << nil
58
+ each do |e|
59
+ t << e.attributes.values_at(*first.attribute_names).map(&:as_json)
60
+ end
61
+ elsif first.is_a?(Array)
62
+ t = map { |a| a.map(&:as_json) }
63
+ elsif first.is_a?(Hash) || first.is_a?(ActiveSupport::HashWithIndifferentAccess)
64
+ t << first.keys
65
+ t << nil
66
+ each do |e|
67
+ t << e.values_at(*first.keys).map(&:as_json)
68
+ end
69
+ else
70
+ return self
71
+ end
72
+ end
73
+ t
74
+ end
75
+
76
+ def write_csv(filename, *fields, **options)
77
+ generate_csv(filename, **options) do |csv|
78
+ if size > 0 && first.is_a?(ActiveRecord::Base)
79
+ if fields.empty?
80
+ fields = first.attributes.keys
81
+ else
82
+ fields = fields.map(&:to_s)
83
+ end
84
+ csv << fields
85
+ end
86
+ if size > 0 && first.is_a?(Hash)
87
+ if fields.empty?
88
+ fields = first.keys
89
+ end
90
+ csv << fields
91
+ end
92
+ each do |row|
93
+ if row.is_a?(Array)
94
+ csv << row.map(&:to_s)
95
+ else
96
+ csv << row.slice(*fields).values.map(&:to_s)
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def write_excel(filename, *fields, **options)
103
+ sheet_name = options[:sheet_name] || 'Sheet1'
104
+ generate_excel(filename) do |workbook|
105
+ workbook.add_worksheet(name: sheet_name) do |sheet|
106
+ if size > 0 && first.is_a?(ActiveRecord::Base)
107
+ if fields.empty?
108
+ fields = first.attributes.keys
109
+ else
110
+ fields = fields.map(&:to_s)
111
+ end
112
+ sheet.add_row(fields, types: [:string] * fields.size)
113
+ end
114
+ if size > 0 && first.is_a?(Hash)
115
+ if fields.empty?
116
+ fields = first.keys
117
+ end
118
+ sheet.add_row(fields, types: [:string] * fields.size)
119
+ end
120
+ each do |row|
121
+ if row.is_a?(Array)
122
+ sheet.add_row(row.map(&:to_s), types: [:string] * row.size)
123
+ else
124
+ sheet.add_row(row.slice(*fields).values.map(&:to_s), types: [:string] * fields.size)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def dump(filename, batch_size=500)
132
+ File.open(File.expand_path(filename), 'w') do |file|
133
+ group_by(&:class).each do |(klass, records)|
134
+ file.puts(klass.to_upsert_sql(records, batch_size))
135
+ end
136
+ end
137
+ {size: size, file: File.expand_path(filename)}
138
+ end
139
+ 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,112 @@
1
+ require 'crudboy/concerns'
2
+ module Kernel
3
+ CSV_BOM = "\xef\xbb\xbf"
4
+
5
+ include ::Crudboy::Concerns::GlobalDataDefinition
6
+
7
+ def sql(sql)
8
+ ActiveRecord::Base.connection.exec_query(sql)
9
+ end
10
+
11
+ def print_tables(format = :md)
12
+ require 'terminal-table'
13
+
14
+ tables = ActiveRecord::Base.connection.tables.map do |table_name|
15
+ {
16
+ table: table_name,
17
+ table_comment: ActiveRecord::Base.connection.table_comment(table_name) || '',
18
+ columns: ::ActiveRecord::Base.connection.columns(table_name)
19
+ }
20
+ end
21
+
22
+ outputs = tables.map do |table|
23
+ table_name = table[:table]
24
+ table_comment = table[:table_comment]
25
+ case format
26
+ when :md
27
+ "# #{table_name} #{table_comment}\n\n" +
28
+ Terminal::Table.new { |t|
29
+ t.headings = ['PK', 'Name', 'SQL Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
30
+ t.rows = table[:columns].map { |column|
31
+ pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column)
32
+ 'Y'
33
+ else
34
+ ''
35
+ end
36
+ [pk, "`#{column.name}`", column.sql_type, column.sql_type_metadata.limit || '', column.sql_type_metadata.precision || '',
37
+ column.sql_type_metadata.scale || '', column.default || '', column.null, column.comment || '']
38
+ }
39
+ t.style = {
40
+ border_top: false,
41
+ border_bottom: false,
42
+ border_i: '|'
43
+ }
44
+ }.to_s.lines.map { |l| ' ' + l }.join
45
+ when :org
46
+ "* #{table_name} #{table_comment}\n\n" +
47
+ Terminal::Table.new { |t|
48
+ t.headings = ['PK', 'Name', 'SQL Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
49
+ t.rows = table[:columns].map { |column|
50
+ pk = if [::ActiveRecord::Base.connection.primary_key(table_name)].flatten.include?(column)
51
+ 'Y'
52
+ else
53
+ ''
54
+ end
55
+ [pk, "=#{column.name}=", column.sql_type, column.sql_type_metadata.limit || '', column.sql_type_metadata.precision || '',
56
+ column.sql_type_metadata.scale || '', column.default || '', column.null, column.comment || '']
57
+ }
58
+ t.style = {
59
+ border_top: false,
60
+ border_bottom: false,
61
+ }
62
+ }.to_s.lines.map { |l| ' ' + l.gsub(/^\+|\+$/, '|') }.join
63
+ when :sql
64
+ "-- Table: #{table_name}\n\n" + ActiveRecord::Base.connection.exec_query("show create table `#{table_name}`").rows.last.last + ';'
65
+ end
66
+ end
67
+
68
+ outputs.each { |out| puts out; puts }
69
+ end
70
+
71
+ def generate_csv(filename, **options, &block)
72
+ opts = {
73
+ col_sep: "\t",
74
+ row_sep: "\r\n"
75
+ }
76
+ opts.merge!(options.except(:encoding))
77
+ encoding = options[:encoding] || 'UTF-16LE'
78
+ File.open(File.expand_path(filename), "w:#{encoding}") do |file|
79
+ file.write(CSV_BOM)
80
+ file.write CSV.generate(**opts, &block)
81
+ end
82
+ end
83
+
84
+ def parse_csv(filename, **options)
85
+ encoding = options[:encoding] || 'UTF-16'
86
+ opts = {
87
+ headers: false,
88
+ col_sep: "\t",
89
+ row_sep: "\r\n"
90
+ }
91
+ opts.merge!(options.except(:encoding))
92
+ CSV.parse(IO.read(File.expand_path(filename), encoding: encoding, binmode: true).encode('UTF-8'), **opts).to_a
93
+ end
94
+
95
+ def generate_excel(filename)
96
+ Axlsx::Package.new do |package|
97
+ yield(package.workbook)
98
+ package.serialize(File.expand_path(filename))
99
+ end
100
+ end
101
+
102
+ def parse_excel(filename)
103
+ xlsx = Roo::Excelx.new(File.expand_path(filename))
104
+ xlsx.sheets.each_with_object({}) do |sheet_name, result|
105
+ begin
106
+ result[sheet_name] = xlsx.sheet(sheet_name).to_a
107
+ rescue
108
+ end
109
+ end
110
+ end
111
+
112
+ 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