paper_trail_diff 0.14.0 → 0.15.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 +343 -0
- data/QUICKSTART.md +21 -4
- data/README.md +58 -55
- data/docs/architecture.md +32 -11
- data/docs/performance.md +116 -391
- data/docs/reference.md +345 -106
- data/docs/testing.md +63 -7
- data/lib/paper_trail_diff/activity_boundary.rb +83 -26
- data/lib/paper_trail_diff/activity_child_candidate_loader.rb +2 -1
- data/lib/paper_trail_diff/activity_event.rb +41 -10
- data/lib/paper_trail_diff/activity_history.rb +34 -9
- data/lib/paper_trail_diff/activity_range.rb +18 -3
- data/lib/paper_trail_diff/activity_snapshot_sequence.rb +76 -37
- data/lib/paper_trail_diff/activity_target_candidate_loader.rb +5 -5
- data/lib/paper_trail_diff/activity_timeline_builder.rb +266 -111
- data/lib/paper_trail_diff/activity_transaction_grouper.rb +2 -1
- data/lib/paper_trail_diff/activity_tree_filter.rb +114 -0
- data/lib/paper_trail_diff/activity_version_collector.rb +7 -5
- data/lib/paper_trail_diff/activity_view.rb +69 -0
- data/lib/paper_trail_diff/analysis.rb +13 -1
- data/lib/paper_trail_diff/analysis_batch.rb +24 -30
- data/lib/paper_trail_diff/approximate_paths.rb +48 -0
- data/lib/paper_trail_diff/association_activity_edge.rb +6 -6
- data/lib/paper_trail_diff/association_discovery.rb +9 -1
- data/lib/paper_trail_diff/association_key.rb +56 -0
- data/lib/paper_trail_diff/association_traversal.rb +13 -4
- data/lib/paper_trail_diff/batch_boundary_resolver.rb +3 -4
- data/lib/paper_trail_diff/batch_preload.rb +53 -0
- data/lib/paper_trail_diff/batched_root_analyzer.rb +12 -14
- data/lib/paper_trail_diff/batched_root_versions.rb +38 -14
- data/lib/paper_trail_diff/change_row.rb +133 -0
- data/lib/paper_trail_diff/collection_comparator.rb +5 -32
- data/lib/paper_trail_diff/configuration.rb +35 -12
- data/lib/paper_trail_diff/deleted_analysis_batch.rb +18 -18
- data/lib/paper_trail_diff/deleted_root_filter.rb +79 -0
- data/lib/paper_trail_diff/deprecation.rb +61 -0
- data/lib/paper_trail_diff/diagnostics.rb +84 -99
- data/lib/paper_trail_diff/endpoint.rb +26 -3
- data/lib/paper_trail_diff/errors.rb +3 -1
- data/lib/paper_trail_diff/fallback_paths.rb +38 -0
- data/lib/paper_trail_diff/historical_association_reifier.rb +2 -4
- data/lib/paper_trail_diff/historical_filter.rb +19 -10
- data/lib/paper_trail_diff/historical_snapshot_store.rb +57 -67
- data/lib/paper_trail_diff/live_endpoint_batch_loader.rb +1 -35
- data/lib/paper_trail_diff/nested_comparator.rb +19 -18
- data/lib/paper_trail_diff/paper_trail_adapter.rb +122 -127
- data/lib/paper_trail_diff/preloaded_endpoint_batch_loader.rb +13 -1
- data/lib/paper_trail_diff/prepared_activity_index.rb +1 -1
- data/lib/paper_trail_diff/prepared_edge_loader.rb +2 -1
- data/lib/paper_trail_diff/prepared_history.rb +9 -1
- data/lib/paper_trail_diff/prepared_history_loader.rb +1 -0
- data/lib/paper_trail_diff/prepared_record_index.rb +38 -7
- data/lib/paper_trail_diff/root_version_selection.rb +4 -2
- data/lib/paper_trail_diff/scope_condition.rb +76 -21
- data/lib/paper_trail_diff/scope_ranking.rb +12 -2
- data/lib/paper_trail_diff/scoped_analysis.rb +11 -0
- data/lib/paper_trail_diff/scoped_analysis_builder.rb +16 -8
- data/lib/paper_trail_diff/scoped_root_selection.rb +18 -12
- data/lib/paper_trail_diff/snapshot.rb +16 -1
- data/lib/paper_trail_diff/support.rb +29 -2
- data/lib/paper_trail_diff/time_range.rb +32 -10
- data/lib/paper_trail_diff/timeline_builder.rb +7 -7
- data/lib/paper_trail_diff/timeline_range.rb +9 -1
- data/lib/paper_trail_diff/timeline_snapshot_provider.rb +24 -17
- data/lib/paper_trail_diff/transaction_cut.rb +181 -0
- data/lib/paper_trail_diff/traversal_preparer.rb +134 -29
- data/lib/paper_trail_diff/version.rb +1 -1
- data/lib/paper_trail_diff/version_boundary.rb +37 -8
- data/lib/paper_trail_diff/version_query.rb +34 -10
- data/lib/paper_trail_diff/version_scope_filter.rb +9 -10
- data/lib/paper_trail_diff/version_sequence_diagnostics.rb +31 -16
- data/lib/paper_trail_diff/version_table.rb +60 -0
- data/lib/paper_trail_diff.rb +98 -56
- data/sig/generated/paper_trail_diff/activity_boundary.rbs +54 -19
- data/sig/generated/paper_trail_diff/activity_event.rbs +25 -4
- data/sig/generated/paper_trail_diff/activity_history.rbs +21 -6
- data/sig/generated/paper_trail_diff/activity_range.rbs +9 -2
- data/sig/generated/paper_trail_diff/activity_snapshot_sequence.rbs +48 -9
- data/sig/generated/paper_trail_diff/activity_timeline_builder.rbs +134 -30
- data/sig/generated/paper_trail_diff/activity_tree_filter.rbs +73 -0
- data/sig/generated/paper_trail_diff/activity_version_collector.rbs +2 -0
- data/sig/generated/paper_trail_diff/activity_view.rbs +42 -0
- data/sig/generated/paper_trail_diff/analysis.rbs +8 -1
- data/sig/generated/paper_trail_diff/analysis_batch.rbs +8 -15
- data/sig/generated/paper_trail_diff/approximate_paths.rbs +23 -0
- data/sig/generated/paper_trail_diff/association_discovery.rbs +5 -0
- data/sig/generated/paper_trail_diff/association_key.rbs +29 -0
- data/sig/generated/paper_trail_diff/association_traversal.rbs +4 -0
- data/sig/generated/paper_trail_diff/batch_preload.rbs +29 -0
- data/sig/generated/paper_trail_diff/batched_root_analyzer.rbs +9 -10
- data/sig/generated/paper_trail_diff/batched_root_versions.rbs +14 -4
- data/sig/generated/paper_trail_diff/change_row.rbs +86 -0
- data/sig/generated/paper_trail_diff/collection_comparator.rbs +2 -16
- data/sig/generated/paper_trail_diff/configuration.rbs +19 -3
- data/sig/generated/paper_trail_diff/deleted_analysis_batch.rbs +8 -8
- data/sig/generated/paper_trail_diff/deleted_root_filter.rbs +48 -0
- data/sig/generated/paper_trail_diff/deprecation.rbs +35 -0
- data/sig/generated/paper_trail_diff/diagnostics.rbs +37 -30
- data/sig/generated/paper_trail_diff/endpoint.rbs +11 -2
- data/sig/generated/paper_trail_diff/errors.rbs +3 -1
- data/sig/generated/paper_trail_diff/fallback_paths.rbs +22 -0
- data/sig/generated/paper_trail_diff/historical_filter.rbs +12 -2
- data/sig/generated/paper_trail_diff/historical_snapshot_store.rbs +33 -28
- data/sig/generated/paper_trail_diff/live_endpoint_batch_loader.rbs +0 -15
- data/sig/generated/paper_trail_diff/nested_comparator.rbs +10 -7
- data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +54 -52
- data/sig/generated/paper_trail_diff/preloaded_endpoint_batch_loader.rbs +9 -1
- data/sig/generated/paper_trail_diff/prepared_history.rbs +5 -0
- data/sig/generated/paper_trail_diff/prepared_record_index.rbs +19 -2
- data/sig/generated/paper_trail_diff/root_version_selection.rbs +2 -0
- data/sig/generated/paper_trail_diff/scope_condition.rbs +43 -9
- data/sig/generated/paper_trail_diff/scope_ranking.rbs +8 -0
- data/sig/generated/paper_trail_diff/scoped_analysis.rbs +6 -0
- data/sig/generated/paper_trail_diff/scoped_analysis_builder.rbs +6 -1
- data/sig/generated/paper_trail_diff/scoped_root_selection.rbs +8 -4
- data/sig/generated/paper_trail_diff/snapshot.rbs +8 -0
- data/sig/generated/paper_trail_diff/support.rbs +15 -0
- data/sig/generated/paper_trail_diff/time_range.rbs +20 -6
- data/sig/generated/paper_trail_diff/timeline_builder.rbs +2 -2
- data/sig/generated/paper_trail_diff/timeline_range.rbs +2 -0
- data/sig/generated/paper_trail_diff/timeline_snapshot_provider.rbs +17 -7
- data/sig/generated/paper_trail_diff/transaction_cut.rbs +94 -0
- data/sig/generated/paper_trail_diff/traversal_preparer.rbs +66 -8
- data/sig/generated/paper_trail_diff/version_boundary.rbs +20 -3
- data/sig/generated/paper_trail_diff/version_query.rbs +18 -2
- data/sig/generated/paper_trail_diff/version_scope_filter.rbs +4 -5
- data/sig/generated/paper_trail_diff/version_sequence_diagnostics.rbs +16 -9
- data/sig/generated/paper_trail_diff/version_table.rbs +30 -0
- data/sig/generated/paper_trail_diff.rbs +29 -26
- metadata +46 -10
- data/lib/paper_trail_diff/activity_grouping.rb +0 -38
- data/lib/paper_trail_diff/batch_safety.rb +0 -63
- data/lib/paper_trail_diff/time_activity_timeline_builder.rb +0 -223
- data/sig/generated/paper_trail_diff/activity_grouping.rbs +0 -22
- data/sig/generated/paper_trail_diff/batch_safety.rbs +0 -30
- data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +0 -92
|
@@ -70,6 +70,14 @@ module PaperTrailDiff
|
|
|
70
70
|
# : (Array[RecordSnapshot], before: RecordSnapshot?, after: RecordSnapshot?, membership_preserved: bool) -> AssociationSnapshot
|
|
71
71
|
def transition_to: (Array[RecordSnapshot], before: RecordSnapshot?, after: RecordSnapshot?, membership_preserved: bool) -> AssociationSnapshot
|
|
72
72
|
|
|
73
|
+
# Collections are documented in record type, then ID, order wherever they
|
|
74
|
+
# were read from. Most arrive sorted, so they are checked before sorting.
|
|
75
|
+
# : (Array[RecordSnapshot]) -> Array[RecordSnapshot]
|
|
76
|
+
def ordered: (Array[RecordSnapshot]) -> Array[RecordSnapshot]
|
|
77
|
+
|
|
78
|
+
# : (Array[Array[untyped]]) -> bool
|
|
79
|
+
def sorted?: (Array[Array[untyped]]) -> bool
|
|
80
|
+
|
|
73
81
|
# : (AssociationSnapshot) -> CollectionTransition?
|
|
74
82
|
def transition_from: (AssociationSnapshot) -> CollectionTransition?
|
|
75
83
|
|
|
@@ -12,6 +12,13 @@ module PaperTrailDiff
|
|
|
12
12
|
# : (untyped) -> untyped
|
|
13
13
|
def self?.serialize: (untyped) -> untyped
|
|
14
14
|
|
|
15
|
+
# Records sort by type first so that mixed id types stay comparable, then
|
|
16
|
+
# naturally within one type. Ordering by the printed form instead would put
|
|
17
|
+
# id 10 before id 2, which is deterministic but reads as unsorted wherever a
|
|
18
|
+
# result is rendered.
|
|
19
|
+
# : (untyped, untyped) -> Array[untyped]
|
|
20
|
+
def self?.sortable_identity: (untyped, untyped) -> Array[untyped]
|
|
21
|
+
|
|
15
22
|
# : (untyped) -> Array[untyped]
|
|
16
23
|
def self?.chronological_version_key: (untyped) -> Array[untyped]
|
|
17
24
|
|
|
@@ -28,6 +35,14 @@ module PaperTrailDiff
|
|
|
28
35
|
# : (Array[untyped]) -> Array[untyped]?
|
|
29
36
|
def self?.ambiguous_pair: (Array[untyped]) -> Array[untyped]?
|
|
30
37
|
|
|
38
|
+
# Ids order versions saved in one instant only when they count up in one
|
|
39
|
+
# table. Two version tables count separately, so ids there say nothing.
|
|
40
|
+
# : (untyped, untyped) -> bool
|
|
41
|
+
def self?.orderable_tie?: (untyped, untyped) -> bool
|
|
42
|
+
|
|
43
|
+
# : (untyped) -> untyped
|
|
44
|
+
def self?.version_table: (untyped) -> untyped
|
|
45
|
+
|
|
31
46
|
# Versions sharing a timestamp, whether or not their ids order them. PT-AT
|
|
32
47
|
# indexes association membership per version but resolves it by timestamp,
|
|
33
48
|
# so association state cannot be told apart across such a pair even when the
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Validates and applies a finite wall-clock range without loading model state.
|
|
5
|
+
#
|
|
6
|
+
# A Date is a whole day and a String a local time, both in Time.zone, as
|
|
7
|
+
# Rails reads them, or in UTC without one: never the server's own zone. An
|
|
8
|
+
# inclusive Date end runs through that day.
|
|
5
9
|
class TimeRange
|
|
6
10
|
attr_reader begin_time: Time
|
|
7
11
|
|
|
@@ -41,12 +45,22 @@ module PaperTrailDiff
|
|
|
41
45
|
# : (untyped, boundary: Symbol) -> Time
|
|
42
46
|
def coerce_time: (untyped, boundary: Symbol) -> Time
|
|
43
47
|
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
# Anything else names its instant, and `getutc` reads it without
|
|
49
|
+
# `to_time`, which Active Support 8.0 and older warn will change on a
|
|
50
|
+
# zoned time, and without changing the caller's own value as `utc` does.
|
|
51
|
+
# : (untyped) -> untyped
|
|
52
|
+
def time_value: (untyped) -> untyped
|
|
53
|
+
|
|
54
|
+
# An inclusive date end runs through the day: to the next one's start.
|
|
55
|
+
# : (Range[untyped]) -> untyped
|
|
56
|
+
def end_value: (Range[untyped]) -> untyped
|
|
57
|
+
|
|
58
|
+
# A DateTime is a Date too, but names its instant.
|
|
59
|
+
# : (untyped) -> bool
|
|
60
|
+
def calendar_date?: (untyped) -> bool
|
|
61
|
+
|
|
62
|
+
# : () -> untyped
|
|
63
|
+
def zone: () -> untyped
|
|
50
64
|
|
|
51
65
|
# : () -> void
|
|
52
66
|
def validate_order!: () -> void
|
|
@@ -23,8 +23,8 @@ module PaperTrailDiff
|
|
|
23
23
|
# : (RootVersionPlan) -> [Array[Step], RecordSnapshot?, RecordSnapshot?]
|
|
24
24
|
def compare_history: (RootVersionPlan) -> [ Array[Step], RecordSnapshot?, RecordSnapshot? ]
|
|
25
25
|
|
|
26
|
-
# : (RootVersionPlan) -> Array[Step]
|
|
27
|
-
def build_steps: (RootVersionPlan) -> Array[Step]
|
|
26
|
+
# : (RootVersionPlan, untyped) -> Array[Step]
|
|
27
|
+
def build_steps: (RootVersionPlan, untyped) -> Array[Step]
|
|
28
28
|
|
|
29
29
|
# : () -> [Array[Step], nil, nil]
|
|
30
30
|
def empty_history: () -> [ Array[Step], nil, nil ]
|
|
@@ -73,6 +73,8 @@ module PaperTrailDiff
|
|
|
73
73
|
# : () -> untyped
|
|
74
74
|
def ordered_versions: () -> untyped
|
|
75
75
|
|
|
76
|
+
# A batch passes each root the window it already moved to transaction
|
|
77
|
+
# edges; a single record's window is moved here.
|
|
76
78
|
# : (untyped) -> TimeRange?
|
|
77
79
|
def build_time_range: (untyped) -> TimeRange?
|
|
78
80
|
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
# Generated from lib/paper_trail_diff/timeline_snapshot_provider.rb with RBS::Inline
|
|
2
2
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
|
-
#
|
|
4
|
+
# Prepares the snapshot store for TimelineBuilder, one root at a time.
|
|
5
5
|
class TimelineSnapshotProvider
|
|
6
6
|
# : (HistoricalSnapshotStore, live_snapshotter: untyped) -> void
|
|
7
7
|
def initialize: (HistoricalSnapshotStore, live_snapshotter: untyped) -> void
|
|
8
8
|
|
|
9
|
-
# : (untyped, Array[untyped]) ->
|
|
10
|
-
def prepare: (untyped, Array[untyped]) ->
|
|
9
|
+
# : (untyped, Array[untyped]) -> PreparedTimelineSnapshots
|
|
10
|
+
def prepare: (untyped, Array[untyped]) -> PreparedTimelineSnapshots
|
|
11
|
+
|
|
12
|
+
@live_snapshotter: untyped
|
|
13
|
+
|
|
14
|
+
@store: HistoricalSnapshotStore
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# One root's timeline snapshots, read with the history prepared for it.
|
|
18
|
+
# Adjacent steps share endpoints, so each immutable snapshot, including a
|
|
19
|
+
# nil create state, is kept for this root only and released with it, and
|
|
20
|
+
# batch memory does not retain every reconstructed graph.
|
|
21
|
+
class PreparedTimelineSnapshots
|
|
22
|
+
# : (PreparedHistory?, store: HistoricalSnapshotStore, live_snapshotter: untyped) -> void
|
|
23
|
+
def initialize: (PreparedHistory?, store: HistoricalSnapshotStore, live_snapshotter: untyped) -> void
|
|
11
24
|
|
|
12
25
|
# A window closing on current state ends at the live record rather than at a
|
|
13
26
|
# version, which is the one endpoint no version history can reconstruct.
|
|
14
27
|
# : (untyped) -> RecordSnapshot?
|
|
15
28
|
def call: (untyped) -> RecordSnapshot?
|
|
16
29
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# : (untyped) -> RecordSnapshot?
|
|
20
|
-
def live_snapshot: (untyped) -> RecordSnapshot?
|
|
30
|
+
@history: PreparedHistory?
|
|
21
31
|
|
|
22
32
|
@live_snapshotter: untyped
|
|
23
33
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/transaction_cut.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Moves each edge of a wall-clock window that falls inside one of a root's
|
|
5
|
+
# transactions back to where that transaction began, so the transaction is
|
|
6
|
+
# wholly in the later window.
|
|
7
|
+
#
|
|
8
|
+
# PT-AT reads a root's associations at the start of the transaction of the
|
|
9
|
+
# root version it reifies. A window starting just after a child saved its
|
|
10
|
+
# part of such a transaction therefore starts from the state before that
|
|
11
|
+
# child's change, and one ending there ends at it too, so the change belonged
|
|
12
|
+
# to no window and surfaced at the next transaction, under its author.
|
|
13
|
+
#
|
|
14
|
+
# Only the root's own versions on either side of an edge are consulted:
|
|
15
|
+
# rows locked by an update keep two transactions of one root from
|
|
16
|
+
# interleaving, so a transaction holding a root version and crossing the edge
|
|
17
|
+
# holds the root's nearest version on one side of it. Each edge is moved from
|
|
18
|
+
# where it was asked for, never from where another edge moved, so adjacent
|
|
19
|
+
# windows move their shared edge alike.
|
|
20
|
+
module TransactionCut
|
|
21
|
+
# One edge of a window. A strict edge keeps versions recorded at its
|
|
22
|
+
# instant on the left, as an inclusive end does. A plain class rather than
|
|
23
|
+
# Data.define, which arrived in Ruby 3.2 while this gem supports 3.1.
|
|
24
|
+
class Edge
|
|
25
|
+
# : (Time, bool) -> void
|
|
26
|
+
def initialize: (Time, bool) -> void
|
|
27
|
+
|
|
28
|
+
# : (untyped) -> untyped
|
|
29
|
+
def left: (untyped) -> untyped
|
|
30
|
+
|
|
31
|
+
# : (untyped) -> untyped
|
|
32
|
+
def right: (untyped) -> untyped
|
|
33
|
+
|
|
34
|
+
# : (untyped, later: bool) -> untyped
|
|
35
|
+
def side: (untyped, later: bool) -> untyped
|
|
36
|
+
|
|
37
|
+
@strict: bool
|
|
38
|
+
|
|
39
|
+
@time: Time
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Roots per statement: two lookups each stays under SQLite's limit of 500
|
|
43
|
+
# terms in a compound SELECT.
|
|
44
|
+
SLICE: ::Integer
|
|
45
|
+
|
|
46
|
+
# The same as `windows` for a batch of roots, keyed by identity.
|
|
47
|
+
# : (Array[untyped], TimeRange?) -> Hash[Array[String], TimeRange]
|
|
48
|
+
def self.batch_windows: (Array[untyped], TimeRange?) -> Hash[Array[String], TimeRange]
|
|
49
|
+
|
|
50
|
+
# Where history for a batch must be read from: the earliest moved start.
|
|
51
|
+
# : (TimeRange?, Hash[Array[String], TimeRange]) -> Time?
|
|
52
|
+
def self.earliest_start: (TimeRange?, Hash[Array[String], TimeRange]) -> Time?
|
|
53
|
+
|
|
54
|
+
# The windows that move, keyed by root id. A root missing from the result
|
|
55
|
+
# keeps the window as asked.
|
|
56
|
+
# : (untyped, Array[untyped], TimeRange) -> Hash[String, TimeRange]
|
|
57
|
+
def self.windows: (untyped, Array[untyped], TimeRange) -> Hash[String, TimeRange]
|
|
58
|
+
|
|
59
|
+
# : (untyped) -> untyped
|
|
60
|
+
def self.transaction_versions: (untyped) -> untyped
|
|
61
|
+
|
|
62
|
+
# Each root's earliest start among its transactions that cross the edge.
|
|
63
|
+
# : (untyped, Array[untyped], Edge) -> Hash[String, Time]
|
|
64
|
+
def self.moved_edges: (untyped, Array[untyped], Edge) -> Hash[String, Time]
|
|
65
|
+
|
|
66
|
+
# The roots whose versions next to the edge each transaction holds.
|
|
67
|
+
# : (untyped, Array[untyped], Edge) -> Hash[untyped, Array[String]]
|
|
68
|
+
def self.nearest_roots: (untyped, Array[untyped], Edge) -> Hash[untyped, Array[String]]
|
|
69
|
+
|
|
70
|
+
# Each root's nearest versions on either side of an edge, as [id,
|
|
71
|
+
# transaction] pairs; versions tied at the nearest instant are all kept.
|
|
72
|
+
# One indexed lookup per root and side, where a single query over the
|
|
73
|
+
# roots would read every version before the edge. The lookups are written
|
|
74
|
+
# once with the root's id left open, as building a relation for each root
|
|
75
|
+
# costs more than the query. Rows read as SQL text come back as the adapter
|
|
76
|
+
# returns them, so the transaction is cast as the grouped query's keys are.
|
|
77
|
+
# : (untyped, Array[untyped], Edge) -> Array[[String, untyped]]
|
|
78
|
+
def self.nearest: (untyped, Array[untyped], Edge) -> Array[[ String, untyped ]]
|
|
79
|
+
|
|
80
|
+
# Both sides' lookups for a root whose id is each `?`.
|
|
81
|
+
# : (untyped, Edge) -> String
|
|
82
|
+
def self.nearest_lookups: (untyped, Edge) -> String
|
|
83
|
+
|
|
84
|
+
# : (untyped, Edge, bool) -> String
|
|
85
|
+
def self.nearest_lookup: (untyped, Edge, bool) -> String
|
|
86
|
+
|
|
87
|
+
# Where each transaction that has versions on both sides of the edge began.
|
|
88
|
+
# : (untyped, Array[untyped], Edge) -> Hash[untyped, Time]
|
|
89
|
+
def self.crossing_starts: (untyped, Array[untyped], Edge) -> Hash[untyped, Time]
|
|
90
|
+
|
|
91
|
+
# : (TimeRange, Time?, Time?) -> TimeRange
|
|
92
|
+
def self.moved: (TimeRange, Time?, Time?) -> TimeRange
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -3,30 +3,88 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Checks that a model can be traversed before any reconstruction starts, and
|
|
5
5
|
# that association tracking is actually available when history is involved.
|
|
6
|
+
# `diagnose` reports the same refusals instead of raising the first.
|
|
6
7
|
class TraversalPreparer
|
|
7
|
-
#
|
|
8
|
-
|
|
8
|
+
# One reason to refuse a request: the error `call` raises, and the code
|
|
9
|
+
# and path `diagnose` reports.
|
|
10
|
+
class Refusal
|
|
11
|
+
attr_reader error: untyped
|
|
12
|
+
|
|
13
|
+
attr_reader code: Symbol
|
|
14
|
+
|
|
15
|
+
attr_reader message: String
|
|
16
|
+
|
|
17
|
+
attr_reader path: String?
|
|
18
|
+
|
|
19
|
+
# : (untyped, Symbol, String, ?String?) -> void
|
|
20
|
+
def initialize: (untyped, Symbol, String, ?String?) -> void
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
PROBLEMS: untyped
|
|
24
|
+
|
|
25
|
+
# Each model a path's history needs versions of and has none, with the
|
|
26
|
+
# refusal's code: the target, then each model a has_many :through goes
|
|
27
|
+
# through. A polymorphic target is not known until a row names it;
|
|
28
|
+
# `diagnose` reports that separately rather than guessing here.
|
|
29
|
+
# : (untyped) -> Array[[Symbol, untyped]]
|
|
30
|
+
def self.unversioned_models: (untyped) -> Array[[ Symbol, untyped ]]
|
|
31
|
+
|
|
32
|
+
# A has_many :through reads past membership from the versions of the
|
|
33
|
+
# models it goes through, so each of them needs history too.
|
|
34
|
+
# : (untyped) -> Array[untyped]
|
|
35
|
+
def self.through_models: (untyped) -> Array[untyped]
|
|
36
|
+
|
|
37
|
+
# : (tree: AssociationTree, traversal: AssociationTraversal, ?allow_approximate: bool) -> void
|
|
38
|
+
def initialize: (tree: AssociationTree, traversal: AssociationTraversal, ?allow_approximate: bool) -> void
|
|
9
39
|
|
|
10
40
|
# : (untyped, historical: bool) -> void
|
|
11
41
|
def call: (untyped, historical: bool) -> void
|
|
12
42
|
|
|
43
|
+
# Every reason to refuse a request over the model, in the order `call`
|
|
44
|
+
# raises them. Live-to-live comparison reads current state, so only the
|
|
45
|
+
# traversal itself is checked for it.
|
|
46
|
+
# : (untyped, historical: bool) -> Array[Refusal]
|
|
47
|
+
def refusals: (untyped, historical: bool) -> Array[Refusal]
|
|
48
|
+
|
|
13
49
|
private
|
|
14
50
|
|
|
51
|
+
@allow_approximate: bool
|
|
52
|
+
|
|
53
|
+
@refusals: Hash[Array[untyped], Array[Refusal]]
|
|
54
|
+
|
|
15
55
|
@traversal: AssociationTraversal
|
|
16
56
|
|
|
17
57
|
@tree: AssociationTree
|
|
18
58
|
|
|
59
|
+
# : (untyped, bool) -> Array[Refusal]
|
|
60
|
+
def collect: (untyped, bool) -> Array[Refusal]
|
|
61
|
+
|
|
62
|
+
# : (untyped) -> Refusal?
|
|
63
|
+
def traversal_refusal: (untyped) -> Refusal?
|
|
64
|
+
|
|
65
|
+
# A path prepared history cannot read is read with PT-AT, which rebuilds
|
|
66
|
+
# an association from its current members, swapping in older versions of
|
|
67
|
+
# them. A record that entered the association since, such as one moved
|
|
68
|
+
# into a scope or restored from a soft delete, then reads as a member it
|
|
69
|
+
# was not, and one that left reads as missing. Only a caller who accepts
|
|
70
|
+
# that may read such a path.
|
|
71
|
+
# : (untyped) -> Array[Refusal]
|
|
72
|
+
def approximate_refusals: (untyped) -> Array[Refusal]
|
|
73
|
+
|
|
19
74
|
# A model PaperTrail never versioned has no history to reconstruct, so a
|
|
20
75
|
# comparison over it can only ever answer "nothing changed" -- which is a
|
|
21
76
|
# wrong answer rather than an empty one. Live-to-live comparison reads
|
|
22
77
|
# current state and is unaffected, so this applies to historical work only.
|
|
23
|
-
# : (untyped) ->
|
|
24
|
-
def
|
|
78
|
+
# : (untyped) -> Array[Refusal]
|
|
79
|
+
def unversioned_refusals: (untyped) -> Array[Refusal]
|
|
80
|
+
|
|
81
|
+
# : (String, untyped, Symbol) -> Refusal
|
|
82
|
+
def unversioned: (String, untyped, Symbol) -> Refusal
|
|
25
83
|
|
|
26
|
-
# : (
|
|
27
|
-
def
|
|
84
|
+
# : () -> bool
|
|
85
|
+
def association_tracking?: () -> bool
|
|
28
86
|
|
|
29
|
-
# : () ->
|
|
30
|
-
def
|
|
87
|
+
# : () -> Refusal
|
|
88
|
+
def tracking_refusal: () -> Refusal
|
|
31
89
|
end
|
|
32
90
|
end
|
|
@@ -11,13 +11,30 @@ module PaperTrailDiff
|
|
|
11
11
|
# : (untyped, untyped) -> bool
|
|
12
12
|
def self?.at_or_after?: (untyped, untyped) -> bool
|
|
13
13
|
|
|
14
|
+
# A range starting at a version starts before that version's transaction,
|
|
15
|
+
# as PT-AT reads associations there. A version saved earlier in the same
|
|
16
|
+
# transaction, such as the child update behind a `touch: true` parent
|
|
17
|
+
# version, can be recorded before the start, so the start moves back to
|
|
18
|
+
# the earliest version of each root's first transaction in the range.
|
|
19
|
+
# History loaded from the start alone would read that child after its
|
|
20
|
+
# change.
|
|
21
|
+
# : (Array[untyped], untyped) -> untyped
|
|
22
|
+
def self?.transaction_start: (Array[untyped], untyped) -> untyped
|
|
23
|
+
|
|
24
|
+
# The transaction of each root's first version at or after the start.
|
|
25
|
+
# : (Array[untyped], untyped) -> Array[untyped]
|
|
26
|
+
def self?.opening_transactions: (Array[untyped], untyped) -> Array[untyped]
|
|
27
|
+
|
|
28
|
+
# A custom version class need not carry the column, and PaperTrail leaves
|
|
29
|
+
# it nil outside a transaction. Both read as "no transaction here", which
|
|
30
|
+
# groups with nothing.
|
|
31
|
+
# : (untyped) -> untyped
|
|
32
|
+
def self?.transaction_id: (untyped) -> untyped
|
|
33
|
+
|
|
14
34
|
# : (untyped) -> bool
|
|
15
35
|
def self?.version_record?: (untyped) -> bool
|
|
16
36
|
|
|
17
37
|
# : (untyped, untyped) -> bool
|
|
18
38
|
def self?.same_version?: (untyped, untyped) -> bool
|
|
19
|
-
|
|
20
|
-
# : (untyped, untyped) -> bool
|
|
21
|
-
def self?.orderable_tie?: (untyped, untyped) -> bool
|
|
22
39
|
end
|
|
23
40
|
end
|
|
@@ -9,13 +9,29 @@ module PaperTrailDiff
|
|
|
9
9
|
# : (untyped) -> untyped
|
|
10
10
|
def self?.chronological: (untyped) -> untyped
|
|
11
11
|
|
|
12
|
+
# Every version of a model's items. They are read from the version class
|
|
13
|
+
# rather than through the model's versions association, whose scope may
|
|
14
|
+
# leave some out: a history replayed without them credits their changes to
|
|
15
|
+
# the next version kept.
|
|
16
|
+
# : (untyped) -> untyped
|
|
17
|
+
def self?.for_model: (untyped) -> untyped
|
|
18
|
+
|
|
19
|
+
# The versions of the given items, read the same way.
|
|
20
|
+
# : (untyped, untyped) -> untyped
|
|
21
|
+
def self?.for_items: (untyped, untyped) -> untyped
|
|
22
|
+
|
|
12
23
|
# Keeps each item's earliest version among those the block also accepts:
|
|
13
24
|
# no other version of the same item that the block accepts is earlier. A
|
|
14
25
|
# correlated NOT EXISTS stays portable where window functions are not.
|
|
15
26
|
# : (untyped) ?{ (untyped) -> untyped } -> untyped
|
|
16
27
|
def self?.no_earlier_version: (untyped) ?{ (untyped) -> untyped } -> untyped
|
|
17
28
|
|
|
18
|
-
#
|
|
19
|
-
|
|
29
|
+
# The same for either side of an instant: each item's earliest version
|
|
30
|
+
# when the versions lie after it, and its latest when they lie before.
|
|
31
|
+
# : (untyped, later: bool) ?{ (untyped) -> untyped } -> untyped
|
|
32
|
+
def self?.no_nearer_version: (untyped, later: bool) ?{ (untyped) -> untyped } -> untyped
|
|
33
|
+
|
|
34
|
+
# : (untyped, untyped, later: bool) ?{ (untyped) -> untyped } -> untyped
|
|
35
|
+
def self?.nearer_version: (untyped, untyped, later: bool) ?{ (untyped) -> untyped } -> untyped
|
|
20
36
|
end
|
|
21
37
|
end
|
|
@@ -6,6 +6,10 @@ module PaperTrailDiff
|
|
|
6
6
|
# caller, because the one that reveals the last selected mutation is drawn
|
|
7
7
|
# from them.
|
|
8
8
|
class VersionScopeFilter
|
|
9
|
+
# A filter is a callable that narrows the version relation.
|
|
10
|
+
# : (untyped) -> untyped
|
|
11
|
+
def self.validated: (untyped) -> untyped
|
|
12
|
+
|
|
9
13
|
# : (untyped) -> void
|
|
10
14
|
def initialize: (untyped) -> void
|
|
11
15
|
|
|
@@ -16,11 +20,6 @@ module PaperTrailDiff
|
|
|
16
20
|
# : (untyped) -> Set[untyped]?
|
|
17
21
|
def chosen_ids: (untyped) -> Set[untyped]?
|
|
18
22
|
|
|
19
|
-
private
|
|
20
|
-
|
|
21
23
|
@scope: untyped
|
|
22
|
-
|
|
23
|
-
# : (untyped) -> untyped
|
|
24
|
-
def validated: (untyped) -> untyped
|
|
25
24
|
end
|
|
26
25
|
end
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Checks the recorded sequence itself, before any question of association
|
|
5
5
|
# setup: whether the versions can be ordered at all, and whether timestamps
|
|
6
|
-
# they share hide changes
|
|
6
|
+
# they share hide changes from a path PT-AT reads.
|
|
7
7
|
class VersionSequenceDiagnostics
|
|
8
|
-
# : (untyped, untyped, ?
|
|
9
|
-
def initialize: (untyped, untyped, ?
|
|
8
|
+
# : (untyped, untyped, ?fallback_selected: bool) -> void
|
|
9
|
+
def initialize: (untyped, untyped, ?fallback_selected: bool) -> void
|
|
10
10
|
|
|
11
11
|
# : () -> Array[DiagnosticIssue]
|
|
12
12
|
def call: () -> Array[DiagnosticIssue]
|
|
13
13
|
|
|
14
14
|
private
|
|
15
15
|
|
|
16
|
-
@
|
|
16
|
+
@fallback_selected: bool
|
|
17
17
|
|
|
18
18
|
@from_version: untyped
|
|
19
19
|
|
|
@@ -25,15 +25,22 @@ module PaperTrailDiff
|
|
|
25
25
|
# : (Array[untyped]) -> DiagnosticIssue?
|
|
26
26
|
def unorderable: (Array[untyped]) -> DiagnosticIssue?
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
28
|
+
# Loaded history orders versions that share a timestamp by id, but PT-AT
|
|
29
|
+
# places association changes by timestamp alone, so a path it reads loses
|
|
30
|
+
# any change between such a pair. Unlike an unorderable sequence this is
|
|
31
|
+
# not always wrong: if nothing that path reaches changed between them the
|
|
32
|
+
# result is correct, and the gem cannot tell which it is, because not
|
|
33
|
+
# seeing the change is the symptom. So it warns.
|
|
33
34
|
# : (Array[untyped]) -> DiagnosticIssue?
|
|
34
35
|
def tied_timestamps: (Array[untyped]) -> DiagnosticIssue?
|
|
35
36
|
|
|
36
37
|
# : () -> Array[untyped]
|
|
37
38
|
def ordered_range_versions: () -> Array[untyped]
|
|
39
|
+
|
|
40
|
+
# Between the two versions, in either order, or from the one version on
|
|
41
|
+
# when the other endpoint is the current record. A version that recorded
|
|
42
|
+
# no time bounds nothing.
|
|
43
|
+
# : () -> Range[untyped]?
|
|
44
|
+
def time_range: () -> Range[untyped]?
|
|
38
45
|
end
|
|
39
46
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/version_table.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# PT-AT reads an association's history from its owner's version tables: it
|
|
5
|
+
# looks for a child's versions in the owner's version table, and numbers a
|
|
6
|
+
# transaction by the first version saved in it, in whichever table. A model
|
|
7
|
+
# whose versions, or version associations, live in another table than its
|
|
8
|
+
# owner's would read other records' versions or none, so it is refused.
|
|
9
|
+
module VersionTable
|
|
10
|
+
# Why the association, or a part of a through, cannot be read across
|
|
11
|
+
# version tables.
|
|
12
|
+
# : (untyped) -> String?
|
|
13
|
+
def self.unsupported_reason: (untyped) -> String?
|
|
14
|
+
|
|
15
|
+
# The reflection, or the first part of a through, whose ends keep versions
|
|
16
|
+
# in different tables. A polymorphic belongs_to names no single target,
|
|
17
|
+
# and an unversioned target has no history to compare.
|
|
18
|
+
# : (untyped) -> untyped
|
|
19
|
+
def self.separate_part: (untyped) -> untyped
|
|
20
|
+
|
|
21
|
+
# PT-AT's version association rows joined to the versions they were saved
|
|
22
|
+
# with. Its `belongs_to :version` names PaperTrail's default class whatever
|
|
23
|
+
# table a model's versions are in, so the join names the table instead.
|
|
24
|
+
# : (untyped, untyped) -> untyped
|
|
25
|
+
def self.joined: (untyped, untyped) -> untyped
|
|
26
|
+
|
|
27
|
+
# : (untyped) -> String
|
|
28
|
+
def self.tables: (untyped) -> String
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -9,36 +9,33 @@ module PaperTrailDiff
|
|
|
9
9
|
SUPPORTED_ASSOCIATION_MACROS: untyped
|
|
10
10
|
|
|
11
11
|
# Compares net state between explicit PaperTrail-version or current-record endpoints.
|
|
12
|
-
# : (untyped, untyped, ?associations:
|
|
13
|
-
def self.compare: (untyped, untyped, ?associations:
|
|
12
|
+
# : (untyped, untyped, ?associations: association_option, ?ignore: ignore_option, ?reload_live_endpoints: bool?, ?allow_approximate: bool) -> Diff
|
|
13
|
+
def self.compare: (untyped, untyped, ?associations: association_option, ?ignore: ignore_option, ?reload_live_endpoints: bool?, ?allow_approximate: bool) -> Diff
|
|
14
14
|
|
|
15
15
|
# Compares many independent endpoint pairs while batching current-record loading.
|
|
16
|
-
# : (Array[comparison_input], ?associations:
|
|
17
|
-
def self.compare_many: (Array[comparison_input], ?associations:
|
|
16
|
+
# : (Array[comparison_input], ?associations: association_option, ?ignore: ignore_option, ?reload_live_endpoints: bool?, ?allow_expensive: bool?, ?allow_approximate: bool) -> comparison_results
|
|
17
|
+
def self.compare_many: (Array[comparison_input], ?associations: association_option, ?ignore: ignore_option, ?reload_live_endpoints: bool?, ?allow_expensive: bool?, ?allow_approximate: bool) -> comparison_results
|
|
18
18
|
|
|
19
19
|
# Compares every adjacent reconstructed state in an inclusive version range,
|
|
20
20
|
# by default the record's whole history through its current state.
|
|
21
|
-
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations:
|
|
22
|
-
def self.timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations:
|
|
21
|
+
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?version_scope: untyped, ?close_on: Symbol?, ?allow_approximate: bool) -> Array[Step]
|
|
22
|
+
def self.timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?version_scope: untyped, ?close_on: Symbol?, ?allow_approximate: bool) -> Array[Step]
|
|
23
23
|
|
|
24
24
|
# Compares adjacent root and selected-descendant activity boundaries.
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
# later version.
|
|
28
|
-
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Array[ActivityStep]
|
|
29
|
-
def self.activity_timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Array[ActivityStep]
|
|
25
|
+
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?reload_live_endpoints: bool?, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?recorded_changes: bool, ?allow_approximate: bool) -> Array[ActivityStep]
|
|
26
|
+
def self.activity_timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?reload_live_endpoints: bool?, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?recorded_changes: bool, ?allow_approximate: bool) -> Array[ActivityStep]
|
|
30
27
|
|
|
31
28
|
# Builds an endpoint diff and root-checkpoint timeline while normalizing each version once.
|
|
32
|
-
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations:
|
|
33
|
-
def self.analyze: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations:
|
|
29
|
+
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?recorded_changes: bool, ?allow_approximate: bool) -> Analysis
|
|
30
|
+
def self.analyze: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?recorded_changes: bool, ?allow_approximate: bool) -> Analysis
|
|
34
31
|
|
|
35
32
|
# Analyzes an array of records over one shared range, preparing their
|
|
36
33
|
# selected history once for the batch instead of once per record, and
|
|
37
34
|
# returns a frozen Hash keyed by identity. Roots with nothing to report in
|
|
38
35
|
# the window return an empty `Analysis`. To let a relation choose the
|
|
39
36
|
# records, use `analyze_scope`.
|
|
40
|
-
# : (Array[untyped], ?within: untyped, ?associations:
|
|
41
|
-
def self.analyze_many: (Array[untyped], ?within: untyped, ?associations:
|
|
37
|
+
# : (Array[untyped], ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?allow_expensive: bool?, ?recorded_changes: bool, ?allow_approximate: bool) -> Hash[identity, Analysis]
|
|
38
|
+
def self.analyze_many: (Array[untyped], ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?allow_expensive: bool?, ?recorded_changes: bool, ?allow_approximate: bool) -> Hash[identity, Analysis]
|
|
42
39
|
|
|
43
40
|
# Analyzes every root the relation reaches whose history moved inside the
|
|
44
41
|
# window. With `activity: true`, a change to a record on one of the
|
|
@@ -66,15 +63,19 @@ module PaperTrailDiff
|
|
|
66
63
|
# Note also that a relation selects on current state, not on state during
|
|
67
64
|
# the window: `where(status: 'published')` means published *now*, which is a
|
|
68
65
|
# different set from what was published while the window was open.
|
|
69
|
-
# : (untyped, limit: Integer?, ?within: untyped, ?associations:
|
|
70
|
-
def self.analyze_scope: (untyped, limit: Integer?, ?within: untyped, ?associations:
|
|
66
|
+
# : (untyped, limit: Integer?, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?historical_filter: untyped, ?max_filter_versions: Integer, ?include_deleted: bool, ?allow_expensive: bool?, ?recorded_changes: bool, ?allow_approximate: bool) -> ScopedAnalysis
|
|
67
|
+
def self.analyze_scope: (untyped, limit: Integer?, ?within: untyped, ?associations: association_option, ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?historical_filter: untyped, ?max_filter_versions: Integer, ?include_deleted: bool, ?allow_expensive: bool?, ?recorded_changes: bool, ?allow_approximate: bool) -> ScopedAnalysis
|
|
71
68
|
|
|
72
69
|
# Recent root and selected-child activity through the current saved state,
|
|
73
70
|
# for every root that changed or whose requested associations did.
|
|
74
71
|
# Historical windows remain explicit on analyze_scope; this entry point is
|
|
75
72
|
# deliberately named for a report through now rather than an as-of query.
|
|
76
|
-
# : (untyped, since: untyped, limit: Integer, ?associations:
|
|
77
|
-
def self.report: (untyped, since: untyped, limit: Integer, ?associations:
|
|
73
|
+
# : (untyped, since: untyped, limit: Integer, ?associations: association_option, ?ignore: ignore_option, ?snapshots: bool, ?group: Symbol?, ?include_deleted: bool, ?historical_filter: untyped, ?max_filter_versions: Integer, ?allow_expensive: bool?, ?recorded_changes: bool, ?allow_approximate: bool) -> ScopedAnalysis
|
|
74
|
+
def self.report: (untyped, since: untyped, limit: Integer, ?associations: association_option, ?ignore: ignore_option, ?snapshots: bool, ?group: Symbol?, ?include_deleted: bool, ?historical_filter: untyped, ?max_filter_versions: Integer, ?allow_expensive: bool?, ?recorded_changes: bool, ?allow_approximate: bool) -> ScopedAnalysis
|
|
75
|
+
|
|
76
|
+
# Warns about deprecated options; see Deprecation.
|
|
77
|
+
# : () -> untyped
|
|
78
|
+
def self.deprecator: () -> untyped
|
|
78
79
|
|
|
79
80
|
# Returns the association macros this release can normalize.
|
|
80
81
|
# : () -> Array[Symbol]
|
|
@@ -84,9 +85,11 @@ module PaperTrailDiff
|
|
|
84
85
|
# : (untyped, ?max_depth: Integer) -> Array[AssociationDescriptor]
|
|
85
86
|
def self.association_paths: (untyped, ?max_depth: Integer) -> Array[AssociationDescriptor]
|
|
86
87
|
|
|
87
|
-
# Reports known reconstruction hazards without mutating application state
|
|
88
|
-
# :
|
|
89
|
-
|
|
88
|
+
# Reports known reconstruction hazards without mutating application state,
|
|
89
|
+
# for the endpoints `compare` takes: two versions, or a version and the
|
|
90
|
+
# current record. Every reason `compare` would refuse them is an error.
|
|
91
|
+
# : (untyped, untyped, ?associations: association_option, ?allow_approximate: bool) -> DiagnosticReport
|
|
92
|
+
def self.diagnose: (untyped, untyped, ?associations: association_option, ?allow_approximate: bool) -> DiagnosticReport
|
|
90
93
|
|
|
91
94
|
# Looks inside an attribute the database stores whole, such as a JSON or
|
|
92
95
|
# jsonb column, and reports which keys changed.
|
|
@@ -111,9 +114,7 @@ module PaperTrailDiff
|
|
|
111
114
|
|
|
112
115
|
# Reads what PaperTrail recorded with one version: its `object_changes`,
|
|
113
116
|
# typed by the version's model, as `{ "title" => <from "Draft" to "Published"> }`.
|
|
114
|
-
#
|
|
115
|
-
# versions = PaperTrail::Version.where(id: steps.map { _1.source_boundary.version_id })
|
|
116
|
-
# versions.to_h { [_1.id, PaperTrailDiff.recorded_changes(_1)] }
|
|
117
|
+
# Activity methods read it for every step source with `recorded_changes: true`.
|
|
117
118
|
#
|
|
118
119
|
# A step's diff compares the states before and after an event, so it also
|
|
119
120
|
# includes anything saved without a version in between; this does not.
|
|
@@ -149,10 +150,12 @@ module PaperTrailDiff
|
|
|
149
150
|
|
|
150
151
|
type comparison_results = Hash[identity, Diff]
|
|
151
152
|
|
|
152
|
-
type report_options = { within: untyped, activity: bool, version_scope: untyped,
|
|
153
|
+
type report_options = { within: untyped, activity: bool, version_scope: untyped, view: ActivityView }
|
|
153
154
|
|
|
154
155
|
type ignore_option = Array[String | Symbol] | Hash[String | Symbol, untyped]
|
|
155
156
|
|
|
157
|
+
type association_option = String | Symbol | Array[untyped] | Hash[untyped, untyped]
|
|
158
|
+
|
|
156
159
|
type reference_key = :type | :id | "type" | "id"
|
|
157
160
|
|
|
158
161
|
type traversal_context = :change | :included_state
|