breathing 0.0.4 → 0.0.9
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/README.md +25 -1
- data/breathing.gemspec +3 -2
- data/lib/breathing.rb +20 -0
- data/lib/breathing/change_log.rb +34 -0
- data/lib/breathing/cli.rb +26 -0
- data/lib/breathing/excel.rb +40 -22
- data/lib/breathing/installer.rb +14 -19
- data/lib/breathing/terminal_table.rb +30 -0
- data/lib/breathing/trigger.rb +49 -45
- data/spec/breathing/excel_spec.rb +7 -2
- data/spec/breathing/terminal_table_spec.rb +22 -0
- metadata +21 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78f7bd77e2aacd87fe91b8e42eeb8dd590fac1a067c90c092a8069fa9d2a391f
|
4
|
+
data.tar.gz: cb6802aa877aa675fe62170d7d3fde082b1262439c33409a5a38169c90a05c8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1610bf010d660a7f0c1f00ed61b3235e75497220ef2b21c0f9a5c200b2c670c8495cbf905b782c7202f0236549e086296141d6e3d1b5fd1c24684d3a3af69335
|
7
|
+
data.tar.gz: 22d2262edfd99554875fbbaec0edac113c5e970a41f32126f7ebf18cc7b141987f9c58b1da1c02f4c1257e8f07a07e4a9a269c8876f6957bcc10cbac3767b161
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Logging mechanism using database triggers to store the old and new row states in
|
|
6
6
|
## Install
|
7
7
|
|
8
8
|
```
|
9
|
-
gem install
|
9
|
+
gem install breathing
|
10
10
|
```
|
11
11
|
|
12
12
|
## Usage
|
@@ -49,6 +49,30 @@ Cleanup command.
|
|
49
49
|
|
50
50
|
- Output file `breathing.xlsx`
|
51
51
|
|
52
|
+
### out
|
53
|
+
|
54
|
+
```
|
55
|
+
% DATABASE_URL="mysql2://user:pass@host:port/database" breathing out --table users --id 1
|
56
|
+
```
|
57
|
+
|
58
|
+
```
|
59
|
+
+----------------+------------------------+--------+----+-----+------+----------------------------+----------------------------+
|
60
|
+
| users |
|
61
|
+
+----------------+------------------------+--------+----+-----+------+----------------------------+----------------------------+
|
62
|
+
| change_logs.id | change_logs.created_at | action | id | age | name | created_at | updated_at |
|
63
|
+
+----------------+------------------------+--------+----+-----+------+----------------------------+----------------------------+
|
64
|
+
| 1 | 2020-12-18 22:43:32 | INSERT | 10 | 20 | a | 2020-12-18 13:43:32.316923 | 2020-12-18 13:43:32.316923 |
|
65
|
+
| 2 | 2020-12-18 22:43:32 | UPDATE | 10 | 21 | a | 2020-12-18 13:43:32.316923 | 2020-12-18 13:43:32.319706 |
|
66
|
+
| 3 | 2020-12-18 22:43:32 | DELETE | 10 | 21 | a | 2020-12-18 13:43:32.316923 | 2020-12-18 13:43:32.319706 |
|
67
|
+
+----------------+------------------------+--------+----+-----+------+----------------------------+----------------------------+
|
68
|
+
```
|
69
|
+
|
70
|
+
### tail
|
71
|
+
|
72
|
+
```
|
73
|
+
% DATABASE_URL="mysql2://user:pass@host:port/database" breathing tail --table users --id 1
|
74
|
+
```
|
75
|
+
|
52
76
|
## Compatibility
|
53
77
|
|
54
78
|
- Ruby 2.3.0+
|
data/breathing.gemspec
CHANGED
@@ -2,7 +2,7 @@ $:.push File.expand_path('lib', __dir__)
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'breathing'
|
5
|
-
s.version = '0.0.
|
5
|
+
s.version = '0.0.9'
|
6
6
|
s.platform = Gem::Platform::RUBY
|
7
7
|
s.authors = ['Akira Kusumoto']
|
8
8
|
s.email = ['akirakusumo10@gmail.com']
|
@@ -20,10 +20,11 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
s.add_runtime_dependency 'thor'
|
22
22
|
|
23
|
-
s.add_dependency 'activerecord', ['>=
|
23
|
+
s.add_dependency 'activerecord', ['>= 6.0.0']
|
24
24
|
s.add_dependency 'hairtrigger'
|
25
25
|
s.add_dependency 'mysql2'
|
26
26
|
s.add_dependency 'pg'
|
27
|
+
s.add_dependency 'terminal-table'
|
27
28
|
s.add_dependency 'rubyXL', ['>= 3.4.0']
|
28
29
|
s.add_development_dependency 'pry-byebug'
|
29
30
|
s.add_development_dependency 'rspec', '~> 3.9'
|
data/lib/breathing.rb
CHANGED
@@ -4,6 +4,7 @@ require 'breathing/installer'
|
|
4
4
|
require 'breathing/trigger'
|
5
5
|
require 'breathing/change_log'
|
6
6
|
require 'breathing/excel'
|
7
|
+
require 'breathing/terminal_table'
|
7
8
|
|
8
9
|
module Breathing
|
9
10
|
VERSION = Gem.loaded_specs['breathing'].version.to_s
|
@@ -28,5 +29,24 @@ module Breathing
|
|
28
29
|
ActiveRecord::Base.establish_connection
|
29
30
|
Breathing::Excel.new.create
|
30
31
|
end
|
32
|
+
|
33
|
+
def render_terminal_table(table_name:, id: 1)
|
34
|
+
ActiveRecord::Base.establish_connection
|
35
|
+
puts Breathing::TerminalTable.new(table_name).render(id: id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def tail_f(table_name:, id: 1)
|
39
|
+
ActiveRecord::Base.establish_connection
|
40
|
+
table = Breathing::TerminalTable.new(table_name)
|
41
|
+
|
42
|
+
loop do
|
43
|
+
text = table.render(id: id)
|
44
|
+
if text.present?
|
45
|
+
puts text
|
46
|
+
id = table.last_id + 1
|
47
|
+
end
|
48
|
+
sleep 5
|
49
|
+
end
|
50
|
+
end
|
31
51
|
end
|
32
52
|
end
|
data/lib/breathing/change_log.rb
CHANGED
@@ -12,5 +12,39 @@ module Breathing
|
|
12
12
|
names = before_data.keys.present? ? before_data.keys : after_data.keys
|
13
13
|
names.reject { |name| name == 'id' }
|
14
14
|
end
|
15
|
+
|
16
|
+
def data
|
17
|
+
action == 'DELETE' ? before_data : after_data
|
18
|
+
end
|
19
|
+
|
20
|
+
def data_attributes
|
21
|
+
data_column_names.each.with_object('change_logs.id' => id,
|
22
|
+
'change_logs.created_at' => created_at.to_s(:db),
|
23
|
+
'action' => action,
|
24
|
+
'id' => transaction_id) do |name, hash|
|
25
|
+
hash[name] = data[name]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def diff
|
30
|
+
return nil if action != 'UPDATE'
|
31
|
+
|
32
|
+
changed_attribute_columns.each.with_object({}) do |column_name, diff_hash|
|
33
|
+
diff_hash[column_name] = {before_data[column_name] => after_data[column_name]}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def attributes_for_excel
|
38
|
+
{
|
39
|
+
'change_logs.id' => id,
|
40
|
+
'created_at' => created_at.to_s(:db),
|
41
|
+
'table_name' => table_name,
|
42
|
+
'action' => action,
|
43
|
+
'id' => transaction_id,
|
44
|
+
'diff' => diff.to_s,
|
45
|
+
'before_data' => before_data.to_s,
|
46
|
+
'after_data' => after_data.to_s,
|
47
|
+
}
|
48
|
+
end
|
15
49
|
end
|
16
50
|
end
|
data/lib/breathing/cli.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'thor'
|
2
3
|
require 'breathing'
|
3
4
|
|
@@ -6,26 +7,51 @@ module Breathing
|
|
6
7
|
default_command :export
|
7
8
|
|
8
9
|
desc 'install', 'Create table change_logs and create triggers'
|
10
|
+
|
9
11
|
def install
|
10
12
|
Breathing.install
|
11
13
|
end
|
12
14
|
|
13
15
|
desc 'uninstall', 'Drop table change_logs and drop triggers'
|
16
|
+
|
14
17
|
def uninstall
|
15
18
|
Breathing.uninstall
|
16
19
|
end
|
17
20
|
|
18
21
|
desc 'clear', 'Delete all records in change_logs table'
|
22
|
+
|
19
23
|
def clear
|
20
24
|
Breathing.clear
|
21
25
|
end
|
22
26
|
|
23
27
|
desc 'export', 'output xlsx'
|
28
|
+
|
24
29
|
def export
|
25
30
|
Breathing.export
|
26
31
|
end
|
27
32
|
|
33
|
+
desc 'out', 'output stdout'
|
34
|
+
method_option :type, aliases: '-t', default: 'terminal_table', type: :string
|
35
|
+
method_option :table, type: :string, required: true
|
36
|
+
method_option :id, default: 1, type: :numeric
|
37
|
+
def out
|
38
|
+
if options[:table] == 'terminal_table'
|
39
|
+
Breathing.render_terminal_table(table_name: options[:table], id: options[:id].to_i)
|
40
|
+
else
|
41
|
+
# TODO
|
42
|
+
# Breathing.export(table_name: options[:table], id: options[:id].to_i)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'tail', 'tail terminal_table'
|
47
|
+
method_option :table, type: :string, required: true
|
48
|
+
method_option :id, default: 1, type: :numeric
|
49
|
+
def tail
|
50
|
+
Breathing.tail_f(table_name: options[:table], id: options[:id].to_i)
|
51
|
+
end
|
52
|
+
|
28
53
|
desc 'version', 'Show Version'
|
54
|
+
|
29
55
|
def version
|
30
56
|
say "Version: #{Breathing::VERSION}"
|
31
57
|
end
|
data/lib/breathing/excel.rb
CHANGED
@@ -11,64 +11,82 @@ module Breathing
|
|
11
11
|
def create(id: 1, file_name: 'breathing.xlsx')
|
12
12
|
sheet = @workbook[0]
|
13
13
|
table_names = Breathing::ChangeLog.where('id >= ?', id).group(:table_name).pluck(:table_name)
|
14
|
-
table_names.each do |table_name|
|
14
|
+
table_names.sort.each do |table_name|
|
15
15
|
if sheet.sheet_name == 'Sheet1'
|
16
16
|
sheet.sheet_name = table_name
|
17
17
|
else
|
18
18
|
sheet = @workbook.add_worksheet(table_name)
|
19
19
|
end
|
20
20
|
|
21
|
-
rows = Breathing::ChangeLog.where(table_name: table_name).where('id >= ?', id).order(:id)
|
21
|
+
rows = Breathing::ChangeLog.where(table_name: table_name).where('id >= ?', id).order(:id).to_a
|
22
22
|
|
23
|
-
if
|
24
|
-
|
25
|
-
|
23
|
+
next if rows.size.zero?
|
24
|
+
|
25
|
+
add_header_row(sheet, rows.first)
|
26
26
|
add_body_rows(sheet, rows)
|
27
27
|
add_style(sheet)
|
28
28
|
end
|
29
29
|
|
30
|
+
add_change_logs_sheet(id) if table_names.size.positive?
|
31
|
+
|
30
32
|
@workbook.write(file_name)
|
31
33
|
end
|
32
34
|
|
33
35
|
private
|
34
36
|
|
35
37
|
def add_header_row(sheet, row)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
sheet.add_cell(0, i, column_name).change_font_bold(true)
|
38
|
+
header_color = 'ddedf3' # blue
|
39
|
+
row.data_attributes.keys.each.with_index do |header_column, column_index|
|
40
|
+
cell = sheet.add_cell(0, column_index, header_column)
|
41
|
+
cell.change_fill(header_color)
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
45
|
def add_body_rows(sheet, rows)
|
45
|
-
rows.each.with_index(1) do |row,
|
46
|
-
|
47
|
-
|
48
|
-
sheet.add_cell(i, 2, row.transaction_id)
|
49
|
-
row.data_column_names.each.with_index(3) do |column_name, j|
|
50
|
-
data = row.action == 'DELETE' ? row.before_data : row.after_data
|
51
|
-
cell_object = sheet.add_cell(i, j, data[column_name])
|
46
|
+
rows.each.with_index(1) do |row, row_number|
|
47
|
+
row.data_attributes.each.with_index do |(column_name, value), column_index|
|
48
|
+
cell = sheet.add_cell(row_number, column_index, value)
|
52
49
|
if row.action == 'UPDATE' && column_name != 'updated_at' && row.changed_attribute_columns.include?(column_name)
|
53
|
-
|
50
|
+
cell.change_fill('ffff00') # color: yellow
|
54
51
|
elsif row.action == 'DELETE'
|
55
|
-
|
52
|
+
cell.change_fill('d9d9d9') # color: grey
|
56
53
|
end
|
57
54
|
end
|
58
55
|
end
|
59
56
|
end
|
60
57
|
|
61
58
|
def add_style(sheet)
|
62
|
-
sheet.sheet_data.rows.each.with_index do |row,
|
63
|
-
row.cells.each do |cell|
|
59
|
+
sheet.sheet_data.rows.each.with_index do |row, row_index|
|
60
|
+
row.cells.each.with_index do |cell, column_index|
|
64
61
|
%i[top bottom left right].each do |direction|
|
65
62
|
cell.change_border(direction, 'thin')
|
66
63
|
end
|
64
|
+
cell.change_border(:bottom, 'double') if row_index.zero?
|
67
65
|
|
68
|
-
cell.
|
66
|
+
cell_width = cell.value.to_s.size + 2
|
67
|
+
sheet.change_column_width(column_index, cell_width) if cell_width > sheet.get_column_width(column_index)
|
69
68
|
end
|
70
69
|
end
|
70
|
+
|
71
71
|
sheet.change_row_horizontal_alignment(0, 'center')
|
72
72
|
end
|
73
|
+
|
74
|
+
def add_change_logs_sheet(id)
|
75
|
+
sheet = @workbook.add_worksheet(Breathing::ChangeLog.table_name)
|
76
|
+
|
77
|
+
change_logs = Breathing::ChangeLog.where('id >= ?', id).order(:id)
|
78
|
+
change_logs.first.attributes_for_excel.keys.each.with_index do |header_column, column_index|
|
79
|
+
cell = sheet.add_cell(0, column_index, header_column)
|
80
|
+
cell.change_fill('ddedf3') # blue
|
81
|
+
end
|
82
|
+
|
83
|
+
change_logs.each.with_index(1) do |change_log, row_number|
|
84
|
+
change_log.attributes_for_excel.each.with_index do |(_column_name, value), column_index|
|
85
|
+
sheet.add_cell(row_number, column_index, value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
add_style(sheet)
|
90
|
+
end
|
73
91
|
end
|
74
92
|
end
|
data/lib/breathing/installer.rb
CHANGED
@@ -8,9 +8,9 @@ module Breathing
|
|
8
8
|
|
9
9
|
class Installer
|
10
10
|
def install
|
11
|
-
raise Breathing::UnsupportedError, "Version MySQL 5.6 is not supported." unless
|
11
|
+
raise Breathing::UnsupportedError, "Version MySQL 5.6 is not supported." unless database_supported_version?
|
12
12
|
|
13
|
-
create_log_table
|
13
|
+
create_log_table
|
14
14
|
|
15
15
|
models.each do |model|
|
16
16
|
column_names = model.columns.map(&:name)
|
@@ -21,14 +21,13 @@ module Breathing
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def uninstall
|
24
|
-
drop_log_table
|
25
|
-
|
24
|
+
drop_log_table
|
26
25
|
models.each { |model| Breathing::Trigger.new(model, log_table_name).drop }
|
27
26
|
end
|
28
27
|
|
29
28
|
private
|
30
29
|
|
31
|
-
def
|
30
|
+
def database_supported_version?
|
32
31
|
connection = ActiveRecord::Base.connection
|
33
32
|
connection.adapter_name == "PostgreSQL" || (connection.adapter_name == 'Mysql2' && connection.raw_connection.info[:version].to_f >= 5.7)
|
34
33
|
end
|
@@ -39,26 +38,22 @@ module Breathing
|
|
39
38
|
|
40
39
|
def create_log_table(table_name: log_table_name)
|
41
40
|
ActiveRecord::Schema.define version: 0 do
|
42
|
-
create_table table_name,
|
43
|
-
t.
|
44
|
-
t.string
|
45
|
-
t.string
|
46
|
-
t.
|
47
|
-
t.json
|
48
|
-
t.
|
41
|
+
create_table table_name, if_not_exists: true do |t|
|
42
|
+
t.datetime :created_at, null: false, index: true
|
43
|
+
t.string :table_name, null: false
|
44
|
+
t.string :action, null: false
|
45
|
+
t.string :transaction_id, null: false
|
46
|
+
t.json :before_data, null: false
|
47
|
+
t.json :after_data, null: false
|
48
|
+
|
49
49
|
t.index %w[table_name transaction_id]
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
def drop_log_table
|
55
|
-
|
56
|
-
|
57
|
-
ActiveRecord::Base.connection.execute(sql)
|
58
|
-
end
|
59
|
-
|
60
|
-
def log_table_exists?
|
61
|
-
ActiveRecord::Base.connection.table_exists?(log_table_name)
|
55
|
+
puts "DROP TABLE #{log_table_name}"
|
56
|
+
ActiveRecord::Base.connection.drop_table(log_table_name, if_exists: true)
|
62
57
|
end
|
63
58
|
|
64
59
|
def models
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'breathing'
|
2
|
+
require 'terminal-table'
|
3
|
+
|
4
|
+
module Breathing
|
5
|
+
class TerminalTable
|
6
|
+
attr_reader :last_id
|
7
|
+
|
8
|
+
def initialize(table_name)
|
9
|
+
@last_id = 1
|
10
|
+
@table_name = table_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def render(id: 1)
|
14
|
+
rows = Breathing::ChangeLog.where(table_name: @table_name).where("id >= ? ", id).order(:id)
|
15
|
+
|
16
|
+
return if rows.size.zero?
|
17
|
+
|
18
|
+
@table = Terminal::Table.new(title: rows.first.table_name,
|
19
|
+
headings: rows.first.data_attributes.keys,
|
20
|
+
rows: rows.map { |row| row.data_attributes.values })
|
21
|
+
|
22
|
+
@last_id = rows.last.id
|
23
|
+
@table.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def rows
|
27
|
+
@table.rows
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/breathing/trigger.rb
CHANGED
@@ -11,59 +11,25 @@ module Breathing
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def create
|
14
|
-
|
15
|
-
|
16
|
-
unless exists?(trigger_name)
|
17
|
-
puts "CREATE TRIGGER #{trigger_name}"
|
14
|
+
exists_trigger_names = ActiveRecord::Base.connection.triggers.keys
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
INSERT INTO #{log_table_name} (action, table_name, transaction_id, before_data, after_data, created_at)
|
22
|
-
VALUES ('INSERT', '#{model.table_name}', NEW.id,
|
23
|
-
'{}',
|
24
|
-
#{row_to_json(model.columns, 'NEW')},
|
25
|
-
CURRENT_TIMESTAMP);
|
26
|
-
SQL
|
27
|
-
end
|
28
|
-
end
|
16
|
+
trigger_name = "#{log_table_name}_insert_#{model.table_name}"
|
17
|
+
create_insert_trigger(trigger_name, model) if exists_trigger_names.exclude?(trigger_name)
|
29
18
|
|
30
19
|
trigger_name = "#{log_table_name}_update_#{model.table_name}"
|
31
|
-
|
32
|
-
puts "CREATE TRIGGER #{trigger_name}"
|
33
|
-
|
34
|
-
ActiveRecord::Base.connection.create_trigger(trigger_name).on(model.table_name).before(:update).of(:updated_at) do
|
35
|
-
<<-SQL
|
36
|
-
INSERT INTO #{log_table_name} (action, table_name, transaction_id, before_data, after_data, created_at)
|
37
|
-
VALUES ('UPDATE', '#{model.table_name}', NEW.id,
|
38
|
-
#{row_to_json(model.columns, 'OLD')},
|
39
|
-
#{row_to_json(model.columns, 'NEW')},
|
40
|
-
CURRENT_TIMESTAMP);
|
41
|
-
SQL
|
42
|
-
end
|
43
|
-
end
|
20
|
+
create_update_trigger(trigger_name, model) if exists_trigger_names.exclude?(trigger_name)
|
44
21
|
|
45
22
|
trigger_name = "#{log_table_name}_delete_#{model.table_name}"
|
46
|
-
|
47
|
-
puts "CREATE TRIGGER #{trigger_name}"
|
48
|
-
ActiveRecord::Base.connection.create_trigger(trigger_name).on(model.table_name).after(:delete) do
|
49
|
-
<<-SQL
|
50
|
-
INSERT INTO #{log_table_name} (action, table_name, transaction_id, before_data, after_data, created_at)
|
51
|
-
VALUES ('DELETE', '#{model.table_name}', OLD.id,
|
52
|
-
#{row_to_json(model.columns, 'OLD')},
|
53
|
-
'{}',
|
54
|
-
CURRENT_TIMESTAMP);
|
55
|
-
SQL
|
56
|
-
end
|
57
|
-
end
|
23
|
+
create_delete_trigger(trigger_name, model) if exists_trigger_names.exclude?(trigger_name)
|
58
24
|
end
|
59
25
|
|
60
26
|
def drop
|
61
|
-
%w[insert update delete].
|
62
|
-
trigger_name = "#{log_table_name}_#{action}_#{model.table_name}"
|
27
|
+
trigger_names = %w[insert update delete].map { |action| "#{log_table_name}_#{action}_#{model.table_name}" }
|
63
28
|
|
29
|
+
trigger_names.each do |trigger_name|
|
64
30
|
begin
|
65
31
|
sql = "DROP TRIGGER IF EXISTS #{trigger_name}"
|
66
|
-
if
|
32
|
+
if postgresql?
|
67
33
|
sql << " ON #{model.table_name} CASCADE;"
|
68
34
|
sql << " DROP FUNCTION IF EXISTS #{trigger_name} CASCADE;"
|
69
35
|
end
|
@@ -77,12 +43,50 @@ module Breathing
|
|
77
43
|
|
78
44
|
private
|
79
45
|
|
80
|
-
def
|
81
|
-
ActiveRecord::Base.connection.
|
46
|
+
def postgresql?
|
47
|
+
ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_trigger(name)
|
51
|
+
puts "CREATE TRIGGER #{name}"
|
52
|
+
ActiveRecord::Base.connection.create_trigger(name) # hairtrigger gem
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_insert_trigger(trigger_name, model)
|
56
|
+
create_trigger(trigger_name).on(model.table_name).after(:insert) do
|
57
|
+
<<-SQL
|
58
|
+
INSERT INTO #{log_table_name} (created_at, action, table_name, transaction_id, before_data, after_data)
|
59
|
+
VALUES (CURRENT_TIMESTAMP, 'INSERT', '#{model.table_name}', NEW.id,
|
60
|
+
'{}',
|
61
|
+
#{row_to_json(model.columns, 'NEW')});
|
62
|
+
SQL
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_update_trigger(trigger_name, model)
|
67
|
+
create_trigger(trigger_name).on(model.table_name).before(:update).of(:updated_at) do
|
68
|
+
<<-SQL
|
69
|
+
INSERT INTO #{log_table_name} (created_at, action, table_name, transaction_id, before_data, after_data)
|
70
|
+
VALUES (CURRENT_TIMESTAMP, 'UPDATE', '#{model.table_name}', NEW.id,
|
71
|
+
#{row_to_json(model.columns, 'OLD')},
|
72
|
+
#{row_to_json(model.columns, 'NEW')});
|
73
|
+
SQL
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_delete_trigger(trigger_name, model)
|
78
|
+
create_trigger(trigger_name).on(model.table_name).after(:delete) do
|
79
|
+
<<-SQL
|
80
|
+
INSERT INTO #{log_table_name} (created_at, action, table_name, transaction_id, before_data, after_data)
|
81
|
+
VALUES (CURRENT_TIMESTAMP, 'DELETE', '#{model.table_name}', OLD.id,
|
82
|
+
#{row_to_json(model.columns, 'OLD')},
|
83
|
+
'{}');
|
84
|
+
SQL
|
85
|
+
end
|
82
86
|
end
|
83
87
|
|
84
88
|
def row_to_json(columns, state)
|
85
|
-
if
|
89
|
+
if postgresql?
|
86
90
|
"row_to_json(#{state}.*)"
|
87
91
|
else
|
88
92
|
json_object_values = columns.each.with_object([]) do |column, array|
|
@@ -23,13 +23,18 @@ describe Breathing::Excel do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'multi sheets' do
|
26
|
-
User.create!(name: 'a', age: 20)
|
26
|
+
user = User.create!(name: 'a', age: 20)
|
27
|
+
user.update!(age: 21)
|
28
|
+
user.destroy!
|
27
29
|
Department.create!(name: 'a')
|
28
30
|
|
29
31
|
Tempfile.open(['tmp', '.xlsx']) do |file|
|
30
32
|
Breathing::Excel.new.create(file_name: file.path)
|
31
33
|
workbook = RubyXL::Parser.parse(file.path)
|
32
|
-
expect(workbook.sheets.
|
34
|
+
expect(workbook.sheets.map(&:name)).to eq(%w[departments users change_logs])
|
35
|
+
change_logs_sheet = workbook.worksheets.last
|
36
|
+
|
37
|
+
expect(change_logs_sheet.sheet_data.size).to eq(Breathing::ChangeLog.count + 1)
|
33
38
|
end
|
34
39
|
end
|
35
40
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Breathing::TerminalTable do
|
4
|
+
describe '#render' do
|
5
|
+
before { Breathing::Installer.new.install }
|
6
|
+
after do
|
7
|
+
Breathing::Installer.new.uninstall if ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
8
|
+
end
|
9
|
+
|
10
|
+
it do
|
11
|
+
user = User.create!(name: 'a', age: 20)
|
12
|
+
user.update!(age: 21)
|
13
|
+
user.destroy!
|
14
|
+
expect(Breathing::ChangeLog.count).to eq(3)
|
15
|
+
|
16
|
+
table = Breathing::TerminalTable.new(:users)
|
17
|
+
puts table.render(id: 1)
|
18
|
+
|
19
|
+
expect(table.rows.size).to eq(3)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: breathing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Akira Kusumoto
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 6.0.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 6.0.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: hairtrigger
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: terminal-table
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rubyXL
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,9 +157,11 @@ files:
|
|
143
157
|
- lib/breathing/cli.rb
|
144
158
|
- lib/breathing/excel.rb
|
145
159
|
- lib/breathing/installer.rb
|
160
|
+
- lib/breathing/terminal_table.rb
|
146
161
|
- lib/breathing/trigger.rb
|
147
162
|
- spec/app.rb
|
148
163
|
- spec/breathing/excel_spec.rb
|
164
|
+
- spec/breathing/terminal_table_spec.rb
|
149
165
|
- spec/breathing_spec.rb
|
150
166
|
- spec/database.yml
|
151
167
|
- spec/spec_helper.rb
|
@@ -175,6 +191,7 @@ summary: Audit logging for database
|
|
175
191
|
test_files:
|
176
192
|
- spec/app.rb
|
177
193
|
- spec/breathing/excel_spec.rb
|
194
|
+
- spec/breathing/terminal_table_spec.rb
|
178
195
|
- spec/breathing_spec.rb
|
179
196
|
- spec/database.yml
|
180
197
|
- spec/spec_helper.rb
|