breathing 0.0.1 → 0.0.2
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/.circleci/config.yml +53 -0
- data/README.md +14 -4
- data/breathing.gemspec +4 -3
- data/lib/breathing.rb +33 -1
- data/lib/breathing/change_log.rb +16 -0
- data/lib/breathing/cli.rb +22 -2
- data/lib/breathing/excel.rb +74 -0
- data/lib/breathing/installer.rb +71 -0
- data/lib/breathing/trigger.rb +100 -0
- data/spec/app.rb +8 -0
- data/spec/breathing/excel_spec.rb +34 -0
- data/spec/breathing_spec.rb +33 -0
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b0a34aef932a21f7369a766114261c484fcc68cdb32ec50e81c721da2f13d0a
|
4
|
+
data.tar.gz: eb6c8c5370a13e422674c9256032eeb650a4a58e018eed78f33c234a7a9cbc41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91a00104ce4496d7250df3664624cd3cd5fb1b6af4d48f57445fbd7f0e798a4e4dd2003d0a2cfc3b1bc1e31d4729971abcfc0086701208a37b1a1975522527da
|
7
|
+
data.tar.gz: 4dc36063b0e355d5f245660136b2c439bacf16bf2da61fe899ee05b19a51f17c09e2036e1f6aa4505671c1f89cd4f4c637f0f58baf1adf0d1776bf6379594ccf
|
@@ -0,0 +1,53 @@
|
|
1
|
+
version: 2.1
|
2
|
+
|
3
|
+
executors:
|
4
|
+
default:
|
5
|
+
working_directory: ~/app
|
6
|
+
docker:
|
7
|
+
- image: circleci/ruby:2.6
|
8
|
+
environment:
|
9
|
+
DB_USER: 'root'
|
10
|
+
DB_PASS: 'root'
|
11
|
+
DB_HOST: '127.0.0.1'
|
12
|
+
- image: circleci/mysql:8-ram
|
13
|
+
environment:
|
14
|
+
MYSQL_USER: root
|
15
|
+
MYSQL_ROOT_PASSWORD: root
|
16
|
+
MYSQL_DATABASE: breathing_test
|
17
|
+
command: [--default-authentication-plugin=mysql_native_password]
|
18
|
+
|
19
|
+
commands:
|
20
|
+
setup_bundle:
|
21
|
+
steps:
|
22
|
+
- restore_cache:
|
23
|
+
key: bundle-{{ checksum "breathing.gemspec" }}
|
24
|
+
- run:
|
25
|
+
name: install dependencies
|
26
|
+
command: |
|
27
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
28
|
+
- save_cache:
|
29
|
+
key: bundle-{{ checksum "breathing.gemspec" }}
|
30
|
+
paths:
|
31
|
+
- vendor/bundle
|
32
|
+
|
33
|
+
wait_for_db:
|
34
|
+
steps:
|
35
|
+
- run:
|
36
|
+
name: Wait for DB
|
37
|
+
command: dockerize -wait tcp://127.0.0.1:3306 -timeout 1m
|
38
|
+
|
39
|
+
jobs:
|
40
|
+
test:
|
41
|
+
executor: default
|
42
|
+
steps:
|
43
|
+
- checkout
|
44
|
+
- setup_bundle
|
45
|
+
- wait_for_db
|
46
|
+
- run: bundle exec rspec ./spec
|
47
|
+
|
48
|
+
workflows:
|
49
|
+
version: 2
|
50
|
+
|
51
|
+
test:
|
52
|
+
jobs:
|
53
|
+
- test
|
data/README.md
CHANGED
@@ -28,9 +28,9 @@ Just run the following command.
|
|
28
28
|
|
29
29
|
- Create table `change_logs`
|
30
30
|
- Create triggers
|
31
|
-
-
|
32
|
-
-
|
33
|
-
-
|
31
|
+
- change_logs_insert_{table_name}
|
32
|
+
- change_logs_update_{table_name}
|
33
|
+
- change_logs_delete_{table_name}
|
34
34
|
|
35
35
|
### Uninstall
|
36
36
|
|
@@ -40,7 +40,17 @@ Cleanup command.
|
|
40
40
|
% DATABASE_URL="mysql2://user:pass@host:port/database" breathing uninstall
|
41
41
|
```
|
42
42
|
|
43
|
-
- Drop table `change_logs`
|
43
|
+
- Drop table `change_logs`
|
44
|
+
- Drop triggers
|
45
|
+
- change_logs_insert_{table_name}
|
46
|
+
- change_logs_update_{table_name}
|
47
|
+
- change_logs_delete_{table_name}
|
48
|
+
|
49
|
+
### export
|
50
|
+
|
51
|
+
```
|
52
|
+
% DATABASE_URL="mysql2://user:pass@host:port/database" breathing export
|
53
|
+
```
|
44
54
|
|
45
55
|
## Copyright
|
46
56
|
|
data/breathing.gemspec
CHANGED
@@ -2,13 +2,13 @@ $:.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.2'
|
6
6
|
s.platform = Gem::Platform::RUBY
|
7
7
|
s.authors = ['Akira Kusumoto']
|
8
8
|
s.email = ['akirakusumo10@gmail.com']
|
9
9
|
s.homepage = 'https://github.com/bluerabbit/breathing'
|
10
|
-
s.summary = '
|
11
|
-
s.description = '
|
10
|
+
s.summary = 'Audit logging for database'
|
11
|
+
s.description = 'Audit logging for database'
|
12
12
|
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_runtime_dependency 'thor'
|
22
22
|
|
23
23
|
s.add_dependency 'activerecord', ['>= 5.0.0']
|
24
|
+
s.add_dependency 'rubyXL', ['>= 3.4.0']
|
24
25
|
s.add_development_dependency 'mysql2'
|
25
26
|
s.add_development_dependency 'pry-byebug'
|
26
27
|
s.add_development_dependency 'rspec', '~> 3.9'
|
data/lib/breathing.rb
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
-
|
1
|
+
require 'active_record'
|
2
|
+
require 'breathing/installer'
|
3
|
+
require 'breathing/trigger'
|
4
|
+
require 'breathing/change_log'
|
5
|
+
require 'breathing/excel'
|
6
|
+
|
7
|
+
module Breathing
|
2
8
|
VERSION = Gem.loaded_specs['breathing'].version.to_s
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def install
|
12
|
+
establish_connection
|
13
|
+
Breathing::Installer.new.install
|
14
|
+
end
|
15
|
+
|
16
|
+
def uninstall
|
17
|
+
establish_connection
|
18
|
+
Breathing::Installer.new.uninstall
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear
|
22
|
+
establish_connection
|
23
|
+
Breathing::ChangeLog.delete_all
|
24
|
+
end
|
25
|
+
|
26
|
+
def export
|
27
|
+
establish_connection
|
28
|
+
Breathing::Excel.new.create
|
29
|
+
end
|
30
|
+
|
31
|
+
def establish_connection
|
32
|
+
ActiveRecord::Base.establish_connection(url: ENV['DATABASE_URL'])
|
33
|
+
end
|
34
|
+
end
|
3
35
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Breathing
|
4
|
+
class ChangeLog < ActiveRecord::Base
|
5
|
+
def changed_attribute_columns
|
6
|
+
before_data.each.with_object([]) do |(column, value), columns|
|
7
|
+
columns << column if after_data[column] != value
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def data_column_names
|
12
|
+
names = before_data.keys.present? ? before_data.keys : after_data.keys
|
13
|
+
names.reject { |name| name == 'id' }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/breathing/cli.rb
CHANGED
@@ -1,9 +1,29 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'breathing'
|
3
3
|
|
4
|
-
|
4
|
+
module Breathing
|
5
5
|
class Cli < Thor
|
6
|
-
default_command :
|
6
|
+
default_command :install
|
7
|
+
|
8
|
+
desc 'install', 'Create table change_logs and create triggers'
|
9
|
+
def install
|
10
|
+
Breathing.install
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'uninstall', 'Drop table change_logs and drop triggers'
|
14
|
+
def uninstall
|
15
|
+
Breathing.uninstall
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'clear', 'Delete all records in change_logs table'
|
19
|
+
def clear
|
20
|
+
Breathing.clear
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'export', 'output xlsx'
|
24
|
+
def export
|
25
|
+
Breathing.export
|
26
|
+
end
|
7
27
|
|
8
28
|
desc 'version', 'Show Version'
|
9
29
|
def version
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rubyXL'
|
2
|
+
require 'rubyXL/convenience_methods'
|
3
|
+
require 'breathing'
|
4
|
+
|
5
|
+
module Breathing
|
6
|
+
class Excel
|
7
|
+
def initialize
|
8
|
+
@workbook = RubyXL::Workbook.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(id: 1, file_name: 'breathing.xlsx')
|
12
|
+
sheet = @workbook[0]
|
13
|
+
table_names = Breathing::ChangeLog.where('id >= ?', id).group(:table_name).pluck(:table_name)
|
14
|
+
table_names.each do |table_name|
|
15
|
+
if sheet.sheet_name == 'Sheet1'
|
16
|
+
sheet.sheet_name = table_name
|
17
|
+
else
|
18
|
+
sheet = @workbook.add_worksheet(table_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
rows = Breathing::ChangeLog.where(table_name: table_name).where('id >= ?', id).order(:id)
|
22
|
+
|
23
|
+
if first_row = rows.first
|
24
|
+
add_header_row(sheet, first_row)
|
25
|
+
end
|
26
|
+
add_body_rows(sheet, rows)
|
27
|
+
add_style(sheet)
|
28
|
+
end
|
29
|
+
|
30
|
+
@workbook.write(file_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def add_header_row(sheet, row)
|
36
|
+
sheet.add_cell(0, 0, 'change_logs.id').change_font_bold(true)
|
37
|
+
sheet.add_cell(0, 1, 'action').change_font_bold(true)
|
38
|
+
sheet.add_cell(0, 2, 'id').change_font_bold(true)
|
39
|
+
row.data_column_names.each.with_index(3) do |column_name, i|
|
40
|
+
sheet.add_cell(0, i, column_name).change_font_bold(true)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_body_rows(sheet, rows)
|
45
|
+
rows.each.with_index(1) do |row, i|
|
46
|
+
sheet.add_cell(i, 0, row.id)
|
47
|
+
sheet.add_cell(i, 1, row.action)
|
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])
|
52
|
+
if row.action == 'UPDATE' && column_name != 'updated_at' && row.changed_attribute_columns.include?(column_name)
|
53
|
+
cell_object.change_fill('ffff00') # color: yellow
|
54
|
+
elsif row.action == 'DELETE'
|
55
|
+
cell_object.change_fill('d9d9d9') # color: grey
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_style(sheet)
|
62
|
+
sheet.sheet_data.rows.each.with_index do |row, i|
|
63
|
+
row.cells.each do |cell|
|
64
|
+
%i[top bottom left right].each do |direction|
|
65
|
+
cell.change_border(direction, 'thin')
|
66
|
+
end
|
67
|
+
|
68
|
+
cell.change_border(:bottom, 'medium') if i.zero?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
sheet.change_row_horizontal_alignment(0, 'center')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'breathing'
|
3
|
+
require 'breathing/trigger'
|
4
|
+
require 'breathing/change_log'
|
5
|
+
|
6
|
+
module Breathing
|
7
|
+
class Installer
|
8
|
+
def install
|
9
|
+
create_log_table unless log_table_exists?
|
10
|
+
|
11
|
+
models.each do |model|
|
12
|
+
column_names = model.columns.map(&:name)
|
13
|
+
if column_names.include?('id') && column_names.include?('updated_at')
|
14
|
+
Breathing::Trigger.new(model, log_table_name).create
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def uninstall
|
20
|
+
drop_log_table if log_table_exists?
|
21
|
+
|
22
|
+
models.each { |model| Breathing::Trigger.new(model, log_table_name).drop }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def log_table_name
|
28
|
+
Breathing::ChangeLog.table_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_log_table(table_name: log_table_name)
|
32
|
+
ActiveRecord::Schema.define version: 0 do
|
33
|
+
create_table table_name, force: false do |t|
|
34
|
+
t.string :action, null: false
|
35
|
+
t.string :table_name, null: false
|
36
|
+
t.string :transaction_id, null: false
|
37
|
+
t.json :before_data, null: false
|
38
|
+
t.json :after_data, null: false
|
39
|
+
t.datetime :created_at, null: false, index: true
|
40
|
+
t.index %w[table_name transaction_id]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def drop_log_table
|
46
|
+
sql = "DROP TABLE #{log_table_name}"
|
47
|
+
puts sql
|
48
|
+
ActiveRecord::Base.connection.execute(sql)
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_table_exists?
|
52
|
+
ActiveRecord::Base.connection.table_exists?(log_table_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def models
|
56
|
+
ignores = %w[schema_migrations ar_internal_metadata] << log_table_name
|
57
|
+
|
58
|
+
ActiveRecord::Base.connection.tables.each do |table_name|
|
59
|
+
next if ignores.include?(table_name) || Object.const_defined?(table_name.classify)
|
60
|
+
|
61
|
+
eval <<-EOS
|
62
|
+
class #{table_name.classify} < ActiveRecord::Base
|
63
|
+
self.table_name = :#{table_name}
|
64
|
+
end
|
65
|
+
EOS
|
66
|
+
end
|
67
|
+
|
68
|
+
ActiveRecord::Base.descendants.reject(&:abstract_class).reject { |m| ignores.include? m.table_name }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'breathing/change_log'
|
3
|
+
|
4
|
+
module Breathing
|
5
|
+
class Trigger
|
6
|
+
attr_reader :model, :log_table_name
|
7
|
+
|
8
|
+
def initialize(model, log_table_name)
|
9
|
+
@model = model
|
10
|
+
@log_table_name = log_table_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def create
|
14
|
+
trigger_name = "#{log_table_name}_insert_#{model.table_name}"
|
15
|
+
unless exists?(trigger_name)
|
16
|
+
puts "CREATE TRIGGER #{trigger_name}"
|
17
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
18
|
+
CREATE TRIGGER #{trigger_name}
|
19
|
+
AFTER INSERT
|
20
|
+
ON #{model.table_name}
|
21
|
+
FOR EACH ROW
|
22
|
+
BEGIN
|
23
|
+
INSERT INTO #{log_table_name} (`action`, `table_name`, `transaction_id`, `before_data`, `after_data`, `created_at`)
|
24
|
+
VALUES ('INSERT', '#{model.table_name}', NEW.id,
|
25
|
+
JSON_OBJECT(),
|
26
|
+
JSON_OBJECT(#{json_object_values(model.columns, 'NEW')}),
|
27
|
+
CURRENT_TIMESTAMP);
|
28
|
+
END;
|
29
|
+
SQL
|
30
|
+
end
|
31
|
+
|
32
|
+
trigger_name = "#{log_table_name}_update_#{model.table_name}"
|
33
|
+
unless exists?(trigger_name)
|
34
|
+
puts "CREATE TRIGGER #{trigger_name}"
|
35
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
36
|
+
CREATE TRIGGER #{trigger_name}
|
37
|
+
BEFORE UPDATE
|
38
|
+
ON #{model.table_name}
|
39
|
+
FOR EACH ROW
|
40
|
+
BEGIN
|
41
|
+
IF (OLD.updated_at != NEW.updated_at) THEN
|
42
|
+
INSERT INTO #{log_table_name} (`action`, `table_name`, `transaction_id`, `before_data`, `after_data`, `created_at`)
|
43
|
+
VALUES ('UPDATE', '#{model.table_name}', NEW.id,
|
44
|
+
JSON_OBJECT(#{json_object_values(model.columns, 'OLD')}),
|
45
|
+
JSON_OBJECT(#{json_object_values(model.columns, 'NEW')}),
|
46
|
+
CURRENT_TIMESTAMP);
|
47
|
+
end if;
|
48
|
+
END;
|
49
|
+
SQL
|
50
|
+
end
|
51
|
+
|
52
|
+
trigger_name = "#{log_table_name}_delete_#{model.table_name}"
|
53
|
+
unless exists?(trigger_name)
|
54
|
+
puts "CREATE TRIGGER #{trigger_name}"
|
55
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
56
|
+
CREATE TRIGGER #{trigger_name}
|
57
|
+
AFTER DELETE
|
58
|
+
ON #{model.table_name}
|
59
|
+
FOR EACH ROW
|
60
|
+
BEGIN
|
61
|
+
INSERT INTO #{log_table_name} (`action`, `table_name`, `transaction_id`, `before_data`, `after_data`, `created_at`)
|
62
|
+
VALUES ('DELETE', '#{model.table_name}', OLD.id,
|
63
|
+
JSON_OBJECT(#{json_object_values(model.columns, 'OLD')}),
|
64
|
+
JSON_OBJECT(),
|
65
|
+
CURRENT_TIMESTAMP);
|
66
|
+
END;
|
67
|
+
SQL
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def drop
|
72
|
+
%w[insert update delete].each do |action|
|
73
|
+
trigger_name = "#{log_table_name}_#{action}_#{model.table_name}"
|
74
|
+
next unless exists?(trigger_name)
|
75
|
+
|
76
|
+
begin
|
77
|
+
sql = "DROP TRIGGER #{trigger_name}"
|
78
|
+
puts sql
|
79
|
+
ActiveRecord::Base.connection.execute(sql)
|
80
|
+
rescue StandardError => e
|
81
|
+
puts "#{e.message} trigger_name:#{trigger_name}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def exists?(trigger_name)
|
89
|
+
@trigger_names ||= ActiveRecord::Base.connection.select_rows('SHOW TRIGGERS').map { |row| row.first }
|
90
|
+
@trigger_names.include?(trigger_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
def json_object_values(columns, state)
|
94
|
+
columns.each.with_object([]) do |column, array|
|
95
|
+
array << "'#{column.name}'"
|
96
|
+
array << "#{state}.#{column.name}"
|
97
|
+
end.join(',')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/spec/app.rb
CHANGED
@@ -11,7 +11,15 @@ ActiveRecord::Schema.define version: 0 do
|
|
11
11
|
t.integer :age, null: false
|
12
12
|
t.timestamps null: false
|
13
13
|
end
|
14
|
+
|
15
|
+
create_table :departments, force: true do |t|
|
16
|
+
t.string :name, null: false
|
17
|
+
t.timestamps null: false
|
18
|
+
end
|
14
19
|
end
|
15
20
|
|
16
21
|
class User < ActiveRecord::Base
|
17
22
|
end
|
23
|
+
|
24
|
+
class Department < ActiveRecord::Base
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Breathing::Excel do
|
4
|
+
describe '#create' do
|
5
|
+
before { Breathing::Installer.new.install }
|
6
|
+
after { Breathing::Installer.new.uninstall }
|
7
|
+
|
8
|
+
it do
|
9
|
+
user = User.create!(name: 'a', age: 20)
|
10
|
+
user.update!(age: 21)
|
11
|
+
user.destroy!
|
12
|
+
expect(Breathing::ChangeLog.count).to eq(3)
|
13
|
+
|
14
|
+
Tempfile.open(['tmp', '.xlsx']) do |file|
|
15
|
+
Breathing::Excel.new.create(file_name: file.path)
|
16
|
+
workbook = RubyXL::Parser.parse(file.path)
|
17
|
+
expect(workbook.sheets[0].name).to eq('users')
|
18
|
+
user_sheet = workbook.worksheets[0]
|
19
|
+
expect(user_sheet.sheet_data.size).to eq(Breathing::ChangeLog.where(table_name: :users).count + 1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'multi sheets' do
|
24
|
+
User.create!(name: 'a', age: 20)
|
25
|
+
Department.create!(name: 'a')
|
26
|
+
|
27
|
+
Tempfile.open(['tmp', '.xlsx']) do |file|
|
28
|
+
Breathing::Excel.new.create(file_name: file.path)
|
29
|
+
workbook = RubyXL::Parser.parse(file.path)
|
30
|
+
expect(workbook.sheets.size).to eq(2)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/breathing_spec.rb
CHANGED
@@ -4,4 +4,37 @@ describe Breathing do
|
|
4
4
|
it 'has a version number' do
|
5
5
|
expect(Breathing::VERSION).not_to be nil
|
6
6
|
end
|
7
|
+
|
8
|
+
before { Breathing::Installer.new.install }
|
9
|
+
after { Breathing::Installer.new.uninstall }
|
10
|
+
|
11
|
+
it do
|
12
|
+
expect(Breathing::ChangeLog.count).to eq(0)
|
13
|
+
|
14
|
+
# INSERT
|
15
|
+
user = User.create!(name: 'a', age: 20)
|
16
|
+
expect(Breathing::ChangeLog.count).to eq(1)
|
17
|
+
|
18
|
+
log = Breathing::ChangeLog.where(table_name: user.class.table_name, transaction_id: user.id).last
|
19
|
+
expect(log.before_data).to eq({})
|
20
|
+
expect(log.after_data['name']).to eq('a')
|
21
|
+
expect(log.after_data['age']).to eq(20)
|
22
|
+
|
23
|
+
# UPDATE
|
24
|
+
user.update!(age: 21)
|
25
|
+
expect(Breathing::ChangeLog.count).to eq(2)
|
26
|
+
|
27
|
+
log = Breathing::ChangeLog.where(table_name: user.class.table_name, transaction_id: user.id).last
|
28
|
+
expect(log.before_data['age']).to eq(20)
|
29
|
+
expect(log.after_data['age']).to eq(21)
|
30
|
+
expect(log.before_data['name']).to eq(log.after_data['name'])
|
31
|
+
expect(log.changed_attribute_columns).to eq(%w[age updated_at])
|
32
|
+
|
33
|
+
# DELETE
|
34
|
+
user.destroy!
|
35
|
+
expect(Breathing::ChangeLog.count).to eq(3)
|
36
|
+
log = Breathing::ChangeLog.where(table_name: user.class.table_name, transaction_id: user.id).last
|
37
|
+
expect(log.before_data['name']).to eq('a')
|
38
|
+
expect(log.after_data).to eq({})
|
39
|
+
end
|
7
40
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Akira Kusumoto
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 5.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubyXL
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.4.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.4.0
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: mysql2
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,7 +94,7 @@ dependencies:
|
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '3.9'
|
83
|
-
description:
|
97
|
+
description: Audit logging for database
|
84
98
|
email:
|
85
99
|
- akirakusumo10@gmail.com
|
86
100
|
executables:
|
@@ -88,6 +102,7 @@ executables:
|
|
88
102
|
extensions: []
|
89
103
|
extra_rdoc_files: []
|
90
104
|
files:
|
105
|
+
- ".circleci/config.yml"
|
91
106
|
- ".gitignore"
|
92
107
|
- Gemfile
|
93
108
|
- MIT-LICENSE
|
@@ -96,8 +111,13 @@ files:
|
|
96
111
|
- breathing.gemspec
|
97
112
|
- exe/breathing
|
98
113
|
- lib/breathing.rb
|
114
|
+
- lib/breathing/change_log.rb
|
99
115
|
- lib/breathing/cli.rb
|
116
|
+
- lib/breathing/excel.rb
|
117
|
+
- lib/breathing/installer.rb
|
118
|
+
- lib/breathing/trigger.rb
|
100
119
|
- spec/app.rb
|
120
|
+
- spec/breathing/excel_spec.rb
|
101
121
|
- spec/breathing_spec.rb
|
102
122
|
- spec/database.yml
|
103
123
|
- spec/spec_helper.rb
|
@@ -123,9 +143,10 @@ requirements: []
|
|
123
143
|
rubygems_version: 3.0.3
|
124
144
|
signing_key:
|
125
145
|
specification_version: 4
|
126
|
-
summary:
|
146
|
+
summary: Audit logging for database
|
127
147
|
test_files:
|
128
148
|
- spec/app.rb
|
149
|
+
- spec/breathing/excel_spec.rb
|
129
150
|
- spec/breathing_spec.rb
|
130
151
|
- spec/database.yml
|
131
152
|
- spec/spec_helper.rb
|