paper_trail_diff 0.4.0 → 0.6.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 +44 -0
- data/README.md +120 -3
- data/lib/paper_trail_diff/activity_root_steps.rb +9 -7
- data/lib/paper_trail_diff/activity_timeline_builder.rb +15 -8
- data/lib/paper_trail_diff/analysis.rb +19 -5
- data/lib/paper_trail_diff/analysis_batch.rb +92 -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 +149 -0
- data/lib/paper_trail_diff/comparison_batch.rb +37 -13
- data/lib/paper_trail_diff/historical_snapshot_store.rb +3 -7
- data/lib/paper_trail_diff/paper_trail_adapter.rb +70 -47
- data/lib/paper_trail_diff/root_version_plan.rb +86 -0
- data/lib/paper_trail_diff/root_version_selection.rb +131 -0
- data/lib/paper_trail_diff/time_activity_timeline_builder.rb +25 -13
- data/lib/paper_trail_diff/time_version_range.rb +19 -24
- data/lib/paper_trail_diff/timeline_builder.rb +21 -18
- data/lib/paper_trail_diff/timeline_range.rb +37 -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_range.rb +45 -5
- data/lib/paper_trail_diff/version_scope_filter.rb +37 -0
- data/lib/paper_trail_diff.rb +54 -12
- data/sig/generated/paper_trail_diff/activity_root_steps.rbs +2 -2
- data/sig/generated/paper_trail_diff/activity_timeline_builder.rbs +6 -2
- data/sig/generated/paper_trail_diff/analysis.rbs +13 -4
- data/sig/generated/paper_trail_diff/analysis_batch.rbs +49 -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 +69 -0
- data/sig/generated/paper_trail_diff/comparison_batch.rbs +15 -0
- data/sig/generated/paper_trail_diff/historical_snapshot_store.rbs +0 -1
- data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +24 -15
- data/sig/generated/paper_trail_diff/root_version_plan.rbs +56 -0
- data/sig/generated/paper_trail_diff/root_version_selection.rbs +77 -0
- data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +9 -4
- data/sig/generated/paper_trail_diff/time_version_range.rbs +7 -9
- data/sig/generated/paper_trail_diff/timeline_builder.rbs +6 -6
- data/sig/generated/paper_trail_diff/timeline_range.rbs +15 -2
- data/sig/generated/paper_trail_diff/traversal_preparer.rbs +22 -0
- data/sig/generated/paper_trail_diff/version_range.rbs +17 -2
- data/sig/generated/paper_trail_diff/version_scope_filter.rbs +22 -0
- data/sig/generated/paper_trail_diff.rbs +17 -7
- metadata +20 -4
|
@@ -0,0 +1,49 @@
|
|
|
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, ?version_scope: untyped) -> void
|
|
9
|
+
def initialize: (Array[untyped], time_range: TimeRange?, live_loader: untyped, history_preparer: untyped, analyzer: untyped, ?version_scope: 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
|
+
@version_scope: untyped
|
|
27
|
+
|
|
28
|
+
# A filter is a callable that narrows the version relation, so it is checked
|
|
29
|
+
# up front rather than failing partway through a batch.
|
|
30
|
+
# : (untyped) -> untyped
|
|
31
|
+
def validated_scope: (untyped) -> untyped
|
|
32
|
+
|
|
33
|
+
# : () -> Array[untyped]
|
|
34
|
+
def validated_records: () -> Array[untyped]
|
|
35
|
+
|
|
36
|
+
# Association history is prepared per model class across every selected root
|
|
37
|
+
# version, which is the work that would otherwise repeat for each record.
|
|
38
|
+
# The roots are preloaded first, because preparation reads their current
|
|
39
|
+
# association state as a fallback and would otherwise walk it one root at a
|
|
40
|
+
# time.
|
|
41
|
+
# : (Array[untyped], Hash[Array[String], RootVersionPlan]) -> void
|
|
42
|
+
def prepare: (Array[untyped], Hash[Array[String], RootVersionPlan]) -> void
|
|
43
|
+
|
|
44
|
+
# A root with no versions in range has nothing to report, which is an empty
|
|
45
|
+
# result rather than a failed request.
|
|
46
|
+
# : (untyped, RootVersionPlan) -> Analysis
|
|
47
|
+
def analysis_for: (untyped, RootVersionPlan) -> Analysis
|
|
48
|
+
end
|
|
49
|
+
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, RootVersionPlan) -> Analysis
|
|
11
|
+
def call: (untyped, RootVersionPlan) -> 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, RootVersionPlan) -> Analysis
|
|
26
|
+
def activity_analysis: (untyped, RootVersionPlan) -> Analysis
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
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?, ?version_scope: untyped) -> void
|
|
9
|
+
def initialize: (Array[untyped], time_range: TimeRange?, ?version_scope: untyped) -> void
|
|
10
|
+
|
|
11
|
+
# Returns a plan per record identity.
|
|
12
|
+
# : () -> Hash[Array[String], RootVersionPlan]
|
|
13
|
+
def call: () -> Hash[Array[String], RootVersionPlan]
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
@records: Array[untyped]
|
|
18
|
+
|
|
19
|
+
@time_range: TimeRange?
|
|
20
|
+
|
|
21
|
+
@version_scope: untyped
|
|
22
|
+
|
|
23
|
+
# : (untyped, Array[untyped], Hash[Array[String], RootVersionPlan]) -> void
|
|
24
|
+
def select_model: (untyped, Array[untyped], Hash[Array[String], RootVersionPlan]) -> void
|
|
25
|
+
|
|
26
|
+
# : (untyped, Array[untyped]) -> [Hash[String, Array[untyped]], Set[untyped]?, Hash[String, untyped]]
|
|
27
|
+
def model_versions: (untyped, Array[untyped]) -> [ Hash[String, Array[untyped]], Set[untyped]?, Hash[String, untyped] ]
|
|
28
|
+
|
|
29
|
+
# : (Array[untyped], Set[untyped]?, untyped) -> RootVersionPlan
|
|
30
|
+
def versions_for: (Array[untyped], Set[untyped]?, untyped) -> RootVersionPlan
|
|
31
|
+
|
|
32
|
+
# One extra query names the selected mutations without discarding the
|
|
33
|
+
# unfiltered versions the successor lookup still needs.
|
|
34
|
+
# : (untyped, Array[untyped], TimeRange?) -> Set[untyped]?
|
|
35
|
+
def chosen_version_ids: (untyped, Array[untyped], TimeRange?) -> Set[untyped]?
|
|
36
|
+
|
|
37
|
+
# : (untyped, Array[untyped], TimeRange?) -> Hash[String, Array[untyped]]
|
|
38
|
+
def grouped_versions: (untyped, Array[untyped], TimeRange?) -> Hash[String, Array[untyped]]
|
|
39
|
+
|
|
40
|
+
# : (untyped, Array[untyped], TimeRange?) -> untyped
|
|
41
|
+
def range_scope: (untyped, Array[untyped], TimeRange?) -> untyped
|
|
42
|
+
|
|
43
|
+
# One row per root: the earliest version after the window, found without a
|
|
44
|
+
# window function so the query stays portable.
|
|
45
|
+
# : (untyped, Array[untyped], TimeRange) -> Hash[String, untyped]
|
|
46
|
+
def trailing_versions: (untyped, Array[untyped], TimeRange) -> Hash[String, untyped]
|
|
47
|
+
|
|
48
|
+
# : (untyped, TimeRange) -> untyped
|
|
49
|
+
def no_earlier_trailing: (untyped, TimeRange) -> untyped
|
|
50
|
+
|
|
51
|
+
# Mirrors the window's own end handling, so a version sitting exactly on an
|
|
52
|
+
# inclusive boundary stays inside the window instead of masking the trailing
|
|
53
|
+
# version that follows it.
|
|
54
|
+
# : (untyped, untyped, TimeRange) -> untyped
|
|
55
|
+
def preceding: (untyped, untyped, TimeRange) -> untyped
|
|
56
|
+
|
|
57
|
+
# : (untyped, TimeRange) -> untyped
|
|
58
|
+
def after_window: (untyped, TimeRange) -> untyped
|
|
59
|
+
|
|
60
|
+
# : (untyped, Array[untyped]) -> untyped
|
|
61
|
+
def base_scope: (untyped, Array[untyped]) -> untyped
|
|
62
|
+
|
|
63
|
+
# : (untyped) -> Array[untyped]
|
|
64
|
+
def ordered: (untyped) -> Array[untyped]
|
|
65
|
+
|
|
66
|
+
# : (untyped, untyped) -> Array[String]
|
|
67
|
+
def identity: (untyped, untyped) -> Array[String]
|
|
68
|
+
end
|
|
69
|
+
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
|
|
|
@@ -9,7 +9,6 @@ module PaperTrailDiff
|
|
|
9
9
|
# : (untyped, Array[untyped], ?start_at: untyped, ?end_at: untyped) -> void
|
|
10
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
|
|
@@ -13,14 +16,18 @@ module PaperTrailDiff
|
|
|
13
16
|
# : (Array[comparison_input]) -> comparison_results
|
|
14
17
|
def compare_many: (Array[comparison_input]) -> comparison_results
|
|
15
18
|
|
|
16
|
-
# : (untyped, from: untyped, to: untyped, within: untyped) -> Array[Step]
|
|
17
|
-
def timeline: (untyped, from: untyped, to: untyped, within: untyped) -> Array[Step]
|
|
19
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped) -> Array[Step]
|
|
20
|
+
def timeline: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped) -> Array[Step]
|
|
18
21
|
|
|
19
|
-
# : (untyped, from: untyped, to: untyped, within: untyped) -> Array[ActivityStep]
|
|
20
|
-
def activity_timeline: (untyped, from: untyped, to: untyped, within: untyped) -> Array[ActivityStep]
|
|
22
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped) -> Array[ActivityStep]
|
|
23
|
+
def activity_timeline: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped) -> Array[ActivityStep]
|
|
21
24
|
|
|
22
|
-
# : (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool) -> Analysis
|
|
23
|
-
def analyze: (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool) -> Analysis
|
|
25
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool, ?version_scope: untyped) -> Analysis
|
|
26
|
+
def analyze: (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool, ?version_scope: untyped) -> Analysis
|
|
27
|
+
|
|
28
|
+
# Analyzes many roots over one shared range, preparing their history once.
|
|
29
|
+
# : (Array[untyped], within: untyped, ?activity: bool, ?version_scope: untyped) -> Hash[identity, Analysis]
|
|
30
|
+
def analyze_many: (Array[untyped], within: untyped, ?activity: bool, ?version_scope: untyped) -> Hash[identity, Analysis]
|
|
24
31
|
|
|
25
32
|
private
|
|
26
33
|
|
|
@@ -44,14 +51,22 @@ 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
|
|
|
50
65
|
# : () -> ActivitySnapshotProvider
|
|
51
66
|
def build_activity_snapshotter: () -> ActivitySnapshotProvider
|
|
52
67
|
|
|
53
|
-
# : (untyped, from: untyped, to: untyped, within: untyped) -> ActivityTimelineBuilder
|
|
54
|
-
def activity_builder: (untyped, from: untyped, to: untyped, within: untyped) -> ActivityTimelineBuilder
|
|
68
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped) -> ActivityTimelineBuilder
|
|
69
|
+
def activity_builder: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped) -> ActivityTimelineBuilder
|
|
55
70
|
|
|
56
71
|
# : (untyped) -> void
|
|
57
72
|
def reject_live_habtm_activity!: (untyped) -> void
|
|
@@ -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
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/root_version_plan.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Which versions a range reconstructs, and which pairs of them become steps.
|
|
5
|
+
#
|
|
6
|
+
# Without a filter those are the same thing: every selected version is a
|
|
7
|
+
# boundary and adjacent ones bound each other. A filter separates them, because
|
|
8
|
+
# each selected mutation then has to be bounded by the version that actually
|
|
9
|
+
# reveals it rather than by the next mutation that happened to be selected.
|
|
10
|
+
#
|
|
11
|
+
# A filter also separates what is *reported* from what has to be *replayed*.
|
|
12
|
+
# The activity view rebuilds state by carrying one snapshot forward across
|
|
13
|
+
# every event in order, so skipping a filtered-out version would leave that
|
|
14
|
+
# snapshot holding a state the record had already moved past. `versions` are
|
|
15
|
+
# the boundaries the steps refer to; `reconstruction_versions` are every root
|
|
16
|
+
# version the span passes through, which is what any forward replay must walk.
|
|
17
|
+
class RootVersionPlan
|
|
18
|
+
attr_reader versions: Array[untyped]
|
|
19
|
+
|
|
20
|
+
attr_reader reconstruction_versions: Array[untyped]
|
|
21
|
+
|
|
22
|
+
attr_reader steps: Array[[ untyped, untyped ]]
|
|
23
|
+
|
|
24
|
+
attr_reader context_version: untyped
|
|
25
|
+
|
|
26
|
+
# The root versions this plan reports as mutations, which excludes any
|
|
27
|
+
# version present only to reveal what the last of them produced.
|
|
28
|
+
attr_reader mutations: Array[untyped]
|
|
29
|
+
|
|
30
|
+
# Adjacent boundaries, which is what an unfiltered range reports.
|
|
31
|
+
# : (Array[untyped], ?context_version: untyped) -> RootVersionPlan
|
|
32
|
+
def self.contiguous: (Array[untyped], ?context_version: untyped) -> RootVersionPlan
|
|
33
|
+
|
|
34
|
+
# : () -> RootVersionPlan
|
|
35
|
+
def self.empty: () -> RootVersionPlan
|
|
36
|
+
|
|
37
|
+
# : (versions: Array[untyped], steps: Array[[untyped, untyped]], ?context_version: untyped, ?reconstruction_versions: Array[untyped]?, ?mutations: Array[untyped]?) -> void
|
|
38
|
+
def initialize: (versions: Array[untyped], steps: Array[[ untyped, untyped ]], ?context_version: untyped, ?reconstruction_versions: Array[untyped]?, ?mutations: Array[untyped]?) -> void
|
|
39
|
+
|
|
40
|
+
# : () -> bool
|
|
41
|
+
def empty?: () -> bool
|
|
42
|
+
|
|
43
|
+
# : (untyped) -> bool
|
|
44
|
+
def mutation?: (untyped) -> bool
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
@mutation_keys: Set[Array[untyped]]
|
|
49
|
+
|
|
50
|
+
# : () -> Array[untyped]
|
|
51
|
+
def default_mutations: () -> Array[untyped]
|
|
52
|
+
|
|
53
|
+
# : (untyped) -> Array[untyped]
|
|
54
|
+
def key: (untyped) -> Array[untyped]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/root_version_selection.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Decides which versions a range reports, and which one reveals the last of
|
|
5
|
+
# them. A version records the state before its own event, so whatever follows
|
|
6
|
+
# the final selected mutation is the only thing that can show what it produced.
|
|
7
|
+
# That successor is reconstruction context rather than a reported mutation, so
|
|
8
|
+
# a filter must never remove it.
|
|
9
|
+
#
|
|
10
|
+
# Both the single-record and the batched selectors resolve this here, because
|
|
11
|
+
# the rule is subtle enough that two copies of it would drift apart.
|
|
12
|
+
class RootVersionSelection
|
|
13
|
+
INCOMPLETE: ::String
|
|
14
|
+
|
|
15
|
+
# : (in_range: Array[untyped], selected: Array[untyped], after_range: untyped, windowed: bool, ?context_required: bool, ?filtered: bool) -> void
|
|
16
|
+
def initialize: (in_range: Array[untyped], selected: Array[untyped], after_range: untyped, windowed: bool, ?context_required: bool, ?filtered: bool) -> void
|
|
17
|
+
|
|
18
|
+
# : () -> RootVersionPlan
|
|
19
|
+
def call: () -> RootVersionPlan
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
@after_range: untyped
|
|
24
|
+
|
|
25
|
+
@context_required: bool
|
|
26
|
+
|
|
27
|
+
@filtered: bool
|
|
28
|
+
|
|
29
|
+
@in_range: Array[untyped]
|
|
30
|
+
|
|
31
|
+
@selected: Array[untyped]
|
|
32
|
+
|
|
33
|
+
@windowed: bool
|
|
34
|
+
|
|
35
|
+
# An activity view still needs a root to reconstruct from even when no root
|
|
36
|
+
# version falls inside the window, because descendants may have moved.
|
|
37
|
+
# : () -> RootVersionPlan
|
|
38
|
+
def without_selection: () -> RootVersionPlan
|
|
39
|
+
|
|
40
|
+
# Each selected mutation is bounded by the version that reveals it, not by
|
|
41
|
+
# the next mutation that happened to be selected. Bounding by the next
|
|
42
|
+
# selection would fold anything filtered out in between into it, so the same
|
|
43
|
+
# edit would read differently depending on what followed it.
|
|
44
|
+
# : (untyped) -> RootVersionPlan
|
|
45
|
+
def filtered_plan: (untyped) -> RootVersionPlan
|
|
46
|
+
|
|
47
|
+
# Nothing can follow a destroy, so it never pairs into a step. It is still a
|
|
48
|
+
# selected mutation, and the activity view closes on the absence it leaves,
|
|
49
|
+
# so it has to stay a boundary or a filtered report loses the deletion
|
|
50
|
+
# entirely — the one event it can least afford to drop.
|
|
51
|
+
# : () -> Array[untyped]
|
|
52
|
+
def closing_versions: () -> Array[untyped]
|
|
53
|
+
|
|
54
|
+
# Everything the span passes through, filtered out or not. A replay that
|
|
55
|
+
# skipped the excluded versions would carry a stale state into the next
|
|
56
|
+
# reported step, so the two selected mutations either side of a gap would
|
|
57
|
+
# disagree about what the record looked like between them.
|
|
58
|
+
# : (Array[untyped]) -> Array[untyped]
|
|
59
|
+
def spanned: (Array[untyped]) -> Array[untyped]
|
|
60
|
+
|
|
61
|
+
# : (Array[untyped]) -> Array[untyped]
|
|
62
|
+
def chronological: (Array[untyped]) -> Array[untyped]
|
|
63
|
+
|
|
64
|
+
# : (untyped) -> untyped
|
|
65
|
+
def immediate_successor: (untyped) -> untyped
|
|
66
|
+
|
|
67
|
+
# A version left out by a filter is still the state the last selected change
|
|
68
|
+
# produced, so it is preferred over anything after the range.
|
|
69
|
+
# : () -> untyped
|
|
70
|
+
def revealing_version: () -> untyped
|
|
71
|
+
|
|
72
|
+
# A range closing on the record's own destruction needs no later version:
|
|
73
|
+
# nothing can follow it, so demanding one would reject the range forever.
|
|
74
|
+
# : () -> bool
|
|
75
|
+
def terminal_destroy?: () -> bool
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -22,8 +22,8 @@ module PaperTrailDiff
|
|
|
22
22
|
|
|
23
23
|
@tree: AssociationTree
|
|
24
24
|
|
|
25
|
-
# : () -> [ActivityHistory,
|
|
26
|
-
def history_and_versions: () -> [ ActivityHistory,
|
|
25
|
+
# : () -> [ActivityHistory, RootVersionPlan, ActivityStep?]
|
|
26
|
+
def history_and_versions: () -> [ ActivityHistory, RootVersionPlan, ActivityStep? ]
|
|
27
27
|
|
|
28
28
|
# : (Array[untyped], Array[ActivityEvent]) -> ActivityHistory
|
|
29
29
|
def build_history: (Array[untyped], Array[ActivityEvent]) -> ActivityHistory
|
|
@@ -31,8 +31,13 @@ module PaperTrailDiff
|
|
|
31
31
|
# : (ActivityHistory, ActivityStep?) -> Array[ActivityStep]
|
|
32
32
|
def activity_steps: (ActivityHistory, ActivityStep?) -> Array[ActivityStep]
|
|
33
33
|
|
|
34
|
-
#
|
|
35
|
-
|
|
34
|
+
# Root versions inside the window that the plan does not report are context
|
|
35
|
+
# rather than mutations: the version appended to reveal the last selected
|
|
36
|
+
# change, and anything a filter excluded but the replay still walks. Neither
|
|
37
|
+
# may be counted as a selected mutation. Descendant events are not filtered,
|
|
38
|
+
# so window membership is the whole test for them.
|
|
39
|
+
# : (Array[ActivityEvent], RootVersionPlan) -> Array[ActivityEvent]
|
|
40
|
+
def selected_events: (Array[ActivityEvent], RootVersionPlan) -> Array[ActivityEvent]
|
|
36
41
|
|
|
37
42
|
# The window's last selected mutation is the root's own destruction, so the
|
|
38
43
|
# timeline closes on the absence it leaves rather than on a later boundary.
|
|
@@ -3,28 +3,26 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Selects in-range root versions plus one later reconstruction boundary.
|
|
5
5
|
class TimeVersionRange
|
|
6
|
-
# : (untyped, time_range: TimeRange) -> void
|
|
7
|
-
def initialize: (untyped, time_range: TimeRange) -> void
|
|
6
|
+
# : (untyped, time_range: TimeRange, ?version_scope: untyped) -> void
|
|
7
|
+
def initialize: (untyped, time_range: TimeRange, ?version_scope: untyped) -> void
|
|
8
8
|
|
|
9
9
|
# : (?context_required: bool) -> Array[untyped]
|
|
10
10
|
def select: (?context_required: bool) -> Array[untyped]
|
|
11
11
|
|
|
12
|
+
# : (?context_required: bool) -> RootVersionPlan
|
|
13
|
+
def select_plan: (?context_required: bool) -> RootVersionPlan
|
|
14
|
+
|
|
12
15
|
private
|
|
13
16
|
|
|
14
17
|
@record: untyped
|
|
15
18
|
|
|
16
19
|
@time_range: TimeRange
|
|
17
20
|
|
|
21
|
+
@version_scope: untyped
|
|
22
|
+
|
|
18
23
|
# : () -> untyped
|
|
19
24
|
def versions_relation: () -> untyped
|
|
20
25
|
|
|
21
|
-
# A window closing on the record's own destruction needs no later version:
|
|
22
|
-
# the destroy reveals the preceding mutation and nothing can follow it, so
|
|
23
|
-
# demanding a checkpoint that can never be written would reject the range
|
|
24
|
-
# permanently.
|
|
25
|
-
# : (Array[untyped]) -> bool
|
|
26
|
-
def terminal_destroy?: (Array[untyped]) -> bool
|
|
27
|
-
|
|
28
26
|
# : (untyped) -> untyped
|
|
29
27
|
def trailing_version: (untyped) -> untyped
|
|
30
28
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Selects and compares a chronological slice of a record's version history.
|
|
5
5
|
class TimelineBuilder
|
|
6
|
-
# : (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped) -> void
|
|
7
|
-
def initialize: (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped) -> void
|
|
6
|
+
# : (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?) -> void
|
|
7
|
+
def initialize: (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?) -> void
|
|
8
8
|
|
|
9
9
|
# : () -> Array[Step]
|
|
10
10
|
def build: () -> Array[Step]
|
|
@@ -20,13 +20,13 @@ module PaperTrailDiff
|
|
|
20
20
|
|
|
21
21
|
@snapshotter: untyped
|
|
22
22
|
|
|
23
|
-
# : (
|
|
24
|
-
def compare_history: (
|
|
23
|
+
# : (RootVersionPlan) -> [Array[Step], RecordSnapshot?, RecordSnapshot?]
|
|
24
|
+
def compare_history: (RootVersionPlan) -> [ Array[Step], RecordSnapshot?, RecordSnapshot? ]
|
|
25
25
|
|
|
26
26
|
# : () -> [Array[Step], nil, nil]
|
|
27
27
|
def empty_history: () -> [ Array[Step], nil, nil ]
|
|
28
28
|
|
|
29
|
-
# : () ->
|
|
30
|
-
def selected_versions: () ->
|
|
29
|
+
# : () -> RootVersionPlan
|
|
30
|
+
def selected_versions: () -> RootVersionPlan
|
|
31
31
|
end
|
|
32
32
|
end
|
|
@@ -11,14 +11,21 @@ module PaperTrailDiff
|
|
|
11
11
|
|
|
12
12
|
attr_reader time_range: TimeRange?
|
|
13
13
|
|
|
14
|
-
# : (untyped, from: untyped, to: untyped, within: untyped) -> void
|
|
15
|
-
def initialize: (untyped, from: untyped, to: untyped, within: untyped) -> void
|
|
14
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?) -> void
|
|
15
|
+
def initialize: (untyped, from: untyped, to: untyped, within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?) -> void
|
|
16
16
|
|
|
17
17
|
# A record with no versions has no first or last boundary to resolve, which
|
|
18
18
|
# is an empty history rather than a bad request.
|
|
19
19
|
# : () -> bool
|
|
20
20
|
def unresolved?: () -> bool
|
|
21
21
|
|
|
22
|
+
# A batch may have selected these versions already, in which case reselecting
|
|
23
|
+
# them per record would undo the batching.
|
|
24
|
+
# The plan says which pairs of versions become steps, which a filter can
|
|
25
|
+
# make different from adjacent pairs of the selected versions.
|
|
26
|
+
# : (?context_required: bool) -> RootVersionPlan
|
|
27
|
+
def select_plan: (?context_required: bool) -> RootVersionPlan
|
|
28
|
+
|
|
22
29
|
# : (?context_required: bool) -> Array[untyped]
|
|
23
30
|
def select: (?context_required: bool) -> Array[untyped]
|
|
24
31
|
|
|
@@ -35,6 +42,8 @@ module PaperTrailDiff
|
|
|
35
42
|
|
|
36
43
|
@from: untyped
|
|
37
44
|
|
|
45
|
+
@plan: RootVersionPlan?
|
|
46
|
+
|
|
38
47
|
@record: untyped
|
|
39
48
|
|
|
40
49
|
@requested_from: untyped
|
|
@@ -43,6 +52,10 @@ module PaperTrailDiff
|
|
|
43
52
|
|
|
44
53
|
@to: untyped
|
|
45
54
|
|
|
55
|
+
@version_scope: untyped
|
|
56
|
+
|
|
57
|
+
@versions: Array[untyped]?
|
|
58
|
+
|
|
46
59
|
# : () -> Array[untyped]
|
|
47
60
|
def empty_versions: () -> Array[untyped]
|
|
48
61
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/traversal_preparer.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Checks that a model can be traversed before any reconstruction starts, and
|
|
5
|
+
# that association tracking is actually available when history is involved.
|
|
6
|
+
class TraversalPreparer
|
|
7
|
+
# : (tree: AssociationTree, traversal: AssociationTraversal) -> void
|
|
8
|
+
def initialize: (tree: AssociationTree, traversal: AssociationTraversal) -> void
|
|
9
|
+
|
|
10
|
+
# : (untyped, historical: bool) -> void
|
|
11
|
+
def call: (untyped, historical: bool) -> void
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
@traversal: AssociationTraversal
|
|
16
|
+
|
|
17
|
+
@tree: AssociationTree
|
|
18
|
+
|
|
19
|
+
# : () -> void
|
|
20
|
+
def ensure_association_tracking!: () -> void
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Selects an inclusive, chronological range from a record's root versions.
|
|
5
5
|
class VersionRange
|
|
6
|
-
# : (untyped, from: untyped, to: untyped) -> void
|
|
7
|
-
def initialize: (untyped, from: untyped, to: untyped) -> void
|
|
6
|
+
# : (untyped, from: untyped, to: untyped, ?version_scope: untyped) -> void
|
|
7
|
+
def initialize: (untyped, from: untyped, to: untyped, ?version_scope: untyped) -> void
|
|
8
|
+
|
|
9
|
+
# Same selection as `select`, but keeping the step pairs a filter implies.
|
|
10
|
+
# : () -> RootVersionPlan
|
|
11
|
+
def select_plan_for_range: () -> RootVersionPlan
|
|
12
|
+
|
|
13
|
+
# : () -> untyped
|
|
14
|
+
def validated_relation: () -> untyped
|
|
8
15
|
|
|
9
16
|
# : () -> Array[untyped]
|
|
10
17
|
def select: () -> Array[untyped]
|
|
@@ -20,12 +27,20 @@ module PaperTrailDiff
|
|
|
20
27
|
|
|
21
28
|
@to: untyped
|
|
22
29
|
|
|
30
|
+
@version_scope: untyped
|
|
31
|
+
|
|
23
32
|
# : () -> untyped
|
|
24
33
|
def versions_relation: () -> untyped
|
|
25
34
|
|
|
26
35
|
# : (untyped, ?through: untyped) -> Array[untyped]
|
|
27
36
|
def select_relation: (untyped, ?through: untyped) -> Array[untyped]
|
|
28
37
|
|
|
38
|
+
# : (untyped, ?through: untyped) -> RootVersionPlan
|
|
39
|
+
def select_plan: (untyped, ?through: untyped) -> RootVersionPlan
|
|
40
|
+
|
|
41
|
+
# : (Array[untyped]) -> Array[untyped]
|
|
42
|
+
def ordered: (Array[untyped]) -> Array[untyped]
|
|
43
|
+
|
|
29
44
|
# : (untyped, untyped, boundary: Symbol) -> void
|
|
30
45
|
def validate_boundary!: (untyped, untyped, boundary: Symbol) -> void
|
|
31
46
|
|