pg_audit_log 0.0.2 → 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.
- data/Gemfile +4 -2
- data/Rakefile +9 -3
- data/lib/generators/pg_audit_log/install_generator.rb +1 -1
- data/lib/pg_audit_log/function.rb +3 -1
- data/lib/pg_audit_log/version.rb +1 -1
- data/pg_audit_log.gemspec +4 -3
- data/spec/pg_audit_log_spec.rb +129 -82
- metadata +17 -6
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rspec/core'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
1
5
|
require 'bundler'
|
2
6
|
Bundler::GemHelper.install_tasks
|
3
7
|
|
4
|
-
task :
|
5
|
-
|
6
|
-
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
11
|
+
RSpec::Core::RakeTask.new(:spec)
|
12
|
+
|
@@ -4,7 +4,7 @@ module PgAuditLog
|
|
4
4
|
module Generators
|
5
5
|
class InstallGenerator < Rails::Generators::Base
|
6
6
|
include Rails::Generators::Migration
|
7
|
-
extend ActiveRecord::Generators::Migration
|
7
|
+
extend ::ActiveRecord::Generators::Migration
|
8
8
|
|
9
9
|
source_root File.expand_path('../templates', __FILE__)
|
10
10
|
|
@@ -43,7 +43,9 @@ module PgAuditLog
|
|
43
43
|
END IF;
|
44
44
|
IF TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN
|
45
45
|
EXECUTE 'SELECT CAST($1 . '|| column_name ||' AS TEXT)' INTO old_value USING OLD;
|
46
|
-
|
46
|
+
IF primary_key_column IS NOT NULL THEN
|
47
|
+
EXECUTE 'SELECT CAST($1 . '|| primary_key_column ||' AS VARCHAR)' INTO primary_key_value USING OLD;
|
48
|
+
END IF;
|
47
49
|
END IF;
|
48
50
|
|
49
51
|
IF TG_RELNAME = 'users' AND column_name = 'last_accessed_at' THEN
|
data/lib/pg_audit_log/version.rb
CHANGED
data/pg_audit_log.gemspec
CHANGED
@@ -17,7 +17,8 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.add_dependency("rails", ">=
|
21
|
-
s.
|
22
|
-
s.add_development_dependency('
|
20
|
+
s.add_dependency("rails", ">= 3.0.0")
|
21
|
+
s.add_dependency("pg", ">= 0.9.0")
|
22
|
+
s.add_development_dependency('rspec-rails')
|
23
|
+
s.add_development_dependency('with_model', '>= 0.1.3')
|
23
24
|
end
|
data/spec/pg_audit_log_spec.rb
CHANGED
@@ -13,30 +13,37 @@ describe PgAuditLog do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
with_model :audited_model_without_primary_key do
|
17
|
+
table :id => false do |t|
|
18
|
+
t.string :str
|
19
|
+
t.text :txt
|
20
|
+
t.integer :int
|
21
|
+
t.date :date
|
22
|
+
t.datetime :dt
|
23
|
+
t.boolean :bool
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
before do
|
17
|
-
AuditedModel.
|
18
|
-
|
19
|
-
AFTER INSERT OR UPDATE OR DELETE
|
20
|
-
ON #{AuditedModel.quoted_table_name}
|
21
|
-
FOR EACH ROW
|
22
|
-
EXECUTE PROCEDURE audit_changes()
|
23
|
-
SQL
|
28
|
+
PgAuditLog::Triggers.create_for_table(AuditedModel.table_name)
|
29
|
+
PgAuditLog::Triggers.create_for_table(AuditedModelWithoutPrimaryKey.table_name)
|
24
30
|
end
|
25
31
|
|
26
32
|
after do
|
27
|
-
|
33
|
+
PgAuditLog::Triggers.drop_for_table(AuditedModel.table_name)
|
34
|
+
PgAuditLog::Triggers.drop_for_table(AuditedModelWithoutPrimaryKey.table_name)
|
28
35
|
PgAuditLog::Entry.connection.execute("TRUNCATE #{PgAuditLog::Entry.quoted_table_name}")
|
29
36
|
end
|
30
37
|
|
31
38
|
let(:attributes) { { :str => "foo", :txt => "bar", :int => 5, :date => Date.today, :dt => Time.now.midnight } }
|
32
39
|
|
33
|
-
|
40
|
+
describe "on create" do
|
41
|
+
context "the audit log record with a primary key" do
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
|
43
|
+
before do
|
44
|
+
AuditedModel.create!(attributes)
|
45
|
+
end
|
38
46
|
|
39
|
-
describe "the audit log record" do
|
40
47
|
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str" }) }
|
41
48
|
|
42
49
|
it { should be }
|
@@ -75,110 +82,150 @@ describe PgAuditLog do
|
|
75
82
|
end
|
76
83
|
|
77
84
|
end
|
78
|
-
end
|
79
85
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
86
|
+
context "the audit log record without a primary key" do
|
87
|
+
before do
|
88
|
+
AuditedModelWithoutPrimaryKey.create!(attributes)
|
89
|
+
end
|
84
90
|
|
85
|
-
context "when going from a value to a another value" do
|
86
|
-
before { @model.update_attributes!(:str => "bar") }
|
87
91
|
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str" }) }
|
88
92
|
|
89
|
-
|
90
|
-
its(:
|
91
|
-
its(:
|
93
|
+
it { should be }
|
94
|
+
its(:field_name) { should == "str" }
|
95
|
+
its(:primary_key) { should be_nil }
|
92
96
|
end
|
97
|
+
end
|
93
98
|
|
94
|
-
|
95
|
-
|
96
|
-
before
|
97
|
-
|
99
|
+
describe "on update" do
|
100
|
+
context "the audit log record with a primary key" do
|
101
|
+
before do
|
102
|
+
@model = AuditedModel.create!(attributes)
|
103
|
+
end
|
98
104
|
|
99
|
-
|
100
|
-
|
101
|
-
|
105
|
+
context "when going from a value to a another value" do
|
106
|
+
before { @model.update_attributes!(:str => "bar") }
|
107
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str" }) }
|
102
108
|
|
103
|
-
|
104
|
-
|
105
|
-
|
109
|
+
its(:operation) { should == "UPDATE" }
|
110
|
+
its(:field_value_new) { should == "bar" }
|
111
|
+
its(:field_value_old) { should == "foo" }
|
112
|
+
end
|
106
113
|
|
107
|
-
|
108
|
-
|
109
|
-
|
114
|
+
context "when going from nil to a value" do
|
115
|
+
let(:attributes) { {:txt => nil} }
|
116
|
+
before { @model.update_attributes!(:txt => "baz") }
|
117
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "txt" }) }
|
110
118
|
|
111
|
-
|
112
|
-
|
113
|
-
|
119
|
+
its(:field_value_new) { should == "baz" }
|
120
|
+
its(:field_value_old) { should be_nil }
|
121
|
+
end
|
114
122
|
|
115
|
-
|
116
|
-
|
123
|
+
context "when going from a value to nil" do
|
124
|
+
before { @model.update_attributes!(:str => nil) }
|
125
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str" }) }
|
117
126
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "txt", :operation => "UPDATE" }) }
|
127
|
+
its(:field_value_new) { should be_nil }
|
128
|
+
its(:field_value_old) { should == "foo" }
|
129
|
+
end
|
122
130
|
|
123
|
-
|
124
|
-
|
131
|
+
context "when the value does not change" do
|
132
|
+
before { @model.update_attributes!(:str => "foo") }
|
133
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str", :operation => "UPDATE" }) }
|
125
134
|
|
126
|
-
|
135
|
+
it { should_not be }
|
136
|
+
end
|
127
137
|
|
128
|
-
context "
|
129
|
-
|
130
|
-
|
138
|
+
context "when the value is nil and does not change" do
|
139
|
+
let(:attributes) { {:txt => nil} }
|
140
|
+
before { @model.update_attributes!(:txt => nil) }
|
141
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "txt", :operation => "UPDATE" }) }
|
131
142
|
|
132
|
-
|
133
|
-
its(:field_value_old) { should be_nil }
|
143
|
+
it { should_not be }
|
134
144
|
end
|
135
145
|
|
136
|
-
context "
|
137
|
-
|
138
|
-
|
139
|
-
@model.update_attributes!(:bool => true)
|
146
|
+
context "when the value is a boolean" do
|
147
|
+
|
148
|
+
context "going from nil -> true" do
|
149
|
+
before { @model.update_attributes!(:bool => true) }
|
150
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "bool", :operation => "UPDATE" }) }
|
151
|
+
|
152
|
+
its(:field_value_new) { should == "true" }
|
153
|
+
its(:field_value_old) { should be_nil }
|
140
154
|
end
|
141
|
-
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "bool", :operation => "UPDATE" }) }
|
142
155
|
|
143
|
-
|
144
|
-
|
145
|
-
|
156
|
+
context "going from false -> true" do
|
157
|
+
let(:attributes) { {:bool => false} }
|
158
|
+
before do
|
159
|
+
@model.update_attributes!(:bool => true)
|
160
|
+
end
|
161
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "bool", :operation => "UPDATE" }) }
|
146
162
|
|
147
|
-
|
148
|
-
|
163
|
+
its(:field_value_new) { should == "true" }
|
164
|
+
its(:field_value_old) { should == "false" }
|
165
|
+
end
|
149
166
|
|
150
|
-
|
151
|
-
|
167
|
+
context "going from true -> false" do
|
168
|
+
let(:attributes) { {:bool => true} }
|
169
|
+
|
170
|
+
before do
|
171
|
+
@model.update_attributes!(:bool => false)
|
172
|
+
end
|
173
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "bool", :operation => "UPDATE" }) }
|
174
|
+
|
175
|
+
its(:field_value_new) { should == "false" }
|
176
|
+
its(:field_value_old) { should == "true" }
|
152
177
|
end
|
153
|
-
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "bool", :operation => "UPDATE" }) }
|
154
178
|
|
155
|
-
|
156
|
-
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "the audit log record without a primary key" do
|
183
|
+
before do
|
184
|
+
AuditedModelWithoutPrimaryKey.create!(attributes)
|
185
|
+
AuditedModelWithoutPrimaryKey.update_all(:str => "bar")
|
157
186
|
end
|
158
187
|
|
188
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str" }) }
|
189
|
+
|
190
|
+
its(:primary_key) { should be_nil }
|
159
191
|
end
|
192
|
+
|
160
193
|
end
|
161
194
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
195
|
+
describe "on delete" do
|
196
|
+
|
197
|
+
context "the audit log record with a primary key" do
|
198
|
+
before do
|
199
|
+
model = AuditedModel.create!(attributes)
|
200
|
+
model.delete
|
201
|
+
end
|
167
202
|
|
168
|
-
|
203
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str" }) }
|
169
204
|
|
170
|
-
|
205
|
+
its(:operation) { should == "DELETE" }
|
171
206
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
207
|
+
it "captures all new values for all fields" do
|
208
|
+
attributes.each do |field_name, value|
|
209
|
+
if field_name == :dt
|
210
|
+
PgAuditLog::Entry.last(:conditions => { :field_name => field_name }).field_value_old.should == value.strftime("%Y-%m-%d %H:%M:%S")
|
211
|
+
else
|
212
|
+
PgAuditLog::Entry.last(:conditions => { :field_name => field_name }).field_value_old.should == value.to_s
|
213
|
+
end
|
214
|
+
PgAuditLog::Entry.last(:conditions => { :field_name => field_name }).field_value_new.should be_nil
|
178
215
|
end
|
179
|
-
PgAuditLog::Entry.last(:conditions => { :field_name => field_name }).field_value_new.should be_nil
|
180
216
|
end
|
181
217
|
end
|
218
|
+
|
219
|
+
context "the audit log record without a primary key" do
|
220
|
+
before do
|
221
|
+
AuditedModelWithoutPrimaryKey.create!(attributes)
|
222
|
+
AuditedModelWithoutPrimaryKey.delete_all
|
223
|
+
end
|
224
|
+
|
225
|
+
subject { PgAuditLog::Entry.last(:conditions => { :field_name => "str" }) }
|
226
|
+
|
227
|
+
its(:primary_key) { should be_nil }
|
228
|
+
end
|
182
229
|
end
|
183
230
|
end
|
184
231
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: pg_audit_log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0
|
5
|
+
version: 0.1.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Case Commons, LLC
|
@@ -21,22 +21,22 @@ dependencies:
|
|
21
21
|
requirements:
|
22
22
|
- - ">="
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version:
|
24
|
+
version: 3.0.0
|
25
25
|
type: :runtime
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pg
|
29
29
|
prerelease: false
|
30
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
31
|
none: false
|
32
32
|
requirements:
|
33
33
|
- - ">="
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
36
|
-
type: :
|
35
|
+
version: 0.9.0
|
36
|
+
type: :runtime
|
37
37
|
version_requirements: *id002
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
|
-
name:
|
39
|
+
name: rspec-rails
|
40
40
|
prerelease: false
|
41
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
42
|
none: false
|
@@ -46,6 +46,17 @@ dependencies:
|
|
46
46
|
version: "0"
|
47
47
|
type: :development
|
48
48
|
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: with_model
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 0.1.3
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
49
60
|
description: A completely transparent audit logging component for your application using a stored procedure and triggers. Comes with specs for your project and a rake task to generate the reverse SQL to undo changes logged
|
50
61
|
email:
|
51
62
|
- casecommons-dev@googlegroups.com
|