audit_tables 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.pairs +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +32 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/Rakefile +7 -0
- data/audit_tables.gemspec +34 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/circle.yml +7 -0
- data/lib/audit_tables.rb +66 -0
- data/lib/audit_tables/base.rb +97 -0
- data/lib/audit_tables/build_audit_trigger.rb +47 -0
- data/lib/audit_tables/change_audit_table.rb +46 -0
- data/lib/audit_tables/create_audit_tables_for_existing_tables.rb +18 -0
- data/lib/audit_tables/create_new_audit_table.rb +8 -0
- data/lib/audit_tables/version.rb +4 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 12b1674ebfca6a1ded9baffc0c7942980a16f13c
|
4
|
+
data.tar.gz: 7a8b3695f39e7cb194141e1f950b5477e6efa64d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 88c840a04f5784511915c8924486700e92a340d49a57c3c262ac85a61c6cd7e47f3739f3beaa72980ecc9d3ec77c19f434edef1f2b39c1a2877ecb778d0b9882
|
7
|
+
data.tar.gz: 7727cd00aa9363086aaf1861f10b14e02da47ef50ead9dd581042c514b10da8e385de586a4e11d1328f2bdd4eedc9422be2a86ba63061f0c5350daf7cd83f4d5
|
data/.gitignore
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/Gemfile.lock
|
4
|
+
/_yardoc/
|
5
|
+
/coverage/
|
6
|
+
/doc/
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
10
|
+
.byebug_history
|
11
|
+
|
12
|
+
# Editor Droppings #
|
13
|
+
.idea
|
14
|
+
|
15
|
+
# OS generated files #
|
16
|
+
.DS_Store
|
17
|
+
.DS_Store?
|
18
|
+
._*
|
19
|
+
.Spotlight-V100
|
20
|
+
.Trashes
|
data/.pairs
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# .pairs - configuration for 'git pair'
|
2
|
+
pairs:
|
3
|
+
# <initials>: <Firstname> <Lastname>[; <email-id>]
|
4
|
+
aw: Alex Wang; alexw668
|
5
|
+
cw: Conroy Whitney; conroywhitney
|
6
|
+
en: Eric Nichols; nix2k2
|
7
|
+
jh: Jesse House; house9
|
8
|
+
jl: Julio Lucero; julioalucero
|
9
|
+
tt: Todd Trask;
|
10
|
+
vb: Vitor Oliveira; vbrazo
|
11
|
+
|
12
|
+
email:
|
13
|
+
prefix: team
|
14
|
+
domain: gitpair.com
|
15
|
+
no_solo_prefix: true
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.3
|
3
|
+
Exclude:
|
4
|
+
- 'bin/*'
|
5
|
+
- Gemfile
|
6
|
+
- syncordia.gemspec
|
7
|
+
DisplayCopNames: true
|
8
|
+
AmbiguousOperator:
|
9
|
+
Enabled: false
|
10
|
+
Metrics/LineLength:
|
11
|
+
Max: 120
|
12
|
+
Exclude:
|
13
|
+
- Rakefile
|
14
|
+
Style/Documentation:
|
15
|
+
Enabled: false
|
16
|
+
Style/HashSyntax:
|
17
|
+
Exclude:
|
18
|
+
- Rakefile
|
19
|
+
Style/IndentHash:
|
20
|
+
EnforcedStyle: consistent
|
21
|
+
Style/LambdaCall:
|
22
|
+
Enabled: false
|
23
|
+
Style/MultilineMethodCallIndentation:
|
24
|
+
Enabled: false
|
25
|
+
Style/Next:
|
26
|
+
Enabled: false
|
27
|
+
Style/WordArray:
|
28
|
+
Enabled: false
|
29
|
+
AmbiguousOperator:
|
30
|
+
Enabled: false
|
31
|
+
Style/ModuleFunction:
|
32
|
+
EnforcedStyle: extend_self
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Jesse House
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# AuditTables
|
2
|
+
|
3
|
+
[](https://circleci.com/gh/syncordiahealth/audit_tables/tree/master)
|
4
|
+
|
5
|
+
The `audit_tables` gem can be used by rails applications using a [postgresql](https://www.postgresql.org/) databases.
|
6
|
+
|
7
|
+
It adds helper functions that can be used to create audit tables and database triggers for tracking `INSERT`, `UPDATE` and `DELETE` database actions.
|
8
|
+
|
9
|
+
`audit_tables` works purely at the database level and you will need to use raw sql to view audit logs.
|
10
|
+
|
11
|
+
It generates audit tables using the naming convention `audit_TABLE_NAME`, i.e. `audit_widgets`.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'audit_tables'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install audit_tables
|
28
|
+
|
29
|
+
Optionally add an initializer file: `config/initializers/audit_tables.rb`, if you want to skip audits for some tables.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
AuditTables.configure do |config|
|
33
|
+
config.exclude_tables << 'sessions'
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
NOTE: `ar_internal_metadata` and `schema_migrations` are skipped by default, but you can audit those as well by overriding the configuation option:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
AuditTables.configure do |config|
|
41
|
+
config.exclude_tables = []
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
The following public methods are exposed by the `AuditTables` module:
|
48
|
+
|
49
|
+
* `create_audit_tables_for_existing_tables`
|
50
|
+
* Creates an audit table for all existing tables. You would only call this once from a migration:
|
51
|
+
* `AuditTables.create_audit_tables_for_existing_tables`
|
52
|
+
* `create_audit_table_for(table_name)`
|
53
|
+
* Create an audit table for a new table. See examples below.
|
54
|
+
* `change_audit_table_for(table_name)`
|
55
|
+
* When you make changes in your tables, you can use this method to apply the same changes to the audit tables.
|
56
|
+
* Optionally, you can modify the audit table structure using rails helper methods directly. This method is here for convience only.
|
57
|
+
* `build_audit_triggers_for(table_name)`
|
58
|
+
* create the triggers on the new table or drop and re-create the audit triggers to handle all the new columns on the table passed as a parameter
|
59
|
+
* Since the trigger uses `*` and expects your source table column order to match the audit table column order, you don't actually need to call this after making table changes, however it does not hurt.
|
60
|
+
* `check_for_audit_tables`
|
61
|
+
* You can use this method in your specs, see spec example below.
|
62
|
+
|
63
|
+
#### Example Migration
|
64
|
+
|
65
|
+
```
|
66
|
+
class ExampleMigration < ActiveRecord::Migration[5.0]
|
67
|
+
def change
|
68
|
+
create_table :widgets do |t|
|
69
|
+
t.string :name
|
70
|
+
|
71
|
+
t.timestamps
|
72
|
+
end
|
73
|
+
|
74
|
+
AuditTables.create_audit_table_for(:widgets)
|
75
|
+
AuditTables.build_audit_triggers_for(:widgets)
|
76
|
+
|
77
|
+
add_column :widgets, :serial_number, :string
|
78
|
+
AuditTables.change_audit_table_for(:widgets)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
#### Spec Example
|
84
|
+
|
85
|
+
This checks the database for audit tables and errors if a developer forgot to add one to a migration:
|
86
|
+
|
87
|
+
```
|
88
|
+
describe AuditTables do
|
89
|
+
it 'ensure audit tables' do
|
90
|
+
checks = AuditTables.check_for_audit_tables
|
91
|
+
message = "You forgot to add audit tables for these tables: #{checks}"
|
92
|
+
|
93
|
+
expect(checks).to be_empty, message
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
## Notes
|
99
|
+
|
100
|
+
The structure of the audit tables:
|
101
|
+
|
102
|
+
* `audit_id`, integer auto incrementing
|
103
|
+
* `audit_operation` string - `INSERT`, `UPDATE` or `DELETE`
|
104
|
+
* `audit_timestamp` datetime (when the audit record was created)
|
105
|
+
* `*` all of the columns from the source table (column order must match)
|
106
|
+
|
107
|
+
The `DELETE` trigger copies all the values from the deleted record into the audit table, but it will probably look exactly like the previous `INSERT` or `UPDATE` audit record except for the `audit_timestamp`. If you are tracking which user updated a record, the delete audit probably won't reflect the correct user unless you did an update before the delete. We recommend doing soft deletes in most cases.
|
108
|
+
|
109
|
+
## Development
|
110
|
+
|
111
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
112
|
+
|
113
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
114
|
+
|
115
|
+
## Contributing
|
116
|
+
|
117
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/syncordiahealth/audit_tables.
|
118
|
+
|
119
|
+
|
120
|
+
## License
|
121
|
+
|
122
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
123
|
+
|
124
|
+
## Change Log
|
125
|
+
|
126
|
+
* Version 1.0.0 (2017-01-20)
|
127
|
+
* Initial Release
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'audit_tables/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'audit_tables'
|
9
|
+
spec.version = AuditTables::VERSION
|
10
|
+
spec.authors = ['Syncordia Technologies']
|
11
|
+
spec.email = ['info@syncordiahealth.ie']
|
12
|
+
|
13
|
+
spec.summary = 'Adds audit tables and triggers for postgres databases'
|
14
|
+
spec.description = 'Adds audit tables and triggers for postgres databases'
|
15
|
+
spec.homepage = 'https://github.com/syncordiahealth/audit_tables'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
|
22
|
+
spec.bindir = 'exe'
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ['lib']
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
27
|
+
spec.add_development_dependency 'byebug', '~> 9.0.6'
|
28
|
+
spec.add_development_dependency 'faker'
|
29
|
+
spec.add_development_dependency 'pg'
|
30
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
31
|
+
spec.add_development_dependency 'rails'
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
33
|
+
spec.add_development_dependency 'rubocop', '= 0.42.0'
|
34
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "audit_tables"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/circle.yml
ADDED
data/lib/audit_tables.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# include all of the files inside the `lib/audit_tables` folder
|
4
|
+
Gem.find_files('audit_tables/**/*.rb').each { |path| require path }
|
5
|
+
|
6
|
+
module AuditTables
|
7
|
+
class << self
|
8
|
+
attr_accessor :configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
class Configuration
|
12
|
+
attr_accessor :exclude_tables
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@exclude_tables = ['ar_internal_metadata', 'schema_migrations']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.config
|
20
|
+
self.configuration ||= Configuration.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configure
|
24
|
+
self.configuration ||= Configuration.new
|
25
|
+
yield(configuration)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.all_audit_tables
|
29
|
+
tables = ActiveRecord::Base.connection.tables
|
30
|
+
tables -= config.exclude_tables
|
31
|
+
|
32
|
+
tables
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.build_audit_triggers_for(table_name)
|
36
|
+
AuditTables::BuildAuditTrigger.new(table_name.to_s).build
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.check_for_audit_tables
|
40
|
+
messages = []
|
41
|
+
|
42
|
+
all_audit_tables.select { |table| !table.starts_with?('audit_') }.each do |table_name|
|
43
|
+
messages << table_name unless all_audit_tables.include? "audit_#{table_name}"
|
44
|
+
end
|
45
|
+
|
46
|
+
messages
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.create_audit_table_for(table_name)
|
50
|
+
AuditTables::CreateNewAuditTable.new(table_name.to_s).build
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.create_audit_tables_for_existing_tables
|
54
|
+
AuditTables::CreateAuditTablesForExistingTables.new(config.exclude_tables).process
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.change_audit_table_for(table_name)
|
58
|
+
AuditTables::ChangeAuditTable.new(table_name.to_s).execute
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.rebuild_all_audit_triggers
|
62
|
+
all_audit_tables.select { |table| !table.starts_with?('audit_') }.each do |table_name|
|
63
|
+
build_audit_triggers_for(table_name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module AuditTables
|
3
|
+
class Base < ActiveRecord::Migration[5.0]
|
4
|
+
attr_accessor :audit_table_name, :column, :klass, :table_name
|
5
|
+
|
6
|
+
def initialize(table_name)
|
7
|
+
@table_name = table_name
|
8
|
+
@audit_table_name = "audit_#{table_name}"
|
9
|
+
@klass = table_name.classify.safe_constantize
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def add_new_column
|
15
|
+
if properties.empty?
|
16
|
+
add_column audit_table_name, column.name, column_type
|
17
|
+
else
|
18
|
+
add_column audit_table_name, column.name, column_type, properties
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def audit_columns
|
23
|
+
columns(table_name).each do |column|
|
24
|
+
@column = column
|
25
|
+
|
26
|
+
add_new_column unless column_exists?(audit_table_name, column.name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def sync_audit_tables
|
31
|
+
create_audit_table unless table_exists?
|
32
|
+
audit_columns
|
33
|
+
end
|
34
|
+
|
35
|
+
def column_exists?(table_name, column_name)
|
36
|
+
ActiveRecord::Base.connection.column_exists?(table_name, column_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def column_name
|
40
|
+
column.name
|
41
|
+
end
|
42
|
+
|
43
|
+
def column_type
|
44
|
+
column.type
|
45
|
+
end
|
46
|
+
|
47
|
+
def columns(table_name)
|
48
|
+
ActiveRecord::Base.connection.columns(table_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_audit_table
|
52
|
+
create_table audit_table_name, id: false do |t|
|
53
|
+
t.integer :audit_id, null: false
|
54
|
+
t.string :audit_operation, null: false
|
55
|
+
t.datetime :audit_timestamp, null: false
|
56
|
+
end
|
57
|
+
|
58
|
+
add_column audit_table_name, :id, klass.columns_hash['id'].type
|
59
|
+
end
|
60
|
+
|
61
|
+
def default
|
62
|
+
column.default
|
63
|
+
end
|
64
|
+
|
65
|
+
def limit
|
66
|
+
column.limit
|
67
|
+
end
|
68
|
+
|
69
|
+
def null
|
70
|
+
column.null
|
71
|
+
end
|
72
|
+
|
73
|
+
def precision
|
74
|
+
column.precision
|
75
|
+
end
|
76
|
+
|
77
|
+
def properties
|
78
|
+
elements = {}
|
79
|
+
|
80
|
+
elements[:limit] = limit unless limit.nil?
|
81
|
+
elements[:precision] = precision unless precision.nil?
|
82
|
+
elements[:scale] = scale unless scale.nil?
|
83
|
+
elements[:default] = default unless default.nil?
|
84
|
+
elements[:null] = null
|
85
|
+
|
86
|
+
elements
|
87
|
+
end
|
88
|
+
|
89
|
+
def scale
|
90
|
+
column.scale
|
91
|
+
end
|
92
|
+
|
93
|
+
def table_exists?
|
94
|
+
ActiveRecord::Base.connection.data_source_exists? audit_table_name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module AuditTables
|
3
|
+
class BuildAuditTrigger < ActiveRecord::Migration
|
4
|
+
attr_reader :table_name
|
5
|
+
|
6
|
+
def initialize(table_name)
|
7
|
+
@table_name = table_name
|
8
|
+
end
|
9
|
+
|
10
|
+
# rubocop:disable Metrics/MethodLength:
|
11
|
+
def build
|
12
|
+
sequence_name = "seq_audit_#{table_name}"
|
13
|
+
|
14
|
+
execute %{
|
15
|
+
DROP TRIGGER IF EXISTS trigger_audit_#{table_name} ON #{table_name};
|
16
|
+
|
17
|
+
CREATE SEQUENCE IF NOT EXISTS #{sequence_name} START 1;
|
18
|
+
|
19
|
+
CREATE OR REPLACE FUNCTION trigger_audit_#{table_name}_func() RETURNS TRIGGER AS $body$
|
20
|
+
BEGIN
|
21
|
+
IF (TG_OP = 'INSERT') THEN
|
22
|
+
INSERT INTO audit_#{table_name}
|
23
|
+
SELECT nextval('#{sequence_name}'), 'INSERT', NOW(), *
|
24
|
+
FROM #{table_name}
|
25
|
+
WHERE id = NEW.id;
|
26
|
+
ELSIF (TG_OP = 'UPDATE') THEN
|
27
|
+
INSERT INTO audit_#{table_name}
|
28
|
+
SELECT nextval('#{sequence_name}'), 'UPDATE', NOW(), *
|
29
|
+
FROM #{table_name}
|
30
|
+
WHERE id = OLD.id;
|
31
|
+
ELSIF (TG_OP = 'DELETE') THEN
|
32
|
+
INSERT INTO audit_#{table_name}
|
33
|
+
SELECT nextval('#{sequence_name}'), 'DELETE', NOW(), OLD.*;
|
34
|
+
END IF;
|
35
|
+
RETURN NULL;
|
36
|
+
END;
|
37
|
+
$body$
|
38
|
+
LANGUAGE plpgsql
|
39
|
+
SECURITY DEFINER;
|
40
|
+
|
41
|
+
CREATE TRIGGER trigger_audit_#{table_name} AFTER INSERT OR UPDATE OR DELETE ON #{table_name}
|
42
|
+
FOR EACH ROW EXECUTE PROCEDURE trigger_audit_#{table_name}_func();
|
43
|
+
}
|
44
|
+
end
|
45
|
+
# rubocop:enable Metrics/MethodLength:
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module AuditTables
|
3
|
+
class ChangeAuditTable < Base
|
4
|
+
attr_accessor :audit_column
|
5
|
+
|
6
|
+
def audit_changes
|
7
|
+
columns(audit_table_name).each do |audit_column|
|
8
|
+
@audit_column = audit_column
|
9
|
+
|
10
|
+
column_exists?(table_name, audit_column.name) ? change_attribute : remove_attribute
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def change_attribute
|
15
|
+
change_column audit_table_name, column_name, column_type, properties_curator if properties_curator.count.positive?
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute
|
19
|
+
audit_columns
|
20
|
+
audit_changes
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_column(column)
|
24
|
+
columns(table_name).each { |col| @column = col if column.name == col.name }
|
25
|
+
end
|
26
|
+
|
27
|
+
# rubocop:disable Metrics/AbcSize:
|
28
|
+
def properties_curator
|
29
|
+
elements = {}
|
30
|
+
find_column(audit_column)
|
31
|
+
|
32
|
+
elements[:limit] = limit unless audit_column.limit == limit
|
33
|
+
elements[:precision] = precision unless audit_column.precision == precision
|
34
|
+
elements[:scale] = scale unless audit_column.scale == scale
|
35
|
+
elements[:null] = null unless audit_column.null == null
|
36
|
+
elements[:default] = default unless audit_column.default == default
|
37
|
+
|
38
|
+
elements
|
39
|
+
end
|
40
|
+
# rubocop:enable Metrics/AbcSize:
|
41
|
+
|
42
|
+
def remove_attribute
|
43
|
+
remove_column audit_table_name, audit_column.name unless audit_column.name.include? 'audit'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module AuditTables
|
3
|
+
class CreateAuditTablesForExistingTables
|
4
|
+
attr_reader :klasses
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@klasses = ActiveRecord::Base.connection.tables
|
8
|
+
@klasses -= options
|
9
|
+
end
|
10
|
+
|
11
|
+
def process
|
12
|
+
klasses.each do |klass|
|
13
|
+
AuditTables::BuildAuditTrigger.new(klass).build
|
14
|
+
AuditTables::CreateNewAuditTable.new(klass).build
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: audit_tables
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Syncordia Technologies
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: byebug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 9.0.6
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 9.0.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faker
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pg
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.42.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.42.0
|
125
|
+
description: Adds audit tables and triggers for postgres databases
|
126
|
+
email:
|
127
|
+
- info@syncordiahealth.ie
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- ".pairs"
|
134
|
+
- ".rspec"
|
135
|
+
- ".rubocop.yml"
|
136
|
+
- Gemfile
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- audit_tables.gemspec
|
141
|
+
- bin/console
|
142
|
+
- bin/setup
|
143
|
+
- circle.yml
|
144
|
+
- lib/audit_tables.rb
|
145
|
+
- lib/audit_tables/base.rb
|
146
|
+
- lib/audit_tables/build_audit_trigger.rb
|
147
|
+
- lib/audit_tables/change_audit_table.rb
|
148
|
+
- lib/audit_tables/create_audit_tables_for_existing_tables.rb
|
149
|
+
- lib/audit_tables/create_new_audit_table.rb
|
150
|
+
- lib/audit_tables/version.rb
|
151
|
+
homepage: https://github.com/syncordiahealth/audit_tables
|
152
|
+
licenses:
|
153
|
+
- MIT
|
154
|
+
metadata: {}
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubyforge_project:
|
171
|
+
rubygems_version: 2.5.1
|
172
|
+
signing_key:
|
173
|
+
specification_version: 4
|
174
|
+
summary: Adds audit tables and triggers for postgres databases
|
175
|
+
test_files: []
|