paper_trail_diff 0.7.0 → 0.7.1
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 +21 -0
- data/README.md +7 -2
- data/lib/paper_trail_diff/live_endpoint_provider.rb +21 -2
- data/lib/paper_trail_diff/root_version_selection.rb +6 -1
- data/lib/paper_trail_diff/time_activity_timeline_builder.rb +3 -1
- data/lib/paper_trail_diff/version.rb +1 -1
- data/sig/generated/paper_trail_diff/live_endpoint_provider.rbs +9 -0
- data/sig/generated/paper_trail_diff/root_version_selection.rbs +3 -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: a84116670d1dd65a6cf569a266ccc8587a9eab4e65d38cf84df485c26d33d9dc
|
|
4
|
+
data.tar.gz: 2c8ddb0fd1b2203e644f3c50ed24da33ed25cb4c4240665eab85368cd4534f22
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e32ecbedd636456388129cc664cf688b0aa909d5c05b45124eb1a6bd82dabbffa96557fe5efc946e93fe459234d3c4c5ce9af11f39228ddab55228f5a53e2f9
|
|
7
|
+
data.tar.gz: 1b29680ab942334fd98ce5cc0a4f2bc823fd180b258282e8be2c07ba784c9c3b5aa9edc76e0e118fec3baabcfbc94344c208aef116702874c0eb4dd226cddcac
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
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.7.1] - 2026-08-11
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Load each current endpoint once per call instead of once per request for it,
|
|
11
|
+
so `close_on: :current` no longer costs a live read per root and
|
|
12
|
+
`analyze_many` keeps the flat query cost it exists for. A twenty-root batch
|
|
13
|
+
closing on current state issued 43 queries against the 3 a closed window
|
|
14
|
+
needs. Every root in one result now also reflects the same instant rather
|
|
15
|
+
than whenever its turn came.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Name the way out in `IncompleteTimeRangeError`: the message now points at
|
|
20
|
+
`close_on: :current` and at narrowing the window. Its previous advice — add a
|
|
21
|
+
checkpoint after the window — cannot be followed by a report about the
|
|
22
|
+
present, which is the case that raises it most.
|
|
23
|
+
- Correct the `reload_live_endpoints:` documentation, which claimed `timeline`
|
|
24
|
+
and `analyze` never read live state. They do under `close_on: :current`, and
|
|
25
|
+
they do not accept the option.
|
|
26
|
+
|
|
6
27
|
## [0.7.0] - 2026-08-11
|
|
7
28
|
|
|
8
29
|
### Added
|
data/README.md
CHANGED
|
@@ -242,8 +242,13 @@ must observe one atomic snapshot.
|
|
|
242
242
|
|
|
243
243
|
`compare`, `compare_many`, and `activity_timeline(..., to: record)` reload
|
|
244
244
|
current endpoints by default. A caller that already owns a consistent, fully
|
|
245
|
-
preloaded graph may opt out.
|
|
246
|
-
|
|
245
|
+
preloaded graph may opt out. Each endpoint is loaded once per call, so a batch
|
|
246
|
+
that resolves every root up front and then reads one of them again pays
|
|
247
|
+
nothing the second time, and every root in one result reflects the same instant.
|
|
248
|
+
|
|
249
|
+
The option is not accepted by `timeline`, `analyze`, or `analyze_many`. Those
|
|
250
|
+
are bounded by versions and read live state only under
|
|
251
|
+
[`close_on: :current`](#reporting-up-to-now), which always reloads:
|
|
247
252
|
|
|
248
253
|
```ruby
|
|
249
254
|
orders = Order.where(id: order_ids).preload(line_items: :product).to_a
|
|
@@ -23,18 +23,37 @@ module PaperTrailDiff
|
|
|
23
23
|
else
|
|
24
24
|
PreloadedEndpointBatchLoader.new(tree: tree, traversal: traversal)
|
|
25
25
|
end
|
|
26
|
+
@loaded = {} #: Hash[identity, untyped]
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
# Loads each endpoint at most once for the life of this provider, which is
|
|
30
|
+
# one public call. A batch that resolves every root up front and then asks
|
|
31
|
+
# again for one of them costs nothing the second time, and every root in the
|
|
32
|
+
# result reflects the same instant rather than whenever its turn came.
|
|
28
33
|
#: (Array[untyped]) -> Hash[identity, untyped]
|
|
29
34
|
def call(records)
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
missing = records.reject { |record| @loaded.key?(Endpoint.identity(record)) }
|
|
36
|
+
return known(records) if missing.empty?
|
|
37
|
+
|
|
38
|
+
loaded = Instrumentation.instrument('load_live_endpoints', payload(missing)) do
|
|
39
|
+
@loader.call(missing)
|
|
32
40
|
end
|
|
41
|
+
@loaded.merge!(loaded)
|
|
42
|
+
known(records)
|
|
33
43
|
end
|
|
34
44
|
|
|
35
45
|
private
|
|
36
46
|
|
|
37
47
|
# @rbs @loader: untyped
|
|
48
|
+
# @rbs @loaded: Hash[identity, untyped]
|
|
49
|
+
|
|
50
|
+
#: (Array[untyped]) -> Hash[identity, untyped]
|
|
51
|
+
def known(records)
|
|
52
|
+
records.to_h do |record|
|
|
53
|
+
identity = Endpoint.identity(record)
|
|
54
|
+
[identity, @loaded.fetch(identity)]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
38
57
|
|
|
39
58
|
#: (Array[untyped]) -> Hash[Symbol, untyped]
|
|
40
59
|
def payload(records)
|
|
@@ -11,7 +11,12 @@ module PaperTrailDiff
|
|
|
11
11
|
# Both the single-record and the batched selectors resolve this here, because
|
|
12
12
|
# the rule is subtle enough that two copies of it would drift apart.
|
|
13
13
|
class RootVersionSelection
|
|
14
|
-
|
|
14
|
+
# An error is a specification, so it names the way out as well as the wall.
|
|
15
|
+
# A window ending at the present can never gain a later version, which makes
|
|
16
|
+
# "write a checkpoint afterwards" impossible advice on its own.
|
|
17
|
+
INCOMPLETE = 'time range requires a later root version to reconstruct its ' \
|
|
18
|
+
'final change: pass close_on: :current to end at current state, ' \
|
|
19
|
+
'or narrow the window to end before the last recorded version'
|
|
15
20
|
|
|
16
21
|
#: (in_range: Array[untyped], selected: Array[untyped], after_range: untyped, windowed: bool, ?context_required: bool, ?filtered: bool, ?live_endpoint: untyped) -> void
|
|
17
22
|
def initialize( # rubocop:disable Metrics/ParameterLists
|
|
@@ -187,7 +187,9 @@ module PaperTrailDiff
|
|
|
187
187
|
return true if terminal_destroy?(selected.last)
|
|
188
188
|
return true if plan.closing_record
|
|
189
189
|
|
|
190
|
-
message = 'time range requires a later activity boundary to reconstruct its
|
|
190
|
+
message = 'time range requires a later activity boundary to reconstruct its ' \
|
|
191
|
+
'final change: pass close_on: :current to end at current state, ' \
|
|
192
|
+
'or narrow the window to end before the last recorded boundary'
|
|
191
193
|
raise IncompleteTimeRangeError, message
|
|
192
194
|
end
|
|
193
195
|
|
|
@@ -10,13 +10,22 @@ module PaperTrailDiff
|
|
|
10
10
|
# : (tree: AssociationTree, traversal: AssociationTraversal, reload: bool) -> void
|
|
11
11
|
def initialize: (tree: AssociationTree, traversal: AssociationTraversal, reload: bool) -> void
|
|
12
12
|
|
|
13
|
+
# Loads each endpoint at most once for the life of this provider, which is
|
|
14
|
+
# one public call. A batch that resolves every root up front and then asks
|
|
15
|
+
# again for one of them costs nothing the second time, and every root in the
|
|
16
|
+
# result reflects the same instant rather than whenever its turn came.
|
|
13
17
|
# : (Array[untyped]) -> Hash[identity, untyped]
|
|
14
18
|
def call: (Array[untyped]) -> Hash[identity, untyped]
|
|
15
19
|
|
|
16
20
|
private
|
|
17
21
|
|
|
22
|
+
@loaded: Hash[identity, untyped]
|
|
23
|
+
|
|
18
24
|
@loader: untyped
|
|
19
25
|
|
|
26
|
+
# : (Array[untyped]) -> Hash[identity, untyped]
|
|
27
|
+
def known: (Array[untyped]) -> Hash[identity, untyped]
|
|
28
|
+
|
|
20
29
|
# : (Array[untyped]) -> Hash[Symbol, untyped]
|
|
21
30
|
def payload: (Array[untyped]) -> Hash[Symbol, untyped]
|
|
22
31
|
end
|
|
@@ -10,6 +10,9 @@ module PaperTrailDiff
|
|
|
10
10
|
# Both the single-record and the batched selectors resolve this here, because
|
|
11
11
|
# the rule is subtle enough that two copies of it would drift apart.
|
|
12
12
|
class RootVersionSelection
|
|
13
|
+
# An error is a specification, so it names the way out as well as the wall.
|
|
14
|
+
# A window ending at the present can never gain a later version, which makes
|
|
15
|
+
# "write a checkpoint afterwards" impossible advice on its own.
|
|
13
16
|
INCOMPLETE: ::String
|
|
14
17
|
|
|
15
18
|
# : (in_range: Array[untyped], selected: Array[untyped], after_range: untyped, windowed: bool, ?context_required: bool, ?filtered: bool, ?live_endpoint: untyped) -> void
|
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.7.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Williams
|
|
@@ -200,11 +200,11 @@ licenses:
|
|
|
200
200
|
metadata:
|
|
201
201
|
allowed_push_host: https://rubygems.org
|
|
202
202
|
bug_tracker_uri: https://github.com/aheathwilliams/paper_trail_diff/issues
|
|
203
|
-
changelog_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.7.
|
|
204
|
-
documentation_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.7.
|
|
203
|
+
changelog_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.7.1/CHANGELOG.md
|
|
204
|
+
documentation_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.7.1/README.md
|
|
205
205
|
homepage_uri: https://github.com/aheathwilliams/paper_trail_diff
|
|
206
206
|
rubygems_mfa_required: 'true'
|
|
207
|
-
source_code_uri: https://github.com/aheathwilliams/paper_trail_diff/tree/v0.7.
|
|
207
|
+
source_code_uri: https://github.com/aheathwilliams/paper_trail_diff/tree/v0.7.1
|
|
208
208
|
rdoc_options: []
|
|
209
209
|
require_paths:
|
|
210
210
|
- lib
|