paper_trail_diff 0.3.1 → 0.5.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 +81 -0
- data/README.md +316 -32
- data/lib/paper_trail_diff/activity_boundary.rb +21 -0
- data/lib/paper_trail_diff/activity_event_route_finder.rb +24 -44
- data/lib/paper_trail_diff/activity_root_steps.rb +27 -0
- data/lib/paper_trail_diff/activity_snapshot_sequence.rb +8 -3
- data/lib/paper_trail_diff/activity_timeline_builder.rb +47 -32
- data/lib/paper_trail_diff/analysis.rb +9 -0
- data/lib/paper_trail_diff/analysis_batch.rb +74 -0
- data/lib/paper_trail_diff/batch_boundary_resolver.rb +137 -0
- data/lib/paper_trail_diff/batched_root_analyzer.rb +50 -0
- data/lib/paper_trail_diff/batched_root_versions.rb +131 -0
- data/lib/paper_trail_diff/collection_comparator.rb +14 -1
- data/lib/paper_trail_diff/collection_transition.rb +6 -3
- data/lib/paper_trail_diff/comparison_batch.rb +37 -13
- data/lib/paper_trail_diff/endpoint.rb +27 -2
- data/lib/paper_trail_diff/errors.rb +3 -0
- data/lib/paper_trail_diff/historical_association_reifier.rb +8 -5
- data/lib/paper_trail_diff/historical_snapshot_store.rb +12 -9
- data/lib/paper_trail_diff/paper_trail_adapter.rb +49 -33
- data/lib/paper_trail_diff/prepared_association_reifier.rb +8 -5
- data/lib/paper_trail_diff/prepared_edge_loader.rb +6 -4
- data/lib/paper_trail_diff/prepared_history_loader.rb +3 -2
- data/lib/paper_trail_diff/prepared_record_index.rb +90 -15
- data/lib/paper_trail_diff/snapshot.rb +26 -0
- data/lib/paper_trail_diff/snapshot_normalizer.rb +14 -2
- data/lib/paper_trail_diff/time_activity_timeline_builder.rb +52 -32
- data/lib/paper_trail_diff/time_version_range.rb +11 -0
- data/lib/paper_trail_diff/timeline_builder.rb +3 -3
- data/lib/paper_trail_diff/timeline_range.rb +66 -6
- data/lib/paper_trail_diff/traversal_preparer.rb +40 -0
- data/lib/paper_trail_diff/version.rb +1 -1
- data/lib/paper_trail_diff/version_association_candidate_scope.rb +46 -19
- data/lib/paper_trail_diff.rb +37 -5
- data/sig/generated/paper_trail_diff/activity_boundary.rbs +9 -0
- data/sig/generated/paper_trail_diff/activity_event_route_finder.rbs +10 -17
- data/sig/generated/paper_trail_diff/activity_root_steps.rbs +13 -0
- data/sig/generated/paper_trail_diff/activity_snapshot_sequence.rbs +2 -2
- data/sig/generated/paper_trail_diff/activity_timeline_builder.rbs +20 -10
- data/sig/generated/paper_trail_diff/analysis.rbs +5 -0
- data/sig/generated/paper_trail_diff/analysis_batch.rbs +42 -0
- data/sig/generated/paper_trail_diff/batch_boundary_resolver.rbs +62 -0
- data/sig/generated/paper_trail_diff/batched_root_analyzer.rbs +28 -0
- data/sig/generated/paper_trail_diff/batched_root_versions.rbs +62 -0
- data/sig/generated/paper_trail_diff/collection_comparator.rbs +7 -0
- data/sig/generated/paper_trail_diff/collection_transition.rbs +4 -1
- data/sig/generated/paper_trail_diff/comparison_batch.rbs +15 -0
- data/sig/generated/paper_trail_diff/endpoint.rbs +12 -0
- data/sig/generated/paper_trail_diff/errors.rbs +4 -0
- data/sig/generated/paper_trail_diff/historical_association_reifier.rbs +1 -1
- data/sig/generated/paper_trail_diff/historical_snapshot_store.rbs +2 -3
- data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +16 -7
- data/sig/generated/paper_trail_diff/prepared_association_reifier.rbs +1 -1
- data/sig/generated/paper_trail_diff/prepared_edge_loader.rbs +1 -0
- data/sig/generated/paper_trail_diff/prepared_history_loader.rbs +2 -2
- data/sig/generated/paper_trail_diff/prepared_record_index.rbs +39 -6
- data/sig/generated/paper_trail_diff/snapshot.rbs +20 -0
- data/sig/generated/paper_trail_diff/snapshot_normalizer.rbs +8 -0
- data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +21 -10
- data/sig/generated/paper_trail_diff/time_version_range.rbs +7 -0
- data/sig/generated/paper_trail_diff/timeline_builder.rbs +2 -2
- data/sig/generated/paper_trail_diff/timeline_range.rbs +35 -2
- data/sig/generated/paper_trail_diff/traversal_preparer.rbs +22 -0
- data/sig/generated/paper_trail_diff/version_association_candidate_scope.rbs +17 -4
- data/sig/generated/paper_trail_diff.rbs +13 -3
- metadata +16 -4
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/activity_root_steps.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Root checkpoint steps recovered from the snapshots an activity pass retained,
|
|
5
|
+
# so a combined result does not reconstruct the same boundaries twice.
|
|
6
|
+
module ActivityRootSteps
|
|
7
|
+
# : (Array[untyped], Hash[Array[untyped], RecordSnapshot?]) -> Array[Step]
|
|
8
|
+
def self?.call: (Array[untyped], Hash[Array[untyped], RecordSnapshot?]) -> Array[Step]
|
|
9
|
+
|
|
10
|
+
# : (untyped) -> Array[untyped]
|
|
11
|
+
def self?.version_key: (untyped) -> Array[untyped]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -6,8 +6,8 @@ module PaperTrailDiff
|
|
|
6
6
|
# : (snapshotter: untyped, refresher: untyped, preparer: untyped) -> void
|
|
7
7
|
def initialize: (snapshotter: untyped, refresher: untyped, preparer: untyped) -> void
|
|
8
8
|
|
|
9
|
-
# : (untyped, Array[untyped], ?start_at: untyped) -> void
|
|
10
|
-
def prepare: (untyped, Array[untyped], ?start_at: untyped) -> void
|
|
9
|
+
# : (untyped, Array[untyped], ?start_at: untyped, ?end_at: untyped) -> void
|
|
10
|
+
def prepare: (untyped, Array[untyped], ?start_at: untyped, ?end_at: untyped) -> void
|
|
11
11
|
|
|
12
12
|
# : (untyped, untyped) -> RecordSnapshot?
|
|
13
13
|
def call: (untyped, untyped) -> RecordSnapshot?
|
|
@@ -27,6 +27,9 @@ module PaperTrailDiff
|
|
|
27
27
|
|
|
28
28
|
@tree: AssociationTree
|
|
29
29
|
|
|
30
|
+
# : () -> Array[ActivityStep]
|
|
31
|
+
def no_steps: () -> Array[ActivityStep]
|
|
32
|
+
|
|
30
33
|
# : () -> Array[ActivityStep]
|
|
31
34
|
def build_between_versions: () -> Array[ActivityStep]
|
|
32
35
|
|
|
@@ -42,23 +45,30 @@ module PaperTrailDiff
|
|
|
42
45
|
# : (Array[untyped], ?range_start: untyped, ?range_end: untyped) -> Array[ActivityEvent]
|
|
43
46
|
def collect_events: (Array[untyped], ?range_start: untyped, ?range_end: untyped) -> Array[ActivityEvent]
|
|
44
47
|
|
|
45
|
-
# : (Array[untyped], ?
|
|
46
|
-
def prepare_history: (Array[untyped], ?
|
|
48
|
+
# : (Array[untyped], ?end_at: untyped) -> void
|
|
49
|
+
def prepare_history: (Array[untyped], ?end_at: untyped) -> void
|
|
47
50
|
|
|
48
51
|
# : (Array[untyped], Array[ActivityEvent], ?current: untyped, ?final_boundary: ActivityBoundary?, ?final_snapshot: RecordSnapshot?) -> Array[ActivityStep]
|
|
49
52
|
def build_event_steps: (Array[untyped], Array[ActivityEvent], ?current: untyped, ?final_boundary: ActivityBoundary?, ?final_snapshot: RecordSnapshot?) -> Array[ActivityStep]
|
|
50
53
|
|
|
51
|
-
#
|
|
52
|
-
|
|
54
|
+
# Appends the transition into an explicit closing boundary, which is either
|
|
55
|
+
# a requested current record or the absence a final root destroy leaves.
|
|
56
|
+
# : (ActivityHistory, Array[ActivityEvent], ActivityBoundary?, RecordSnapshot?) -> Array[ActivityStep]
|
|
57
|
+
def activity_steps: (ActivityHistory, Array[ActivityEvent], ActivityBoundary?, RecordSnapshot?) -> Array[ActivityStep]
|
|
53
58
|
|
|
54
|
-
#
|
|
55
|
-
|
|
59
|
+
# A destroyed root has no later version, but its own event states that
|
|
60
|
+
# nothing follows it, so the removal can still close the timeline.
|
|
61
|
+
# : (Array[untyped]) -> ActivityBoundary?
|
|
62
|
+
def destroyed_boundary: (Array[untyped]) -> ActivityBoundary?
|
|
56
63
|
|
|
57
|
-
# : (Array[untyped], Array[
|
|
58
|
-
def
|
|
64
|
+
# : (Array[untyped], Array[ActivityEvent], ?current: untyped) -> ActivityHistory
|
|
65
|
+
def event_history: (Array[untyped], Array[ActivityEvent], ?current: untyped) -> ActivityHistory
|
|
59
66
|
|
|
60
|
-
#
|
|
61
|
-
|
|
67
|
+
# Only the activity view gains the closing removal. The endpoint diff and
|
|
68
|
+
# the root timeline keep their `compare` and `timeline` semantics, under
|
|
69
|
+
# which the state at a destroy version is the state before the deletion.
|
|
70
|
+
# : (Array[untyped], Array[ActivityEvent], ActivityHistory) -> Analysis
|
|
71
|
+
def build_analysis: (Array[untyped], Array[ActivityEvent], ActivityHistory) -> Analysis
|
|
62
72
|
|
|
63
73
|
# : () -> TimeActivityTimelineBuilder
|
|
64
74
|
def time_builder: () -> TimeActivityTimelineBuilder
|
|
@@ -9,6 +9,11 @@ module PaperTrailDiff
|
|
|
9
9
|
|
|
10
10
|
attr_reader activity_timeline: Array[ActivityStep]?
|
|
11
11
|
|
|
12
|
+
# The result for a record whose requested history contains no versions,
|
|
13
|
+
# which is an empty history rather than a failed request.
|
|
14
|
+
# : () -> Analysis
|
|
15
|
+
def self.empty: () -> Analysis
|
|
16
|
+
|
|
12
17
|
# : (diff: Diff, timeline: Array[Step], ?activity_timeline: Array[ActivityStep]?) -> void
|
|
13
18
|
def initialize: (diff: Diff, timeline: Array[Step], ?activity_timeline: Array[ActivityStep]?) -> void
|
|
14
19
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/analysis_batch.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Builds one Analysis per root over a shared range, selecting every root's
|
|
5
|
+
# versions and preparing their association history once for the whole batch
|
|
6
|
+
# rather than once per root.
|
|
7
|
+
class AnalysisBatch
|
|
8
|
+
# : (Array[untyped], time_range: TimeRange?, live_loader: untyped, history_preparer: untyped, analyzer: untyped) -> void
|
|
9
|
+
def initialize: (Array[untyped], time_range: TimeRange?, live_loader: untyped, history_preparer: untyped, analyzer: untyped) -> void
|
|
10
|
+
|
|
11
|
+
# : () -> Hash[identity, Analysis]
|
|
12
|
+
def call: () -> Hash[identity, Analysis]
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
@analyzer: untyped
|
|
17
|
+
|
|
18
|
+
@history_preparer: untyped
|
|
19
|
+
|
|
20
|
+
@live_loader: untyped
|
|
21
|
+
|
|
22
|
+
@records: Array[untyped]
|
|
23
|
+
|
|
24
|
+
@time_range: TimeRange?
|
|
25
|
+
|
|
26
|
+
# : () -> Array[untyped]
|
|
27
|
+
def validated_records: () -> Array[untyped]
|
|
28
|
+
|
|
29
|
+
# Association history is prepared per model class across every selected root
|
|
30
|
+
# version, which is the work that would otherwise repeat for each record.
|
|
31
|
+
# The roots are preloaded first, because preparation reads their current
|
|
32
|
+
# association state as a fallback and would otherwise walk it one root at a
|
|
33
|
+
# time.
|
|
34
|
+
# : (Array[untyped], Hash[Array[String], Array[untyped]]) -> void
|
|
35
|
+
def prepare: (Array[untyped], Hash[Array[String], Array[untyped]]) -> void
|
|
36
|
+
|
|
37
|
+
# A root with no versions in range has nothing to report, which is an empty
|
|
38
|
+
# result rather than a failed request.
|
|
39
|
+
# : (untyped, Array[untyped]) -> Analysis
|
|
40
|
+
def analysis_for: (untyped, Array[untyped]) -> Analysis
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/batch_boundary_resolver.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Resolves `:first` and `:last` endpoints for a whole batch in two queries per
|
|
5
|
+
# model class. Left to the caller this is a per-root lookup, which reintroduces
|
|
6
|
+
# exactly the queries a batched comparison exists to remove.
|
|
7
|
+
class BatchBoundaryResolver
|
|
8
|
+
BOUNDARIES: untyped
|
|
9
|
+
|
|
10
|
+
# : (untyped) -> bool
|
|
11
|
+
def self.symbolic?: (untyped) -> bool
|
|
12
|
+
|
|
13
|
+
# : (Array[[untyped, untyped]]) -> void
|
|
14
|
+
def initialize: (Array[[ untyped, untyped ]]) -> void
|
|
15
|
+
|
|
16
|
+
# Returns the pairs with every resolvable symbol replaced. A symbol that
|
|
17
|
+
# names history the record does not have is left in place, so the caller can
|
|
18
|
+
# decide what an absent history means rather than being handed nil.
|
|
19
|
+
# : () -> Array[[untyped, untyped]]
|
|
20
|
+
def call: () -> Array[[ untyped, untyped ]]
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
@pairs: Array[[ untyped, untyped ]]
|
|
25
|
+
|
|
26
|
+
# : (untyped) -> bool
|
|
27
|
+
def symbolic?: (untyped) -> bool
|
|
28
|
+
|
|
29
|
+
# : (untyped, untyped, Hash[Array[String], Hash[Symbol, untyped]]) -> untyped
|
|
30
|
+
def resolve: (untyped, untyped, Hash[Array[String], Hash[Symbol, untyped]]) -> untyped
|
|
31
|
+
|
|
32
|
+
# A symbol carries no identity of its own, so the pair's other endpoint has
|
|
33
|
+
# to say which record is meant.
|
|
34
|
+
# : (untyped) -> Array[String]?
|
|
35
|
+
def anchor_identity: (untyped) -> Array[String]?
|
|
36
|
+
|
|
37
|
+
# : () -> Hash[Array[String], Hash[Symbol, untyped]]
|
|
38
|
+
def boundary_index: () -> Hash[Array[String], Hash[Symbol, untyped]]
|
|
39
|
+
|
|
40
|
+
# : () -> Array[[untyped, untyped]]
|
|
41
|
+
def anchors: () -> Array[[ untyped, untyped ]]
|
|
42
|
+
|
|
43
|
+
# : (untyped) -> [untyped, untyped]?
|
|
44
|
+
def anchor: (untyped) -> [ untyped, untyped ]?
|
|
45
|
+
|
|
46
|
+
# : (Hash[Array[String], Hash[Symbol, untyped]], untyped, Array[untyped]) -> void
|
|
47
|
+
def add_model_boundaries: (Hash[Array[String], Hash[Symbol, untyped]], untyped, Array[untyped]) -> void
|
|
48
|
+
|
|
49
|
+
# Two grouped queries name each root's outermost versions, and one more
|
|
50
|
+
# loads them, whatever the size of the batch.
|
|
51
|
+
# : (untyped, Array[untyped]) -> [Hash[untyped, untyped], Hash[untyped, untyped], Hash[untyped, untyped]]
|
|
52
|
+
def boundary_versions: (untyped, Array[untyped]) -> [ Hash[untyped, untyped], Hash[untyped, untyped], Hash[untyped, untyped] ]
|
|
53
|
+
|
|
54
|
+
# Grouped keys come back with whatever type the column uses, so match on the
|
|
55
|
+
# string form rather than assuming integers.
|
|
56
|
+
# : (Hash[untyped, untyped], untyped) -> untyped
|
|
57
|
+
def boundary_key: (Hash[untyped, untyped], untyped) -> untyped
|
|
58
|
+
|
|
59
|
+
# : (untyped, untyped) -> Array[String]
|
|
60
|
+
def identity_key: (untyped, untyped) -> Array[String]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/batched_root_analyzer.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Builds one root's Analysis inside a batch, from versions the batch already
|
|
5
|
+
# selected and history it already prepared.
|
|
6
|
+
class BatchedRootAnalyzer
|
|
7
|
+
# : (tree: AssociationTree, timeline_snapshotter: untyped, activity_snapshotter: untyped, preparer: untyped, activity: bool) -> void
|
|
8
|
+
def initialize: (tree: AssociationTree, timeline_snapshotter: untyped, activity_snapshotter: untyped, preparer: untyped, activity: bool) -> void
|
|
9
|
+
|
|
10
|
+
# : (untyped, Array[untyped]) -> Analysis
|
|
11
|
+
def call: (untyped, Array[untyped]) -> Analysis
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
@activity: bool
|
|
16
|
+
|
|
17
|
+
@activity_snapshotter: untyped
|
|
18
|
+
|
|
19
|
+
@preparer: untyped
|
|
20
|
+
|
|
21
|
+
@timeline_snapshotter: untyped
|
|
22
|
+
|
|
23
|
+
@tree: AssociationTree
|
|
24
|
+
|
|
25
|
+
# : (untyped, Array[untyped]) -> Analysis
|
|
26
|
+
def activity_analysis: (untyped, Array[untyped]) -> Analysis
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/batched_root_versions.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Selects each root's versions for a batched range in a fixed number of
|
|
5
|
+
# queries. Only the range forms that mean the same thing for every root are
|
|
6
|
+
# supported: a shared wall-clock window, or each root's own whole history.
|
|
7
|
+
class BatchedRootVersions
|
|
8
|
+
# : (Array[untyped], time_range: TimeRange?) -> void
|
|
9
|
+
def initialize: (Array[untyped], time_range: TimeRange?) -> void
|
|
10
|
+
|
|
11
|
+
# Returns root versions per record identity, in chronological order.
|
|
12
|
+
# : () -> Hash[Array[String], Array[untyped]]
|
|
13
|
+
def call: () -> Hash[Array[String], Array[untyped]]
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
@records: Array[untyped]
|
|
18
|
+
|
|
19
|
+
@time_range: TimeRange?
|
|
20
|
+
|
|
21
|
+
# : (untyped, Array[untyped], Hash[Array[String], Array[untyped]]) -> void
|
|
22
|
+
def select_model: (untyped, Array[untyped], Hash[Array[String], Array[untyped]]) -> void
|
|
23
|
+
|
|
24
|
+
# The window's own versions plus, when it has one, the later version needed
|
|
25
|
+
# to reveal the last mutation inside it. A window closing on the root's own
|
|
26
|
+
# destruction needs no later version, because none can exist.
|
|
27
|
+
# : (Array[untyped], untyped) -> Array[untyped]
|
|
28
|
+
def versions_for: (Array[untyped], untyped) -> Array[untyped]
|
|
29
|
+
|
|
30
|
+
# : (Array[untyped]) -> bool
|
|
31
|
+
def terminal_destroy?: (Array[untyped]) -> bool
|
|
32
|
+
|
|
33
|
+
# : (untyped, Array[untyped], TimeRange?) -> Hash[String, Array[untyped]]
|
|
34
|
+
def grouped_versions: (untyped, Array[untyped], TimeRange?) -> Hash[String, Array[untyped]]
|
|
35
|
+
|
|
36
|
+
# One row per root: the earliest version after the window, found without a
|
|
37
|
+
# window function so the query stays portable.
|
|
38
|
+
# : (untyped, Array[untyped], TimeRange) -> Hash[String, untyped]
|
|
39
|
+
def trailing_versions: (untyped, Array[untyped], TimeRange) -> Hash[String, untyped]
|
|
40
|
+
|
|
41
|
+
# : (untyped, TimeRange) -> untyped
|
|
42
|
+
def no_earlier_trailing: (untyped, TimeRange) -> untyped
|
|
43
|
+
|
|
44
|
+
# Mirrors the window's own end handling, so a version sitting exactly on an
|
|
45
|
+
# inclusive boundary stays inside the window instead of masking the trailing
|
|
46
|
+
# version that follows it.
|
|
47
|
+
# : (untyped, untyped, TimeRange) -> untyped
|
|
48
|
+
def preceding: (untyped, untyped, TimeRange) -> untyped
|
|
49
|
+
|
|
50
|
+
# : (untyped, TimeRange) -> untyped
|
|
51
|
+
def after_window: (untyped, TimeRange) -> untyped
|
|
52
|
+
|
|
53
|
+
# : (untyped, Array[untyped]) -> untyped
|
|
54
|
+
def base_scope: (untyped, Array[untyped]) -> untyped
|
|
55
|
+
|
|
56
|
+
# : (untyped) -> Array[untyped]
|
|
57
|
+
def ordered: (untyped) -> Array[untyped]
|
|
58
|
+
|
|
59
|
+
# : (untyped, untyped) -> Array[String]
|
|
60
|
+
def identity: (untyped, untyped) -> Array[String]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -50,9 +50,16 @@ module PaperTrailDiff
|
|
|
50
50
|
# : (Array[identity]) -> Array[identity]
|
|
51
51
|
def sorted_identities: (Array[identity]) -> Array[identity]
|
|
52
52
|
|
|
53
|
+
# Identities sort by type first so that mixed id types stay comparable, then
|
|
54
|
+
# naturally within one type. Ordering by the printed form instead would put
|
|
55
|
+
# id 10 before id 2, which is deterministic but reads as unsorted wherever a
|
|
56
|
+
# result is rendered.
|
|
53
57
|
# : (identity) -> Array[untyped]
|
|
54
58
|
def sortable_identity: (identity) -> Array[untyped]
|
|
55
59
|
|
|
60
|
+
# : (untyped) -> Array[untyped]
|
|
61
|
+
def sortable_id: (untyped) -> Array[untyped]
|
|
62
|
+
|
|
56
63
|
# : (identity) -> void
|
|
57
64
|
def raise_duplicate_identity!: (identity) -> void
|
|
58
65
|
end
|
|
@@ -7,12 +7,15 @@ module PaperTrailDiff
|
|
|
7
7
|
|
|
8
8
|
attr_reader after: RecordSnapshot?
|
|
9
9
|
|
|
10
|
+
# The origin is recorded by serial rather than by reference, because
|
|
11
|
+
# holding it would retain every earlier snapshot at the same path for the
|
|
12
|
+
# whole timeline.
|
|
10
13
|
# : (from: AssociationSnapshot, before: RecordSnapshot?, after: RecordSnapshot?) -> void
|
|
11
14
|
def initialize: (from: AssociationSnapshot, before: RecordSnapshot?, after: RecordSnapshot?) -> void
|
|
12
15
|
|
|
13
16
|
# : (AssociationSnapshot) -> bool
|
|
14
17
|
def from?: (AssociationSnapshot) -> bool
|
|
15
18
|
|
|
16
|
-
@
|
|
19
|
+
@from_serial: Integer
|
|
17
20
|
end
|
|
18
21
|
end
|
|
@@ -23,6 +23,21 @@ module PaperTrailDiff
|
|
|
23
23
|
|
|
24
24
|
@preparer: untyped
|
|
25
25
|
|
|
26
|
+
# : (Array[[untyped, untyped]]) -> Hash[identity, RecordSnapshot]
|
|
27
|
+
def prepared_live_snapshots: (Array[[ untyped, untyped ]]) -> Hash[identity, RecordSnapshot]
|
|
28
|
+
|
|
29
|
+
# : (untyped) -> bool
|
|
30
|
+
def symbolic?: (untyped) -> bool
|
|
31
|
+
|
|
32
|
+
# A boundary symbol that stayed unresolved means the root has no recorded
|
|
33
|
+
# history. An absent history is an empty result rather than a failed
|
|
34
|
+
# request, matching how the timeline APIs answer the same question.
|
|
35
|
+
# : (Array[[untyped, untyped]], Hash[identity, RecordSnapshot]) -> comparison_results
|
|
36
|
+
def results: (Array[[ untyped, untyped ]], Hash[identity, RecordSnapshot]) -> comparison_results
|
|
37
|
+
|
|
38
|
+
# : (untyped, untyped) -> identity
|
|
39
|
+
def entry_identity: (untyped, untyped) -> identity
|
|
40
|
+
|
|
26
41
|
# : (untyped, untyped, Hash[identity, RecordSnapshot]) -> Diff
|
|
27
42
|
def compare: (untyped, untyped, Hash[identity, RecordSnapshot]) -> Diff
|
|
28
43
|
|
|
@@ -6,6 +6,18 @@ module PaperTrailDiff
|
|
|
6
6
|
# : (untyped, untyped) -> void
|
|
7
7
|
def self?.validate_pair!: (untyped, untyped) -> void
|
|
8
8
|
|
|
9
|
+
# Two versions carry no visible cue about which is earlier, so transposing
|
|
10
|
+
# them is easy to do by accident and impossible to detect afterwards: the
|
|
11
|
+
# result is a valid inverse diff and carries no direction of its own. It is
|
|
12
|
+
# also redundant, because the two orders differ only in which side of each
|
|
13
|
+
# change is `from`. A current-record endpoint is exempt: it is self-evidently
|
|
14
|
+
# the live state, so putting it first is a deliberate reverse comparison.
|
|
15
|
+
# : (untyped, untyped) -> void
|
|
16
|
+
def self?.validate_order!: (untyped, untyped) -> void
|
|
17
|
+
|
|
18
|
+
# : (untyped, untyped) -> bool
|
|
19
|
+
def self?.reversed_versions?: (untyped, untyped) -> bool
|
|
20
|
+
|
|
9
21
|
# : (untyped) -> void
|
|
10
22
|
def self?.validate!: (untyped) -> void
|
|
11
23
|
|
|
@@ -13,6 +13,10 @@ module PaperTrailDiff
|
|
|
13
13
|
class VersionMismatchError < Error
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# Raised when comparison endpoints are given in reverse chronological order.
|
|
17
|
+
class ReversedEndpointsError < Error
|
|
18
|
+
end
|
|
19
|
+
|
|
16
20
|
# Raised when a live endpoint is not a clean, persisted ActiveRecord record.
|
|
17
21
|
class InvalidEndpointError < Error
|
|
18
22
|
end
|
|
@@ -6,10 +6,9 @@ module PaperTrailDiff
|
|
|
6
6
|
# : (tree: AssociationTree, traversal: AssociationTraversal, normalizer: SnapshotNormalizer, preparer: untyped) -> void
|
|
7
7
|
def initialize: (tree: AssociationTree, traversal: AssociationTraversal, normalizer: SnapshotNormalizer, preparer: untyped) -> void
|
|
8
8
|
|
|
9
|
-
# : (untyped, Array[untyped], ?start_at: untyped) -> void
|
|
10
|
-
def prepare: (untyped, Array[untyped], ?start_at: untyped) -> void
|
|
9
|
+
# : (untyped, Array[untyped], ?start_at: untyped, ?end_at: untyped) -> void
|
|
10
|
+
def prepare: (untyped, Array[untyped], ?start_at: untyped, ?end_at: untyped) -> void
|
|
11
11
|
|
|
12
|
-
# Prepares selected history for several roots of the same model class.
|
|
13
12
|
# : (Array[untyped], Array[untyped]) -> void
|
|
14
13
|
def prepare_batch: (Array[untyped], Array[untyped]) -> void
|
|
15
14
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Generated from lib/paper_trail_diff/paper_trail_adapter.rb with RBS::Inline
|
|
2
2
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
|
-
# PaperTrail/ActiveRecord boundary that produces plain record snapshots.
|
|
4
|
+
# PaperTrail/ActiveRecord boundary that produces plain record snapshots. It is
|
|
5
|
+
# deliberately the widest class here: it fronts every public operation and is
|
|
6
|
+
# the only place allowed to know about both PaperTrail and the pure engine.
|
|
7
|
+
# Reconstruction logic lives in the collaborators it wires together.
|
|
5
8
|
class PaperTrailAdapter
|
|
6
9
|
# : (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool) -> void
|
|
7
10
|
def initialize: (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool) -> void
|
|
@@ -22,6 +25,10 @@ module PaperTrailDiff
|
|
|
22
25
|
# : (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool) -> Analysis
|
|
23
26
|
def analyze: (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool) -> Analysis
|
|
24
27
|
|
|
28
|
+
# Analyzes many roots over one shared range, preparing their history once.
|
|
29
|
+
# : (Array[untyped], within: untyped, ?activity: bool) -> Hash[identity, Analysis]
|
|
30
|
+
def analyze_many: (Array[untyped], within: untyped, ?activity: bool) -> Hash[identity, Analysis]
|
|
31
|
+
|
|
25
32
|
private
|
|
26
33
|
|
|
27
34
|
@activity_snapshotter: ActivitySnapshotProvider
|
|
@@ -44,6 +51,14 @@ module PaperTrailDiff
|
|
|
44
51
|
|
|
45
52
|
@traversal: AssociationTraversal
|
|
46
53
|
|
|
54
|
+
@traversal_preparer: TraversalPreparer
|
|
55
|
+
|
|
56
|
+
# : () -> void
|
|
57
|
+
def build_snapshotters: () -> void
|
|
58
|
+
|
|
59
|
+
# : (bool) -> BatchedRootAnalyzer
|
|
60
|
+
def batched_root_analyzer: (bool) -> BatchedRootAnalyzer
|
|
61
|
+
|
|
47
62
|
# : () -> HistoricalSnapshotStore
|
|
48
63
|
def build_historical_store: () -> HistoricalSnapshotStore
|
|
49
64
|
|
|
@@ -73,11 +88,5 @@ module PaperTrailDiff
|
|
|
73
88
|
|
|
74
89
|
# : (untyped) -> RecordSnapshot
|
|
75
90
|
def normalize_live_snapshot: (untyped) -> RecordSnapshot
|
|
76
|
-
|
|
77
|
-
# : (untyped, historical: bool) -> void
|
|
78
|
-
def prepare_traversal!: (untyped, historical: bool) -> void
|
|
79
|
-
|
|
80
|
-
# : () -> void
|
|
81
|
-
def ensure_association_tracking!: () -> void
|
|
82
91
|
end
|
|
83
92
|
end
|
|
@@ -64,6 +64,7 @@ module PaperTrailDiff
|
|
|
64
64
|
# : (untyped, Array[untyped], ?owners: Hash[String, Array[untyped]]?) -> Hash[String, Hash[Symbol, untyped]]
|
|
65
65
|
def group_for: (untyped, Array[untyped], ?owners: Hash[String, Array[untyped]]?) -> Hash[String, Hash[Symbol, untyped]]
|
|
66
66
|
|
|
67
|
+
# Keyed rather than scanned so that a wide edge stays linear in its width.
|
|
67
68
|
# : (Array[Array[untyped]]) -> Hash[String, Array[untyped]]
|
|
68
69
|
def group_ids_by_owner: (Array[Array[untyped]]) -> Hash[String, Array[untyped]]
|
|
69
70
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Expands the explicit association tree into a request-scoped PreparedHistory.
|
|
5
5
|
class PreparedHistoryLoader
|
|
6
|
-
# : (untyped, root_versions: Array[untyped], tree: AssociationTree, traversal: AssociationTraversal, ?root_ids: Array[untyped], ?start_at: untyped, ?live_records: Array[untyped]) -> void
|
|
7
|
-
def initialize: (untyped, root_versions: Array[untyped], tree: AssociationTree, traversal: AssociationTraversal, ?root_ids: Array[untyped], ?start_at: untyped, ?live_records: Array[untyped]) -> void
|
|
6
|
+
# : (untyped, root_versions: Array[untyped], tree: AssociationTree, traversal: AssociationTraversal, ?root_ids: Array[untyped], ?start_at: untyped, ?end_at: untyped, ?live_records: Array[untyped]) -> void
|
|
7
|
+
def initialize: (untyped, root_versions: Array[untyped], tree: AssociationTree, traversal: AssociationTraversal, ?root_ids: Array[untyped], ?start_at: untyped, ?end_at: untyped, ?live_records: Array[untyped]) -> void
|
|
8
8
|
|
|
9
9
|
# : () -> PreparedHistory
|
|
10
10
|
def call: () -> PreparedHistory
|
|
@@ -13,6 +13,9 @@ module PaperTrailDiff
|
|
|
13
13
|
# : (untyped, Hash[untyped, untyped]) -> PreparedRecordState
|
|
14
14
|
def self.from_attributes: (untyped, Hash[untyped, untyped]) -> PreparedRecordState
|
|
15
15
|
|
|
16
|
+
# Mirrors PaperTrail's reifier, which writes attributes directly. Mass
|
|
17
|
+
# assignment would route reconstructed state through application-defined
|
|
18
|
+
# attribute writers and reify a different record than the version stored.
|
|
16
19
|
# : () -> untyped
|
|
17
20
|
def instantiate: () -> untyped
|
|
18
21
|
|
|
@@ -73,16 +76,25 @@ module PaperTrailDiff
|
|
|
73
76
|
|
|
74
77
|
@states: Hash[untyped, PreparedRecordState?]
|
|
75
78
|
|
|
79
|
+
@transaction_positions: Hash[untyped, Integer]
|
|
80
|
+
|
|
76
81
|
@version_positions: Hash[String, Integer]
|
|
77
82
|
|
|
78
83
|
# : (Integer) -> PreparedRecordState?
|
|
79
84
|
def state_after: (Integer) -> PreparedRecordState?
|
|
80
85
|
|
|
81
|
-
#
|
|
82
|
-
|
|
86
|
+
# A version is eligible when it is at or after the boundary, or shares the
|
|
87
|
+
# boundary's transaction. The first is monotonic over the chronological
|
|
88
|
+
# list and the second is indexed, so neither rescans a long history once
|
|
89
|
+
# per boundary.
|
|
90
|
+
# : (untyped) -> Integer?
|
|
91
|
+
def boundary_index: (untyped) -> Integer?
|
|
83
92
|
|
|
84
|
-
# : (untyped
|
|
85
|
-
def
|
|
93
|
+
# : (untyped) -> Integer?
|
|
94
|
+
def transaction_index: (untyped) -> Integer?
|
|
95
|
+
|
|
96
|
+
# : () -> Hash[untyped, Integer]
|
|
97
|
+
def build_transaction_positions: () -> Hash[untyped, Integer]
|
|
86
98
|
|
|
87
99
|
# : (untyped) -> PreparedRecordState?
|
|
88
100
|
def state_for: (untyped) -> PreparedRecordState?
|
|
@@ -90,8 +102,8 @@ module PaperTrailDiff
|
|
|
90
102
|
|
|
91
103
|
# Request-scoped scalar history loaded once per model identity.
|
|
92
104
|
class PreparedRecordIndex
|
|
93
|
-
# : (untyped, ?live_records: Array[untyped]) -> void
|
|
94
|
-
def initialize: (untyped, ?live_records: Array[untyped]) -> void
|
|
105
|
+
# : (untyped, ?end_at: untyped, ?live_records: Array[untyped]) -> void
|
|
106
|
+
def initialize: (untyped, ?end_at: untyped, ?live_records: Array[untyped]) -> void
|
|
95
107
|
|
|
96
108
|
# : (untyped, Array[untyped]) -> void
|
|
97
109
|
def load: (untyped, Array[untyped]) -> void
|
|
@@ -110,6 +122,8 @@ module PaperTrailDiff
|
|
|
110
122
|
|
|
111
123
|
private
|
|
112
124
|
|
|
125
|
+
@end_time: untyped
|
|
126
|
+
|
|
113
127
|
@seeded_live_records: Hash[Array[String], untyped]
|
|
114
128
|
|
|
115
129
|
@series: Hash[Array[String], PreparedRecordSeries]
|
|
@@ -118,6 +132,10 @@ module PaperTrailDiff
|
|
|
118
132
|
|
|
119
133
|
@state_loader: PreparedVersionStateLoader
|
|
120
134
|
|
|
135
|
+
# Callers bound the range with either a version or a bare timestamp.
|
|
136
|
+
# : (untyped) -> untyped
|
|
137
|
+
def boundary_time: (untyped) -> untyped
|
|
138
|
+
|
|
121
139
|
# : (untyped, untyped) -> Array[String]
|
|
122
140
|
def identity: (untyped, untyped) -> Array[String]
|
|
123
141
|
|
|
@@ -127,9 +145,24 @@ module PaperTrailDiff
|
|
|
127
145
|
# : (untyped, untyped, Hash[String, Array[untyped]], Hash[String, untyped]) -> void
|
|
128
146
|
def add_series: (untyped, untyped, Hash[String, Array[untyped]], Hash[String, untyped]) -> void
|
|
129
147
|
|
|
148
|
+
# A PaperTrail version is a pre-change snapshot, so the state at the last
|
|
149
|
+
# selected boundary can only live in the next version after it. One
|
|
150
|
+
# trailing version per identity is retained, rather than every version
|
|
151
|
+
# recorded between the requested range and the present.
|
|
130
152
|
# : (untyped, Array[untyped]) -> Array[untyped]
|
|
131
153
|
def versions_for: (untyped, Array[untyped]) -> Array[untyped]
|
|
132
154
|
|
|
155
|
+
# In range, or the earliest version after it, resolved in the same query
|
|
156
|
+
# rather than with a window function or one query per identity.
|
|
157
|
+
# : (untyped) -> untyped
|
|
158
|
+
def window_condition: (untyped) -> untyped
|
|
159
|
+
|
|
160
|
+
# : (untyped) -> untyped
|
|
161
|
+
def first_after_range: (untyped) -> untyped
|
|
162
|
+
|
|
163
|
+
# : (untyped, untyped) -> untyped
|
|
164
|
+
def preceding_trailing_version: (untyped, untyped) -> untyped
|
|
165
|
+
|
|
133
166
|
# : (untyped, Array[untyped]) -> Array[untyped]
|
|
134
167
|
def available_live_records: (untyped, Array[untyped]) -> Array[untyped]
|
|
135
168
|
|
|
@@ -36,6 +36,24 @@ module PaperTrailDiff
|
|
|
36
36
|
def index: (Array[RecordSnapshot]) -> CollectionIdentityIndex
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
# Identifies one snapshot for internal carry-forward. A transition cannot
|
|
40
|
+
# hold its origin without retaining every earlier snapshot at that path,
|
|
41
|
+
# and `object_id` is only guaranteed unique among live objects, so Ruby may
|
|
42
|
+
# hand a collected snapshot's id to a later one.
|
|
43
|
+
class SerialSequence
|
|
44
|
+
# : () -> void
|
|
45
|
+
def initialize: () -> void
|
|
46
|
+
|
|
47
|
+
# : () -> Integer
|
|
48
|
+
def next_serial: () -> Integer
|
|
49
|
+
|
|
50
|
+
@counter: Integer
|
|
51
|
+
|
|
52
|
+
@mutex: Thread::Mutex
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
SERIALS: untyped
|
|
56
|
+
|
|
39
57
|
# : (Symbol) -> bool
|
|
40
58
|
def self.collection_kind?: (Symbol) -> bool
|
|
41
59
|
|
|
@@ -43,6 +61,8 @@ module PaperTrailDiff
|
|
|
43
61
|
|
|
44
62
|
attr_reader records: Array[RecordSnapshot]
|
|
45
63
|
|
|
64
|
+
attr_reader serial: Integer
|
|
65
|
+
|
|
46
66
|
# : (kind: Symbol, records: Array[RecordSnapshot], ?identity_index_cache: untyped, ?transition: CollectionTransition?) -> void
|
|
47
67
|
def initialize: (kind: Symbol, records: Array[RecordSnapshot], ?identity_index_cache: untyped, ?transition: CollectionTransition?) -> void
|
|
48
68
|
|
|
@@ -39,6 +39,8 @@ module PaperTrailDiff
|
|
|
39
39
|
|
|
40
40
|
private
|
|
41
41
|
|
|
42
|
+
@excluded_attributes: Hash[Array[untyped], Array[String]]
|
|
43
|
+
|
|
42
44
|
@ignore_policy: IgnorePolicy
|
|
43
45
|
|
|
44
46
|
@pool: SnapshotPool
|
|
@@ -55,9 +57,15 @@ module PaperTrailDiff
|
|
|
55
57
|
# : (untyped, Array[untyped], String) -> snapshot_attributes
|
|
56
58
|
def normalized_attributes: (untyped, Array[untyped], String) -> snapshot_attributes
|
|
57
59
|
|
|
60
|
+
# Every input is fixed for one model class at one selected path, and the
|
|
61
|
+
# path's structural columns are recorded before its records normalize, so
|
|
62
|
+
# this is resolved once instead of per record per boundary.
|
|
58
63
|
# : (untyped, Array[untyped], String) -> Array[String]
|
|
59
64
|
def excluded_attributes: (untyped, Array[untyped], String) -> Array[String]
|
|
60
65
|
|
|
66
|
+
# : (untyped, Array[untyped], String) -> Array[String]
|
|
67
|
+
def build_excluded_attributes: (untyped, Array[untyped], String) -> Array[String]
|
|
68
|
+
|
|
61
69
|
# : (Array[untyped]) -> Array[String]
|
|
62
70
|
def relationship_columns: (Array[untyped]) -> Array[String]
|
|
63
71
|
|