paper_trail_scrapbook 0.1.13 → 0.1.15
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/Gemfile.lock +4 -6
- data/README.md +8 -0
- data/lib/paper_trail_scrapbook/changes.rb +44 -15
- data/lib/paper_trail_scrapbook/config.rb +3 -1
- data/lib/paper_trail_scrapbook/life_history.rb +6 -2
- data/lib/paper_trail_scrapbook/user_journal.rb +5 -1
- data/lib/paper_trail_scrapbook/version.rb +1 -1
- data/paper_trail_scrapbook.gemspec +1 -1
- data/spec/dummy_app/app/models/custom_primary_key_record.rb +1 -1
- data/spec/dummy_app/app/models/document.rb +1 -1
- data/spec/dummy_app/app/models/kitchen/banana.rb +1 -1
- data/spec/dummy_app/app/models/post.rb +1 -1
- data/spec/dummy_app/config/initializers/paper_trail.rb +0 -2
- data/spec/paper_trail_scrapbook/life_history_spec.rb +47 -2
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3cd11a4eeaf7c668fe41fe854748cf0f2e8fcaed
|
|
4
|
+
data.tar.gz: a1143aad24dcaa956b19fd47142e7e52b52bc8c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d8ef81dbaa77a0a90944adc34086ef4482fa6a97304490f4f929520465e7b56398868beb04f0ad433742b2d94fed78d019b63a36603ccd62d08ecba4b022c6a
|
|
7
|
+
data.tar.gz: ff4accb4de852aaacff14195e42b520ee4eac4f3a95370d8e43d24e1aa7492c3afd752af0e68524757438a14cb5383c1f8f256713bfb20c9d51ab8cc6a5962e4
|
data/Gemfile.lock
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
paper_trail_scrapbook (0.1.
|
|
4
|
+
paper_trail_scrapbook (0.1.15)
|
|
5
5
|
activerecord
|
|
6
6
|
adamantium
|
|
7
7
|
concord
|
|
8
|
-
paper_trail (>= 5.2, <=
|
|
8
|
+
paper_trail (>= 5.2, <= 10.2.1)
|
|
9
9
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
@@ -100,11 +100,9 @@ GEM
|
|
|
100
100
|
rspec-core (>= 3.4.0, < 3.7.0)
|
|
101
101
|
nokogiri (1.10.2)
|
|
102
102
|
mini_portile2 (~> 2.4.0)
|
|
103
|
-
paper_trail (
|
|
104
|
-
activerecord (>= 4.2, <
|
|
105
|
-
paper_trail-association_tracking (< 2)
|
|
103
|
+
paper_trail (10.2.1)
|
|
104
|
+
activerecord (>= 4.2, < 6.1)
|
|
106
105
|
request_store (~> 1.1)
|
|
107
|
-
paper_trail-association_tracking (1.1.1)
|
|
108
106
|
parallel (1.12.1)
|
|
109
107
|
parser (2.4.0.2)
|
|
110
108
|
ast (~> 2.3)
|
data/README.md
CHANGED
|
@@ -60,6 +60,14 @@ end
|
|
|
60
60
|
PaperTrailScrapbook.config.whodunnit_class = WhoDidIt
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
You also have the option of seeing the most recent changes first (default is chronological)
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
|
|
67
|
+
config.recent_first = true
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
|
|
63
71
|
### Life Story
|
|
64
72
|
|
|
65
73
|
The `LifeStory` module provides a complete history of an object (limited by the
|
|
@@ -8,6 +8,11 @@ module PaperTrailScrapbook
|
|
|
8
8
|
class Changes
|
|
9
9
|
include Concord.new(:version)
|
|
10
10
|
include Adamantium::Flat
|
|
11
|
+
include PaperTrailScrapbook::VersionHelpers
|
|
12
|
+
|
|
13
|
+
POLYMORPH_BT_INDICATOR = '*'
|
|
14
|
+
|
|
15
|
+
delegate :object_changes, to: :version
|
|
11
16
|
|
|
12
17
|
def initialize(*)
|
|
13
18
|
super
|
|
@@ -23,10 +28,11 @@ module PaperTrailScrapbook
|
|
|
23
28
|
# @return [String] Summary analysis of changes
|
|
24
29
|
#
|
|
25
30
|
def change_log
|
|
26
|
-
text =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
text =
|
|
32
|
+
changes
|
|
33
|
+
.map { |k, v| digest(k, v) }
|
|
34
|
+
.compact
|
|
35
|
+
.join("\n")
|
|
30
36
|
|
|
31
37
|
text = text.gsub(' id:', ':') if PaperTrailScrapbook.config.drop_id_suffix
|
|
32
38
|
text
|
|
@@ -34,9 +40,13 @@ module PaperTrailScrapbook
|
|
|
34
40
|
|
|
35
41
|
private
|
|
36
42
|
|
|
43
|
+
def polymorphic?(x)
|
|
44
|
+
x.to_s.start_with?(POLYMORPH_BT_INDICATOR)
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
def digest(key, values)
|
|
38
48
|
old, new = values
|
|
39
|
-
return if old.nil? && (new.nil? || new.eql?('')) || (old == new)
|
|
49
|
+
return if old.nil? && (new.nil? || new.eql?('')) || (old == new && !creating?)
|
|
40
50
|
|
|
41
51
|
"#{BULLET} #{key.tr('_', ' ')}: #{detailed_analysis(key, new, old)}"
|
|
42
52
|
end
|
|
@@ -63,17 +73,40 @@ module PaperTrailScrapbook
|
|
|
63
73
|
return '*empty*' unless value
|
|
64
74
|
|
|
65
75
|
begin
|
|
66
|
-
|
|
76
|
+
assoc_target(key).find(value).to_s.to_s + "[#{value}]"
|
|
67
77
|
rescue StandardError
|
|
68
78
|
"*not found*[#{value}]"
|
|
69
79
|
end
|
|
70
80
|
end
|
|
71
81
|
|
|
82
|
+
def assoc_target(key)
|
|
83
|
+
x = build_associations[key]
|
|
84
|
+
return x unless polymorphic?(x)
|
|
85
|
+
ref = x[1..-1] + '_type'
|
|
86
|
+
|
|
87
|
+
# try object changes to see if the belongs_to class is specified
|
|
88
|
+
latest_class = changes[ref]&.last
|
|
89
|
+
|
|
90
|
+
if latest_class.nil? && create?
|
|
91
|
+
# try the db default class
|
|
92
|
+
# for creates where the object changes do not specify this it
|
|
93
|
+
# is most likely because the default == type selected so
|
|
94
|
+
# the default was not changed and therefore is not in
|
|
95
|
+
# object changes
|
|
96
|
+
orig_instance = Object.const_get(version.item_type.classify).new
|
|
97
|
+
latest_class = orig_instance[ref.to_sym]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
Object.const_get(latest_class.classify)
|
|
101
|
+
end
|
|
102
|
+
|
|
72
103
|
def assoc_klass(name, options = {})
|
|
73
104
|
direct_class = options[:class_name]
|
|
74
|
-
|
|
105
|
+
poly = options[:polymorphic]
|
|
106
|
+
|
|
107
|
+
return direct_class if !poly && direct_class && !direct_class.is_a?(String)
|
|
75
108
|
|
|
76
|
-
Object.const_get((direct_class || name.to_s).classify)
|
|
109
|
+
poly ? POLYMORPH_BT_INDICATOR + name.to_s : Object.const_get((direct_class || name.to_s).classify)
|
|
77
110
|
rescue StandardError
|
|
78
111
|
Object.const_set(name.to_s.classify, Class.new)
|
|
79
112
|
end
|
|
@@ -86,16 +119,12 @@ module PaperTrailScrapbook
|
|
|
86
119
|
@build_associations ||=
|
|
87
120
|
Hash[
|
|
88
121
|
klass
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
122
|
+
.reflect_on_all_associations
|
|
123
|
+
.select { |a| a.macro.equal?(:belongs_to) }
|
|
124
|
+
.map { |x| [x.foreign_key.to_s, assoc_klass(x.name, x.options)] }
|
|
92
125
|
]
|
|
93
126
|
end
|
|
94
127
|
|
|
95
|
-
def object_changes
|
|
96
|
-
version.object_changes
|
|
97
|
-
end
|
|
98
|
-
|
|
99
128
|
def changes
|
|
100
129
|
@changes ||= if object_changes
|
|
101
130
|
YAML
|
|
@@ -22,7 +22,8 @@ module PaperTrailScrapbook
|
|
|
22
22
|
:drop_id_suffix,
|
|
23
23
|
:unknown_whodunnit,
|
|
24
24
|
:invalid_whodunnit,
|
|
25
|
-
:filter_non_changes
|
|
25
|
+
:filter_non_changes,
|
|
26
|
+
:recent_first
|
|
26
27
|
|
|
27
28
|
def initialize
|
|
28
29
|
@whodunnit_class = nil
|
|
@@ -33,6 +34,7 @@ module PaperTrailScrapbook
|
|
|
33
34
|
@invalid_whodunnit = proc { |w| "*missing (#{w})*" }
|
|
34
35
|
@drop_id_suffix = true
|
|
35
36
|
@filter_non_changes = true
|
|
37
|
+
@recent_first = false
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
end
|
|
@@ -23,13 +23,17 @@ module PaperTrailScrapbook
|
|
|
23
23
|
# @return [String] analyzed versions
|
|
24
24
|
#
|
|
25
25
|
def story
|
|
26
|
-
versions.map do |v|
|
|
26
|
+
x = versions.map do |v|
|
|
27
27
|
if primary?(v)
|
|
28
28
|
Chapter
|
|
29
29
|
else
|
|
30
30
|
SecondaryChapter
|
|
31
31
|
end.new(v).story
|
|
32
|
-
end.compact
|
|
32
|
+
end.compact
|
|
33
|
+
|
|
34
|
+
x.reverse! if PaperTrailScrapbook.config.recent_first
|
|
35
|
+
|
|
36
|
+
x.join("\n\n")
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
private
|
|
@@ -26,7 +26,11 @@ module PaperTrailScrapbook
|
|
|
26
26
|
def story
|
|
27
27
|
s = versions.map do |v|
|
|
28
28
|
JournalEntry.new(v).story
|
|
29
|
-
end.compact
|
|
29
|
+
end.compact
|
|
30
|
+
|
|
31
|
+
s.reverse! if PaperTrailScrapbook.config.recent_first
|
|
32
|
+
|
|
33
|
+
s.join("\n\n")
|
|
30
34
|
|
|
31
35
|
"#{preface}#{s.presence || 'No history'}"
|
|
32
36
|
end
|
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
|
|
|
21
21
|
gem.add_dependency 'activerecord'
|
|
22
22
|
gem.add_dependency 'adamantium'
|
|
23
23
|
gem.add_dependency 'concord'
|
|
24
|
-
gem.add_dependency 'paper_trail', ['>= 5.2', '<=
|
|
24
|
+
gem.add_dependency 'paper_trail', ['>= 5.2', '<= 10.2.1']
|
|
25
25
|
|
|
26
26
|
gem.add_development_dependency 'ffaker', '~> 2.5'
|
|
27
27
|
gem.add_development_dependency 'rake', '~> 10.4.2'
|
|
@@ -5,7 +5,7 @@ require 'securerandom'
|
|
|
5
5
|
class CustomPrimaryKeyRecord < ActiveRecord::Base
|
|
6
6
|
self.primary_key = :uuid
|
|
7
7
|
|
|
8
|
-
has_paper_trail class_name: 'CustomPrimaryKeyRecordVersion'
|
|
8
|
+
has_paper_trail versions: { class_name: 'CustomPrimaryKeyRecordVersion' }
|
|
9
9
|
|
|
10
10
|
# This default_scope is to test the case of the Version#item association
|
|
11
11
|
# not returning the item due to unmatched default_scope on the model.
|
|
@@ -61,7 +61,7 @@ module PaperTrailScrapbook
|
|
|
61
61
|
.to match(/How the Grinch stole Xmas\[\d+\] was \*removed\*/)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
context 'it handles missing whodunnit record' do
|
|
64
|
+
context 'when it handles missing whodunnit record' do
|
|
65
65
|
it 'provides a whole story with missing whodunnit record' do
|
|
66
66
|
target
|
|
67
67
|
pid = person.id
|
|
@@ -88,7 +88,7 @@ module PaperTrailScrapbook
|
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
context '
|
|
91
|
+
context 'without papertrail' do
|
|
92
92
|
let(:target) { Elephant.create! }
|
|
93
93
|
|
|
94
94
|
it 'has none' do
|
|
@@ -97,6 +97,15 @@ module PaperTrailScrapbook
|
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
+
context 'with polymorphic data' do
|
|
101
|
+
let(:target) { Whatchamajigger.create!(owner: author) }
|
|
102
|
+
|
|
103
|
+
it 'locates the proper model' do
|
|
104
|
+
expect(subject).to match(/Dr. Seuss\[/)
|
|
105
|
+
expect(subject).to match(/owner type: Person/)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
100
109
|
context 'with related data' do
|
|
101
110
|
before do
|
|
102
111
|
author
|
|
@@ -120,6 +129,42 @@ module PaperTrailScrapbook
|
|
|
120
129
|
expect(subject).to match(/author: Dr\. Seuss\[\d+\]/)
|
|
121
130
|
end
|
|
122
131
|
end
|
|
132
|
+
|
|
133
|
+
context 'recent first' do
|
|
134
|
+
before do
|
|
135
|
+
author
|
|
136
|
+
book
|
|
137
|
+
target
|
|
138
|
+
|
|
139
|
+
config = PaperTrailScrapbook.config
|
|
140
|
+
config.recent_first = true
|
|
141
|
+
config.time_format = '%A, %d %b %Y at %l:%M:%S.%9N %p'
|
|
142
|
+
|
|
143
|
+
def target.trailed_related_content
|
|
144
|
+
[book, author]
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
after do
|
|
149
|
+
config = PaperTrailScrapbook.config
|
|
150
|
+
config.recent_first = false
|
|
151
|
+
config.time_format = '%A, %d %b %Y at %l:%M %p'
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'includes related content history' do
|
|
155
|
+
expect(subject).to match(/created the following Person\[\d+\] info/)
|
|
156
|
+
expect(subject).to match(/name: Dr\. Seuss/)
|
|
157
|
+
|
|
158
|
+
expect(subject).to match(/created the following Book\[\d+\] info/)
|
|
159
|
+
expect(subject).to match(/title: How the Grinch stole Xmas/)
|
|
160
|
+
|
|
161
|
+
expect(subject.squish).to match(/created the following Book.*created the following Person/)
|
|
162
|
+
|
|
163
|
+
expect(subject).to match(/created the following Authorship info/)
|
|
164
|
+
expect(subject).to match(/book: How the Grinch stole Xmas\[\d+\]/)
|
|
165
|
+
expect(subject).to match(/author: Dr\. Seuss\[\d+\]/)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
123
168
|
end
|
|
124
169
|
end
|
|
125
170
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: paper_trail_scrapbook
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Timothy Chambers
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-04-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -61,7 +61,7 @@ dependencies:
|
|
|
61
61
|
version: '5.2'
|
|
62
62
|
- - "<="
|
|
63
63
|
- !ruby/object:Gem::Version
|
|
64
|
-
version:
|
|
64
|
+
version: 10.2.1
|
|
65
65
|
type: :runtime
|
|
66
66
|
prerelease: false
|
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -71,7 +71,7 @@ dependencies:
|
|
|
71
71
|
version: '5.2'
|
|
72
72
|
- - "<="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version:
|
|
74
|
+
version: 10.2.1
|
|
75
75
|
- !ruby/object:Gem::Dependency
|
|
76
76
|
name: ffaker
|
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|