paper_trail_scrapbook 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +101 -52
- data/README.md +92 -13
- data/Rakefile +1 -5
- data/circle.yml +3 -2
- data/lib/paper_trail_scrapbook/version.rb +1 -1
- data/log/mutant/pts.log +5476 -0
- data/paper_trail_scrapbook.gemspec +6 -3
- data/pkg/paper_trail_scrapbook-0.1.7.gem +0 -0
- data/spec/dummy_app/app/models/elephant.rb +1 -1
- data/spec/dummy_app/config/database.yml +8 -7
- data/spec/paper_trail_scrapbook/changes_spec.rb +16 -22
- data/spec/paper_trail_scrapbook/config_spec.rb +14 -0
- data/spec/paper_trail_scrapbook/life_history_spec.rb +15 -7
- data/spec/paper_trail_scrapbook/user_journal_spec.rb +52 -40
- data/spec/paper_trail_scrapbook/version_helpers_spec.rb +18 -6
- metadata +59 -18
- data/spec/dummy_app/config/database.sqlite.yml +0 -15
- data/spec/dummy_app/db/test.sqlite3 +0 -0
@@ -22,17 +22,20 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_dependency 'paper_trail', '>= 5.2'
|
23
23
|
|
24
24
|
gem.add_development_dependency 'ffaker', '~> 2.5'
|
25
|
-
gem.add_development_dependency 'rake', '~>
|
25
|
+
gem.add_development_dependency 'rake', '~> 10.4.2'
|
26
26
|
|
27
27
|
# Why `railties`? Possibly used by `spec/dummy_app` boot up?
|
28
28
|
gem.add_development_dependency 'railties', ['>= 4.0', '< 5.2']
|
29
29
|
|
30
30
|
gem.add_development_dependency 'codeclimate-test-reporter', '~> 1.0.7'
|
31
31
|
gem.add_development_dependency 'database_cleaner', '~> 1.2'
|
32
|
-
gem.add_development_dependency '
|
32
|
+
gem.add_development_dependency 'loofah', '~> 2.2.1'
|
33
|
+
gem.add_development_dependency 'mutant'
|
34
|
+
gem.add_development_dependency 'mutant-rspec'
|
35
|
+
gem.add_development_dependency 'pg', '= 0.18.4'
|
36
|
+
gem.add_development_dependency 'rspec-rails', '~> 3.6'
|
33
37
|
gem.add_development_dependency 'rubocop'
|
34
38
|
gem.add_development_dependency 'rubocop-rspec'
|
35
39
|
gem.add_development_dependency 'simplecov'
|
36
|
-
gem.add_development_dependency 'sqlite3', '~> 1.3.13'
|
37
40
|
gem.add_development_dependency 'timecop', '~> 0.8.0'
|
38
41
|
end
|
Binary file
|
@@ -1,15 +1,16 @@
|
|
1
|
-
|
2
|
-
# gem install sqlite3-ruby (not necessary on OS X Leopard)
|
1
|
+
|
3
2
|
test: &test
|
4
|
-
adapter:
|
3
|
+
adapter: postgresql
|
4
|
+
encoding: unicode
|
5
|
+
host: localhost
|
6
|
+
database: pts_db
|
7
|
+
port: <%= ENV['DB_PORT'] || 5432 %>
|
5
8
|
pool: 5
|
6
|
-
timeout: 5000
|
7
|
-
database: db/test.sqlite3
|
8
9
|
|
9
10
|
foo:
|
10
11
|
<<: *test
|
11
|
-
database:
|
12
|
+
database: pts_foo_db
|
12
13
|
|
13
14
|
bar:
|
14
15
|
<<: *test
|
15
|
-
database:
|
16
|
+
database: pts_bar_db
|
@@ -43,24 +43,12 @@ module PaperTrailScrapbook
|
|
43
43
|
'order_number:',
|
44
44
|
'- ',
|
45
45
|
"- ''",
|
46
|
-
'notes:',
|
47
|
-
'- ',
|
48
|
-
"- ''",
|
49
|
-
'
|
50
|
-
'- ',
|
51
|
-
|
52
|
-
'other_terms:',
|
53
|
-
'- ',
|
54
|
-
"- ' '",
|
55
|
-
'payment_terms_cd:',
|
56
|
-
'- ',
|
57
|
-
'- 1',
|
58
|
-
'other_payment_terms:',
|
59
|
-
'- ',
|
60
|
-
"- ' '",
|
61
|
-
'sent:',
|
62
|
-
'- false',
|
63
|
-
'- true',
|
46
|
+
'notes:', '- ', "- ''",
|
47
|
+
'internal_notes:', '- ', "- ' '",
|
48
|
+
'other_terms:', "- ' '", '-',
|
49
|
+
'payment_terms_cd:', '- ', '- 1',
|
50
|
+
'other_payment_terms:', '- ', "- ' '",
|
51
|
+
'sent:', '- false', '- true',
|
64
52
|
'created_by:',
|
65
53
|
'- ',
|
66
54
|
'- 1742',
|
@@ -89,13 +77,19 @@ module PaperTrailScrapbook
|
|
89
77
|
|
90
78
|
describe '#change_log' do
|
91
79
|
it 'provides a set of update changes' do
|
92
|
-
|
80
|
+
result = subject
|
81
|
+
|
82
|
+
expect(result).to match(/discounted amount: 29612.0 added/)
|
83
|
+
expect(result).to match(/• status: active -> issued/)
|
84
|
+
expect(result).to match(/other terms: was \*removed\*/)
|
93
85
|
end
|
94
86
|
|
95
87
|
it 'filters the proper columns' do
|
96
|
-
|
97
|
-
|
98
|
-
expect(
|
88
|
+
result = subject
|
89
|
+
|
90
|
+
expect(result).not_to match(/created_at:/)
|
91
|
+
expect(result).not_to match(/updated_at:/)
|
92
|
+
expect(result).not_to match(/id:/)
|
99
93
|
end
|
100
94
|
|
101
95
|
it 'provides a set of create changes' do
|
@@ -5,6 +5,20 @@ module PaperTrailScrapbook
|
|
5
5
|
describe '.instance' do
|
6
6
|
it 'returns the singleton instance' do
|
7
7
|
expect { described_class.instance }.not_to raise_error
|
8
|
+
expect(described_class.instance.whodunnit_class).to equal(Person).or be_nil
|
9
|
+
expect(described_class.instance.time_format)
|
10
|
+
.to eql(described_class::DEFAULT_TIME_FORMAT)
|
11
|
+
expect(described_class.instance.events)
|
12
|
+
.to eql(described_class::DEFAULT_EVENTS)
|
13
|
+
expect(described_class.instance.scrub_columns)
|
14
|
+
.to eql(described_class::SCRUB_COLUMNS)
|
15
|
+
expect(described_class.instance.unknown_whodunnit)
|
16
|
+
.to eql(described_class::UNKNOWN_WHODUNNIT)
|
17
|
+
|
18
|
+
expect(described_class.instance.drop_id_suffix)
|
19
|
+
.to be true
|
20
|
+
expect(described_class.instance.filter_non_changes)
|
21
|
+
.to be true
|
8
22
|
end
|
9
23
|
end
|
10
24
|
|
@@ -4,10 +4,10 @@ module PaperTrailScrapbook
|
|
4
4
|
::RSpec.describe LifeHistory do
|
5
5
|
before do
|
6
6
|
PaperTrailScrapbook.config.whodunnit_class = Person
|
7
|
-
PaperTrail.whodunnit = person.id
|
7
|
+
PaperTrail.request.whodunnit = person.id
|
8
8
|
end
|
9
9
|
|
10
|
-
let(:person) {
|
10
|
+
let(:person) {Person.create(name: 'The Tim Man')}
|
11
11
|
|
12
12
|
let(:book) do
|
13
13
|
b = Book.new(title: 'How the Grinch stole Xmas')
|
@@ -27,8 +27,8 @@ module PaperTrailScrapbook
|
|
27
27
|
a
|
28
28
|
end
|
29
29
|
|
30
|
-
let(:object) {
|
31
|
-
let(:subject) {
|
30
|
+
let(:object) {described_class.new(target)}
|
31
|
+
let(:subject) {object.story}
|
32
32
|
|
33
33
|
describe '#story' do
|
34
34
|
it 'provides a whole story' do
|
@@ -56,7 +56,7 @@ module PaperTrailScrapbook
|
|
56
56
|
target.save!
|
57
57
|
|
58
58
|
expect(subject)
|
59
|
-
|
59
|
+
.to match(/How the Grinch stole Xmas\[\d+\] was \*removed\*/)
|
60
60
|
end
|
61
61
|
|
62
62
|
context 'it handles missing whodunnit record' do
|
@@ -66,7 +66,7 @@ module PaperTrailScrapbook
|
|
66
66
|
person.destroy
|
67
67
|
|
68
68
|
expect(subject)
|
69
|
-
|
69
|
+
.to match(/\*missing \(#{pid}\)\* created the following/)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -74,7 +74,7 @@ module PaperTrailScrapbook
|
|
74
74
|
it 'allows for a custom invalid_whodunnit handler' do
|
75
75
|
config = PaperTrailScrapbook.config
|
76
76
|
handler = config.invalid_whodunnit
|
77
|
-
config.invalid_whodunnit = proc {
|
77
|
+
config.invalid_whodunnit = proc {|w| "*WHO (#{w})*"}
|
78
78
|
|
79
79
|
target
|
80
80
|
pid = person.id
|
@@ -85,6 +85,14 @@ module PaperTrailScrapbook
|
|
85
85
|
config.invalid_whodunnit = handler
|
86
86
|
end
|
87
87
|
end
|
88
|
+
|
89
|
+
context 'no papertrail' do
|
90
|
+
let(:target) {Elephant.create!}
|
91
|
+
it 'has none' do
|
92
|
+
target
|
93
|
+
expect(subject).to eql ''
|
94
|
+
end
|
95
|
+
end
|
88
96
|
end
|
89
97
|
end
|
90
98
|
end
|
@@ -4,39 +4,42 @@ module PaperTrailScrapbook
|
|
4
4
|
RSpec.describe UserJournal do
|
5
5
|
before do
|
6
6
|
PaperTrailScrapbook.config.whodunnit_class = Person
|
7
|
-
PaperTrail.whodunnit = person.id
|
7
|
+
PaperTrail.request.whodunnit = person.id
|
8
8
|
|
9
9
|
a_ship
|
10
10
|
end
|
11
11
|
|
12
|
-
let(:name) {
|
13
|
-
let(:title) {
|
14
|
-
let(:a_name) {
|
15
|
-
let(:changes) {
|
16
|
-
let(:b_changes) {
|
12
|
+
let(:name) {'The Tim Man'}
|
13
|
+
let(:title) {'How the Grinch stole Xmas'}
|
14
|
+
let(:a_name) {'Dr. Seuss'}
|
15
|
+
let(:changes) {'made the following changes:'}
|
16
|
+
let(:b_changes) {'made the following Book changes:'}
|
17
17
|
|
18
|
-
let(:book) {
|
19
|
-
let(:author) {
|
20
|
-
let(:a_ship) {
|
18
|
+
let(:book) {Book.create!(title: title)}
|
19
|
+
let(:author) {Person.create!(name: a_name)}
|
20
|
+
let(:a_ship) {Authorship.create!(book: book, author: author)}
|
21
21
|
let(:person) do
|
22
|
-
who = PaperTrail.whodunnit
|
23
|
-
PaperTrail.whodunnit = nil
|
22
|
+
who = PaperTrail.request.whodunnit
|
23
|
+
PaperTrail.request.whodunnit = nil
|
24
24
|
p = Person.new(name: name)
|
25
25
|
p.save!
|
26
|
-
PaperTrail.whodunnit = who
|
26
|
+
PaperTrail.request.whodunnit = who
|
27
27
|
p
|
28
28
|
end
|
29
|
-
let(:format) {
|
30
|
-
let(:object) {
|
31
|
-
let(:subject) {
|
32
|
-
let(:f_starts) {
|
33
|
-
let(:f_ends) {
|
29
|
+
let(:format) {PaperTrailScrapbook.config.time_format}
|
30
|
+
let(:object) {described_class.new(person)}
|
31
|
+
let(:subject) {object.story}
|
32
|
+
let(:f_starts) {starts.strftime(format).squeeze(' ')}
|
33
|
+
let(:f_ends) {ends.strftime(format).squeeze(' ')}
|
34
34
|
|
35
35
|
describe '#story' do
|
36
36
|
context 'with a provided user' do
|
37
37
|
it 'provides a whole story' do
|
38
|
+
#expect(object.starts).to eql 'Thursday, 01 Jan 1970 at 12:00 AM'
|
39
|
+
#expect(object.ends).to eql Time.current.in_time_zone
|
40
|
+
|
38
41
|
expect(subject)
|
39
|
-
|
42
|
+
.to match(/Between .* and .*, #{name} #{changes}/)
|
40
43
|
expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
|
41
44
|
expect(subject).to match(/ • title: #{title}/)
|
42
45
|
expect(subject).to match(/On .*, created Person\[#{author.id}\]:/)
|
@@ -66,48 +69,48 @@ module PaperTrailScrapbook
|
|
66
69
|
a_ship.book = nil
|
67
70
|
a_ship.save!
|
68
71
|
|
69
|
-
expect(subject).to match(/#{title}\[
|
72
|
+
expect(subject).to match(/#{title}\[\d+\] was \*removed\*/)
|
70
73
|
end
|
71
74
|
end
|
72
75
|
|
73
76
|
context 'with a provided class' do
|
74
|
-
let(:object) {
|
77
|
+
let(:object) {described_class.new(person, klass: Book)}
|
75
78
|
|
76
79
|
it 'provides a story for provided class' do
|
77
80
|
expect(subject)
|
78
|
-
|
81
|
+
.to match(/Between .* and .*, #{name} #{b_changes}/)
|
79
82
|
expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
|
80
83
|
expect(subject).to match(/ • title: #{title}/)
|
81
84
|
expect(subject).not_to match(/On .*, created Person\[#{author.id}\]:/)
|
82
85
|
expect(subject).not_to match(/ • name: #{a_name}/)
|
83
86
|
expect(subject)
|
84
|
-
|
87
|
+
.not_to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
|
85
88
|
end
|
86
89
|
end
|
87
90
|
|
88
91
|
context 'with start time and class but no end time' do
|
89
|
-
let(:object) {
|
90
|
-
let(:starts) {
|
92
|
+
let(:object) {described_class.new(person, klass: Book, start: starts)}
|
93
|
+
let(:starts) {Time.current.advance(minutes: -5)}
|
91
94
|
|
92
95
|
it 'provides a story with start time' do
|
93
96
|
expect(subject)
|
94
|
-
|
97
|
+
.to match(/Between #{f_starts} and .*, #{name} #{b_changes}/)
|
95
98
|
expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
|
96
99
|
expect(subject).to match(/ • title: #{title}/)
|
97
100
|
expect(subject).not_to match(/On .*, created Person\[#{author.id}\]:/)
|
98
101
|
expect(subject).not_to match(/ • name: #{a_name}/)
|
99
102
|
expect(subject)
|
100
|
-
|
103
|
+
.not_to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
|
101
104
|
end
|
102
105
|
end
|
103
106
|
|
104
107
|
context 'with end time but no start time and no class' do
|
105
|
-
let(:ends) {
|
106
|
-
let(:object) {
|
108
|
+
let(:ends) {Time.current.advance(minutes: 5)}
|
109
|
+
let(:object) {described_class.new(person, end: ends)}
|
107
110
|
|
108
111
|
it 'provides a story with end time in the future' do
|
109
112
|
expect(subject)
|
110
|
-
|
113
|
+
.to match(/Between .* and #{f_ends}, #{name} #{changes}/)
|
111
114
|
expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
|
112
115
|
expect(subject).to match(/ • title: #{title}/)
|
113
116
|
expect(subject).to match(/On .*, created Person\[#{author.id}\]:/)
|
@@ -116,7 +119,7 @@ module PaperTrailScrapbook
|
|
116
119
|
end
|
117
120
|
|
118
121
|
describe 'with end time in past' do
|
119
|
-
let(:ends) {
|
122
|
+
let(:ends) {Time.current.advance(minutes: -5)}
|
120
123
|
|
121
124
|
it 'provides a story with end time in the past' do
|
122
125
|
expect(subject).not_to match(/#{name} created the following Person/)
|
@@ -128,13 +131,13 @@ module PaperTrailScrapbook
|
|
128
131
|
end
|
129
132
|
|
130
133
|
context 'with start and end times but no class' do
|
131
|
-
let(:starts) {
|
132
|
-
let(:ends) {
|
133
|
-
let(:object) {
|
134
|
+
let(:starts) {Time.current.advance(minutes: -4)}
|
135
|
+
let(:ends) {starts.advance(hours: 1)}
|
136
|
+
let(:object) {described_class.new(person, start: starts, end: ends)}
|
134
137
|
|
135
138
|
it 'provides a story with start and end times' do
|
136
139
|
expect(subject)
|
137
|
-
|
140
|
+
.to match(/Between .* and .*, #{name} #{changes}/)
|
138
141
|
expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
|
139
142
|
expect(subject).to match(/ • title: #{title}/)
|
140
143
|
expect(subject).to match(/On .*, created Person\[#{author.id}\]:/)
|
@@ -145,8 +148,8 @@ module PaperTrailScrapbook
|
|
145
148
|
end
|
146
149
|
|
147
150
|
context 'with start time, end time, and a class' do
|
148
|
-
let(:starts) {
|
149
|
-
let(:ends) {
|
151
|
+
let(:starts) {Time.current.advance(minutes: -4)}
|
152
|
+
let(:ends) {starts.advance(hours: 1)}
|
150
153
|
let(:object) do
|
151
154
|
described_class.new(person, klass: Book, start: starts, end: ends)
|
152
155
|
end
|
@@ -154,25 +157,34 @@ module PaperTrailScrapbook
|
|
154
157
|
describe 'when time range covers current time' do
|
155
158
|
it 'provides a story for provided class with start and end times' do
|
156
159
|
expect(subject)
|
157
|
-
|
160
|
+
.to match(/Between #{f_starts} and #{f_ends}, #{name} #{b_changes}/)
|
158
161
|
expect(subject).to match(/On .*, created Book\[#{book.id}\]:/)
|
159
162
|
expect(subject).to match(/ • title: #{title}/)
|
160
163
|
expect(subject)
|
161
|
-
|
164
|
+
.not_to match(/On .*, created Person\[#{author.id}\]:/)
|
162
165
|
expect(subject).not_to match(/ • name: #{a_name}/)
|
163
166
|
expect(subject)
|
164
|
-
|
167
|
+
.not_to match(/On .*, created Authorship\[#{a_ship.id}\]:/)
|
165
168
|
end
|
166
169
|
end
|
167
170
|
|
168
171
|
describe 'when time range is completely in the future' do
|
169
|
-
let(:starts) {
|
172
|
+
let(:starts) {Time.current.advance(minutes: 4)}
|
170
173
|
|
171
174
|
it 'provides a story' do
|
172
175
|
expect(subject).to eql("Between #{f_starts} and #{f_ends}, #{name}"\
|
173
176
|
" #{b_changes}\n\nNo history".squeeze(' '))
|
174
177
|
end
|
175
178
|
end
|
179
|
+
|
180
|
+
describe 'when no time range' do
|
181
|
+
let(:starts) {nil}
|
182
|
+
let(:ends) {nil}
|
183
|
+
it 'provides a story' do
|
184
|
+
expect(subject).to match("Between Thursday, 01 Jan 1970 at 12:00 AM and #{Time.current.in_time_zone.strftime(format).squeeze(' ')}, #{name}"\
|
185
|
+
" #{b_changes}\n\n".squeeze(' '))
|
186
|
+
end
|
187
|
+
end
|
176
188
|
end
|
177
189
|
end
|
178
190
|
end
|
@@ -3,8 +3,8 @@ require 'ostruct'
|
|
3
3
|
|
4
4
|
module PaperTrailScrapbook
|
5
5
|
RSpec.describe VersionHelpers do
|
6
|
-
let(:person) {
|
7
|
-
let(:book) {
|
6
|
+
let(:person) {Person.create!(name: 'The Tim Man')}
|
7
|
+
let(:book) {Book.create!(title: 'How the Grinch stole Xmas')}
|
8
8
|
let(:version) do
|
9
9
|
OpenStruct.new(event: 'create',
|
10
10
|
item_type: 'Book',
|
@@ -12,12 +12,12 @@ module PaperTrailScrapbook
|
|
12
12
|
created_at: Time.current,
|
13
13
|
version_author: person.id)
|
14
14
|
end
|
15
|
-
let(:config) {
|
16
|
-
let(:subject) {
|
15
|
+
let(:config) {PaperTrailScrapbook.config}
|
16
|
+
let(:subject) {JournalEntry.new(version)}
|
17
17
|
|
18
18
|
before do
|
19
19
|
PaperTrailScrapbook.config.whodunnit_class = Person
|
20
|
-
PaperTrail.whodunnit = person.id
|
20
|
+
PaperTrail.request.whodunnit = person.id
|
21
21
|
end
|
22
22
|
|
23
23
|
describe '#model' do
|
@@ -59,7 +59,7 @@ module PaperTrailScrapbook
|
|
59
59
|
describe '#whenn' do
|
60
60
|
it 'returns version created_at' do
|
61
61
|
expect(subject.whenn)
|
62
|
-
|
62
|
+
.to eql(version.created_at.strftime(config.time_format))
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -67,6 +67,18 @@ module PaperTrailScrapbook
|
|
67
67
|
it 'returns expected value' do
|
68
68
|
expect(subject.kind).to eql('created')
|
69
69
|
end
|
70
|
+
|
71
|
+
context 'garbage' do
|
72
|
+
let(:subject) {JournalEntry.new(version)}
|
73
|
+
it 'returns expected value' do
|
74
|
+
x = PaperTrailScrapbook.config.events
|
75
|
+
PaperTrailScrapbook.config.events = {}
|
76
|
+
|
77
|
+
expect {subject.kind}.to raise_error(ArgumentError, 'incorrect event:create')
|
78
|
+
|
79
|
+
PaperTrailScrapbook.config.events = x
|
80
|
+
end
|
81
|
+
end
|
70
82
|
end
|
71
83
|
|
72
84
|
describe '#who' do
|