paper_trail_diff 0.9.0 → 0.10.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 +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +49 -0
- data/lib/paper_trail_diff/diagnostics.rb +3 -1
- data/lib/paper_trail_diff/errors.rb +4 -0
- data/lib/paper_trail_diff/traversal_preparer.rb +25 -0
- data/lib/paper_trail_diff/version.rb +1 -1
- data/sig/generated/paper_trail_diff/errors.rbs +5 -0
- data/sig/generated/paper_trail_diff/traversal_preparer.rbs +10 -0
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 00ac8585544b9421fab5fa7cf677a5eddaae2867058beb2c7958ff0e8f917da5
|
|
4
|
+
data.tar.gz: 52e079e4f84b77ffd804fd452eaf4acc03618d2e7e685969faddcfe2f8c21eba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 400361a0bd6d3d55b3637a5fdcb42febf04ff62a81b75aba3848f70c62d1865f7983016c31bb34347dc50df5b2f66ccb0e44306e2a31990c3db5c04f3806813e
|
|
7
|
+
data.tar.gz: 0b8497f1dcb9ecd6e12b09e28b7a69fe17363d251308abffb824e8d1d0033572c4bee71351785ed425dc5e46c656d092612c7cb4c1ff33e69ad270d8e2e7260b
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file. The
|
|
4
4
|
project follows [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.10.0] - 2026-08-15
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Add `rake release:preflight`, which runs the full gate and then checks
|
|
11
|
+
everything that must hold before a tag: a clean working tree, a HEAD that
|
|
12
|
+
matches its upstream, an unused tag, a dated changelog section for the
|
|
13
|
+
version, and that the version is not already on RubyGems. It reports and
|
|
14
|
+
never tags, pushes, or publishes. An unreachable RubyGems is reported as
|
|
15
|
+
unknown rather than treated as "not published".
|
|
16
|
+
- Raise `PaperTrailDiff::UnversionedAssociationError` when a selected
|
|
17
|
+
association's target is not versioned and history is being reconstructed.
|
|
18
|
+
Such a comparison could only ever answer "nothing changed", which is a wrong
|
|
19
|
+
answer rather than an empty one. Live-to-live comparison reads current state
|
|
20
|
+
and is unaffected. `diagnose` now reports the same condition as an error
|
|
21
|
+
rather than a warning, so `ok?` no longer stays true for a comparison that
|
|
22
|
+
will raise.
|
|
23
|
+
- Document ActiveStorage attachments, which this reaches through:
|
|
24
|
+
`has_one_attached` points at models Rails owns and PaperTrail never versions,
|
|
25
|
+
so attachments are audited through a versioned model of your own that mirrors
|
|
26
|
+
the metadata onto ordinary columns.
|
|
27
|
+
|
|
6
28
|
## [0.9.0] - 2026-08-14
|
|
7
29
|
|
|
8
30
|
### Added
|
data/README.md
CHANGED
|
@@ -152,6 +152,55 @@ that touches a record and its children in quick succession will manage it — so
|
|
|
152
152
|
this is worth checking before trusting an association history. Recording
|
|
153
153
|
versions at sub-second precision separates them.
|
|
154
154
|
|
|
155
|
+
### ActiveStorage attachments
|
|
156
|
+
|
|
157
|
+
`has_one_attached` and `has_many_attached` point at `ActiveStorage::Attachment`
|
|
158
|
+
and `ActiveStorage::Blob`, which Rails owns and PaperTrail does not version.
|
|
159
|
+
There is no history behind them, so a historical comparison over those paths
|
|
160
|
+
raises `PaperTrailDiff::UnversionedAssociationError` rather than reporting that
|
|
161
|
+
nothing changed. Adding `has_paper_trail` to Rails' own models does not rescue
|
|
162
|
+
it either: `has_one_attached` is a scoped `has_one`, and reifying it from
|
|
163
|
+
history is ambiguous.
|
|
164
|
+
|
|
165
|
+
Audit the attachment through a model you own instead. Give it the facts worth
|
|
166
|
+
auditing as ordinary columns, and version it:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
class DocumentRevision < ApplicationRecord
|
|
170
|
+
belongs_to :attachable, polymorphic: true
|
|
171
|
+
has_one_attached :file
|
|
172
|
+
has_paper_trail
|
|
173
|
+
|
|
174
|
+
# Attaching a file writes to ActiveStorage's tables, not to this record, so
|
|
175
|
+
# nothing would be versioned without copying the facts across.
|
|
176
|
+
after_save :record_file_metadata, if: -> { file.attached? }
|
|
177
|
+
|
|
178
|
+
def record_file_metadata
|
|
179
|
+
blob = file.blob
|
|
180
|
+
return if filename == blob.filename.to_s && checksum == blob.checksum
|
|
181
|
+
|
|
182
|
+
update_columns(
|
|
183
|
+
filename: blob.filename.to_s, content_type: blob.content_type,
|
|
184
|
+
byte_size: blob.byte_size, checksum: blob.checksum
|
|
185
|
+
)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
class Article < ApplicationRecord
|
|
192
|
+
has_many :document_revisions, as: :attachable
|
|
193
|
+
has_paper_trail
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
PaperTrailDiff.compare(before, after, associations: [:document_revisions])
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The bytes stay in ActiveStorage; the *auditable facts* live where PaperTrail
|
|
200
|
+
can see them. Nothing about the column names matters to this gem — it reports
|
|
201
|
+
whichever columns changed. What matters is that replacing a file writes to a
|
|
202
|
+
versioned record, because attaching one on its own does not.
|
|
203
|
+
|
|
155
204
|
## Choosing an entry point
|
|
156
205
|
|
|
157
206
|
| You need | Call |
|
|
@@ -175,7 +175,9 @@ module PaperTrailDiff
|
|
|
175
175
|
def inspect_versioned_model(model_class, path)
|
|
176
176
|
return if model_versioned?(model_class)
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
# An error rather than a warning: `ok?` must not stay true for a
|
|
179
|
+
# comparison that cannot work, and this one raises at runtime.
|
|
180
|
+
add_error(
|
|
179
181
|
:unversioned_association_target,
|
|
180
182
|
"#{model_class.name} does not appear to have PaperTrail enabled",
|
|
181
183
|
path
|
|
@@ -35,6 +35,10 @@ module PaperTrailDiff
|
|
|
35
35
|
# Raised when a requested ActiveRecord association does not exist.
|
|
36
36
|
class UnknownAssociationError < Error; end
|
|
37
37
|
|
|
38
|
+
# Raised when a selected association's target is not versioned, so its history
|
|
39
|
+
# cannot be reconstructed at all.
|
|
40
|
+
class UnversionedAssociationError < Error; end
|
|
41
|
+
|
|
38
42
|
# Raised when a requested association macro is not supported.
|
|
39
43
|
class UnsupportedAssociationError < Error; end
|
|
40
44
|
|
|
@@ -17,6 +17,7 @@ module PaperTrailDiff
|
|
|
17
17
|
|
|
18
18
|
ensure_association_tracking! if historical
|
|
19
19
|
@traversal.validate!(model_class)
|
|
20
|
+
ensure_versioned_targets!(model_class) if historical
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
private
|
|
@@ -24,6 +25,30 @@ module PaperTrailDiff
|
|
|
24
25
|
# @rbs @tree: AssociationTree
|
|
25
26
|
# @rbs @traversal: AssociationTraversal
|
|
26
27
|
|
|
28
|
+
# A model PaperTrail never versioned has no history to reconstruct, so a
|
|
29
|
+
# comparison over it can only ever answer "nothing changed" -- which is a
|
|
30
|
+
# wrong answer rather than an empty one. Live-to-live comparison reads
|
|
31
|
+
# current state and is unaffected, so this applies to historical work only.
|
|
32
|
+
#: (untyped) -> void
|
|
33
|
+
def ensure_versioned_targets!(model_class)
|
|
34
|
+
@traversal.selected_reflections(model_class).each do |path, reflection|
|
|
35
|
+
# A polymorphic target is not known until a row names it; `diagnose`
|
|
36
|
+
# reports that separately rather than guessing here.
|
|
37
|
+
next if reflection.polymorphic?
|
|
38
|
+
next if versioned?(reflection.klass)
|
|
39
|
+
|
|
40
|
+
raise UnversionedAssociationError,
|
|
41
|
+
"association #{path} cannot be compared historically: " \
|
|
42
|
+
"#{reflection.klass.name} is not versioned. Add `has_paper_trail` to it, " \
|
|
43
|
+
'or mirror the fields you need onto a model that has it.'
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#: (untyped) -> bool
|
|
48
|
+
def versioned?(model_class)
|
|
49
|
+
model_class.respond_to?(:paper_trail_options) && !model_class.paper_trail_options.nil?
|
|
50
|
+
end
|
|
51
|
+
|
|
27
52
|
#: () -> void
|
|
28
53
|
def ensure_association_tracking!
|
|
29
54
|
paper_trail = Object.const_get(:PaperTrail) #: untyped
|
|
@@ -45,6 +45,11 @@ module PaperTrailDiff
|
|
|
45
45
|
class UnknownAssociationError < Error
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
# Raised when a selected association's target is not versioned, so its history
|
|
49
|
+
# cannot be reconstructed at all.
|
|
50
|
+
class UnversionedAssociationError < Error
|
|
51
|
+
end
|
|
52
|
+
|
|
48
53
|
# Raised when a requested association macro is not supported.
|
|
49
54
|
class UnsupportedAssociationError < Error
|
|
50
55
|
end
|
|
@@ -16,6 +16,16 @@ module PaperTrailDiff
|
|
|
16
16
|
|
|
17
17
|
@tree: AssociationTree
|
|
18
18
|
|
|
19
|
+
# A model PaperTrail never versioned has no history to reconstruct, so a
|
|
20
|
+
# comparison over it can only ever answer "nothing changed" -- which is a
|
|
21
|
+
# wrong answer rather than an empty one. Live-to-live comparison reads
|
|
22
|
+
# current state and is unaffected, so this applies to historical work only.
|
|
23
|
+
# : (untyped) -> void
|
|
24
|
+
def ensure_versioned_targets!: (untyped) -> void
|
|
25
|
+
|
|
26
|
+
# : (untyped) -> bool
|
|
27
|
+
def versioned?: (untyped) -> bool
|
|
28
|
+
|
|
19
29
|
# : () -> void
|
|
20
30
|
def ensure_association_tracking!: () -> void
|
|
21
31
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: paper_trail_diff
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Williams
|
|
@@ -202,11 +202,11 @@ licenses:
|
|
|
202
202
|
metadata:
|
|
203
203
|
allowed_push_host: https://rubygems.org
|
|
204
204
|
bug_tracker_uri: https://github.com/aheathwilliams/paper_trail_diff/issues
|
|
205
|
-
changelog_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.
|
|
206
|
-
documentation_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.
|
|
205
|
+
changelog_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.10.0/CHANGELOG.md
|
|
206
|
+
documentation_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.10.0/README.md
|
|
207
207
|
homepage_uri: https://github.com/aheathwilliams/paper_trail_diff
|
|
208
208
|
rubygems_mfa_required: 'true'
|
|
209
|
-
source_code_uri: https://github.com/aheathwilliams/paper_trail_diff/tree/v0.
|
|
209
|
+
source_code_uri: https://github.com/aheathwilliams/paper_trail_diff/tree/v0.10.0
|
|
210
210
|
rdoc_options: []
|
|
211
211
|
require_paths:
|
|
212
212
|
- lib
|