paper_trail_diff 0.5.0 → 0.7.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 +52 -0
- data/README.md +146 -7
- data/lib/paper_trail_diff/activity_root_steps.rb +19 -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 +35 -13
- data/lib/paper_trail_diff/batched_root_analyzer.rb +9 -9
- data/lib/paper_trail_diff/batched_root_versions.rb +62 -29
- data/lib/paper_trail_diff/paper_trail_adapter.rb +72 -25
- data/lib/paper_trail_diff/root_version_plan.rb +99 -0
- data/lib/paper_trail_diff/root_version_selection.rb +155 -0
- data/lib/paper_trail_diff/step.rb +14 -5
- data/lib/paper_trail_diff/time_activity_timeline_builder.rb +99 -29
- data/lib/paper_trail_diff/time_version_range.rb +22 -24
- data/lib/paper_trail_diff/timeline_builder.rb +32 -20
- data/lib/paper_trail_diff/timeline_range.rb +34 -7
- data/lib/paper_trail_diff/timeline_snapshot_provider.rb +20 -4
- 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 +32 -13
- data/sig/generated/paper_trail_diff/activity_root_steps.rbs +7 -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 +15 -6
- data/sig/generated/paper_trail_diff/batched_root_analyzer.rbs +4 -4
- data/sig/generated/paper_trail_diff/batched_root_versions.rbs +28 -14
- data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +26 -10
- data/sig/generated/paper_trail_diff/root_version_plan.rbs +66 -0
- data/sig/generated/paper_trail_diff/root_version_selection.rbs +88 -0
- data/sig/generated/paper_trail_diff/step.rbs +5 -2
- data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +41 -15
- data/sig/generated/paper_trail_diff/time_version_range.rbs +9 -9
- data/sig/generated/paper_trail_diff/timeline_builder.rbs +9 -6
- data/sig/generated/paper_trail_diff/timeline_range.rbs +13 -2
- data/sig/generated/paper_trail_diff/timeline_snapshot_provider.rbs +11 -2
- 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 +8 -8
- metadata +10 -4
|
@@ -5,34 +5,48 @@ module PaperTrailDiff
|
|
|
5
5
|
# queries. Only the range forms that mean the same thing for every root are
|
|
6
6
|
# supported: a shared wall-clock window, or each root's own whole history.
|
|
7
7
|
class BatchedRootVersions
|
|
8
|
-
# : (Array[untyped], time_range: TimeRange?) -> void
|
|
9
|
-
def initialize: (Array[untyped], time_range: TimeRange?) -> void
|
|
8
|
+
# : (Array[untyped], time_range: TimeRange?, ?version_scope: untyped, ?live_endpoints: Hash[Array[String], untyped]?) -> void
|
|
9
|
+
def initialize: (Array[untyped], time_range: TimeRange?, ?version_scope: untyped, ?live_endpoints: Hash[Array[String], untyped]?) -> void
|
|
10
10
|
|
|
11
|
-
# Returns
|
|
12
|
-
# : () -> Hash[Array[String],
|
|
13
|
-
def call: () -> Hash[Array[String],
|
|
11
|
+
# Returns a plan per record identity.
|
|
12
|
+
# : () -> Hash[Array[String], RootVersionPlan]
|
|
13
|
+
def call: () -> Hash[Array[String], RootVersionPlan]
|
|
14
14
|
|
|
15
15
|
private
|
|
16
16
|
|
|
17
|
+
@live_endpoints: Hash[Array[String], untyped]?
|
|
18
|
+
|
|
17
19
|
@records: Array[untyped]
|
|
18
20
|
|
|
19
21
|
@time_range: TimeRange?
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
@version_scope: untyped
|
|
24
|
+
|
|
25
|
+
# : (untyped, Array[untyped], Hash[Array[String], RootVersionPlan]) -> void
|
|
26
|
+
def select_model: (untyped, Array[untyped], Hash[Array[String], RootVersionPlan]) -> void
|
|
27
|
+
|
|
28
|
+
# : (untyped, Array[untyped]) -> [Hash[String, Array[untyped]], Set[untyped]?, Hash[String, untyped]]
|
|
29
|
+
def model_versions: (untyped, Array[untyped]) -> [ Hash[String, Array[untyped]], Set[untyped]?, Hash[String, untyped] ]
|
|
23
30
|
|
|
24
|
-
#
|
|
25
|
-
|
|
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]
|
|
31
|
+
# : (Array[untyped], Set[untyped]?, untyped, untyped) -> RootVersionPlan
|
|
32
|
+
def versions_for: (Array[untyped], Set[untyped]?, untyped, untyped) -> RootVersionPlan
|
|
29
33
|
|
|
30
|
-
#
|
|
31
|
-
|
|
34
|
+
# The batch already loaded every root, so closing on current state reuses
|
|
35
|
+
# that rather than reading each record again.
|
|
36
|
+
# : (Array[String], untyped) -> untyped
|
|
37
|
+
def live_endpoint: (Array[String], untyped) -> untyped
|
|
38
|
+
|
|
39
|
+
# One extra query names the selected mutations without discarding the
|
|
40
|
+
# unfiltered versions the successor lookup still needs.
|
|
41
|
+
# : (untyped, Array[untyped], TimeRange?) -> Set[untyped]?
|
|
42
|
+
def chosen_version_ids: (untyped, Array[untyped], TimeRange?) -> Set[untyped]?
|
|
32
43
|
|
|
33
44
|
# : (untyped, Array[untyped], TimeRange?) -> Hash[String, Array[untyped]]
|
|
34
45
|
def grouped_versions: (untyped, Array[untyped], TimeRange?) -> Hash[String, Array[untyped]]
|
|
35
46
|
|
|
47
|
+
# : (untyped, Array[untyped], TimeRange?) -> untyped
|
|
48
|
+
def range_scope: (untyped, Array[untyped], TimeRange?) -> untyped
|
|
49
|
+
|
|
36
50
|
# One row per root: the earliest version after the window, found without a
|
|
37
51
|
# window function so the query stays portable.
|
|
38
52
|
# : (untyped, Array[untyped], TimeRange) -> Hash[String, untyped]
|
|
@@ -6,6 +6,9 @@ module PaperTrailDiff
|
|
|
6
6
|
# the only place allowed to know about both PaperTrail and the pure engine.
|
|
7
7
|
# Reconstruction logic lives in the collaborators it wires together.
|
|
8
8
|
class PaperTrailAdapter
|
|
9
|
+
# The only thing a wall-clock window can close on besides a later version.
|
|
10
|
+
CLOSE_ON_CURRENT: ::Symbol
|
|
11
|
+
|
|
9
12
|
# : (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool) -> void
|
|
10
13
|
def initialize: (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool) -> void
|
|
11
14
|
|
|
@@ -16,18 +19,18 @@ module PaperTrailDiff
|
|
|
16
19
|
# : (Array[comparison_input]) -> comparison_results
|
|
17
20
|
def compare_many: (Array[comparison_input]) -> comparison_results
|
|
18
21
|
|
|
19
|
-
# : (untyped, from: untyped, to: untyped, within: untyped) -> Array[Step]
|
|
20
|
-
def timeline: (untyped, from: untyped, to: untyped, within: untyped) -> Array[Step]
|
|
22
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?close_on: Symbol?) -> Array[Step]
|
|
23
|
+
def timeline: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?close_on: Symbol?) -> Array[Step]
|
|
21
24
|
|
|
22
|
-
# : (untyped, from: untyped, to: untyped, within: untyped) -> Array[ActivityStep]
|
|
23
|
-
def activity_timeline: (untyped, from: untyped, to: untyped, within: untyped) -> Array[ActivityStep]
|
|
25
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?close_on: Symbol?) -> Array[ActivityStep]
|
|
26
|
+
def activity_timeline: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?close_on: Symbol?) -> Array[ActivityStep]
|
|
24
27
|
|
|
25
|
-
# : (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool) -> Analysis
|
|
26
|
-
def analyze: (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool) -> Analysis
|
|
28
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Analysis
|
|
29
|
+
def analyze: (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Analysis
|
|
27
30
|
|
|
28
31
|
# 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]
|
|
32
|
+
# : (Array[untyped], within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Hash[identity, Analysis]
|
|
33
|
+
def analyze_many: (Array[untyped], within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Hash[identity, Analysis]
|
|
31
34
|
|
|
32
35
|
private
|
|
33
36
|
|
|
@@ -53,6 +56,19 @@ module PaperTrailDiff
|
|
|
53
56
|
|
|
54
57
|
@traversal_preparer: TraversalPreparer
|
|
55
58
|
|
|
59
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, version_scope: untyped, live_endpoint: untyped) -> Analysis
|
|
60
|
+
def analyze_activity: (untyped, from: untyped, to: untyped, within: untyped, version_scope: untyped, live_endpoint: untyped) -> Analysis
|
|
61
|
+
|
|
62
|
+
# `close_on:` names what ends a wall-clock window, so it is meaningless for a
|
|
63
|
+
# range whose endpoints the caller already gave explicitly.
|
|
64
|
+
# : (Symbol?, untyped) -> bool
|
|
65
|
+
def close_on_current?: (Symbol?, untyped) -> bool
|
|
66
|
+
|
|
67
|
+
# A destroyed root has no current state to close on, and its own destroy
|
|
68
|
+
# version already terminates the history.
|
|
69
|
+
# : (untyped, Symbol?, untyped) -> untyped
|
|
70
|
+
def live_endpoint_for: (untyped, Symbol?, untyped) -> untyped
|
|
71
|
+
|
|
56
72
|
# : () -> void
|
|
57
73
|
def build_snapshotters: () -> void
|
|
58
74
|
|
|
@@ -65,8 +81,8 @@ module PaperTrailDiff
|
|
|
65
81
|
# : () -> ActivitySnapshotProvider
|
|
66
82
|
def build_activity_snapshotter: () -> ActivitySnapshotProvider
|
|
67
83
|
|
|
68
|
-
# : (untyped, from: untyped, to: untyped, within: untyped) -> ActivityTimelineBuilder
|
|
69
|
-
def activity_builder: (untyped, from: untyped, to: untyped, within: untyped) -> ActivityTimelineBuilder
|
|
84
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?live_endpoint: untyped) -> ActivityTimelineBuilder
|
|
85
|
+
def activity_builder: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?live_endpoint: untyped) -> ActivityTimelineBuilder
|
|
70
86
|
|
|
71
87
|
# : (untyped) -> void
|
|
72
88
|
def reject_live_habtm_activity!: (untyped) -> void
|
|
@@ -0,0 +1,66 @@
|
|
|
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 live record a window closes on when no later version can reveal its
|
|
27
|
+
# final mutation. It is a boundary, never a version, so it stays out of
|
|
28
|
+
# `versions` and out of anything that reconstructs from version history.
|
|
29
|
+
attr_reader closing_record: untyped
|
|
30
|
+
|
|
31
|
+
# The root versions this plan reports as mutations, which excludes any
|
|
32
|
+
# version present only to reveal what the last of them produced.
|
|
33
|
+
attr_reader mutations: Array[untyped]
|
|
34
|
+
|
|
35
|
+
# Adjacent boundaries, which is what an unfiltered range reports.
|
|
36
|
+
# : (Array[untyped], ?context_version: untyped) -> RootVersionPlan
|
|
37
|
+
def self.contiguous: (Array[untyped], ?context_version: untyped) -> RootVersionPlan
|
|
38
|
+
|
|
39
|
+
# : () -> RootVersionPlan
|
|
40
|
+
def self.empty: () -> RootVersionPlan
|
|
41
|
+
|
|
42
|
+
# : (versions: Array[untyped], steps: Array[[untyped, untyped]], ?context_version: untyped, ?reconstruction_versions: Array[untyped]?, ?mutations: Array[untyped]?, ?closing_record: untyped) -> void
|
|
43
|
+
def initialize: (versions: Array[untyped], steps: Array[[ untyped, untyped ]], ?context_version: untyped, ?reconstruction_versions: Array[untyped]?, ?mutations: Array[untyped]?, ?closing_record: untyped) -> void
|
|
44
|
+
|
|
45
|
+
# : () -> bool
|
|
46
|
+
def empty?: () -> bool
|
|
47
|
+
|
|
48
|
+
# What the range's final state is read from, which is the live record when
|
|
49
|
+
# the window closes on current state and the last version otherwise.
|
|
50
|
+
# : () -> untyped
|
|
51
|
+
def final_endpoint: () -> untyped
|
|
52
|
+
|
|
53
|
+
# : (untyped) -> bool
|
|
54
|
+
def mutation?: (untyped) -> bool
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
@mutation_keys: Set[Array[untyped]]
|
|
59
|
+
|
|
60
|
+
# : () -> Array[untyped]
|
|
61
|
+
def default_mutations: () -> Array[untyped]
|
|
62
|
+
|
|
63
|
+
# : (untyped) -> Array[untyped]
|
|
64
|
+
def key: (untyped) -> Array[untyped]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
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, ?live_endpoint: untyped) -> void
|
|
16
|
+
def initialize: (in_range: Array[untyped], selected: Array[untyped], after_range: untyped, windowed: bool, ?context_required: bool, ?filtered: bool, ?live_endpoint: untyped) -> 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
|
+
@live_endpoint: untyped
|
|
32
|
+
|
|
33
|
+
@selected: Array[untyped]
|
|
34
|
+
|
|
35
|
+
@windowed: bool
|
|
36
|
+
|
|
37
|
+
# A window reaching past the last recorded version has only the live record
|
|
38
|
+
# left to show what its final mutation produced. That record is a closing
|
|
39
|
+
# boundary rather than a selected mutation, so it never joins `versions`.
|
|
40
|
+
# : (untyped) -> RootVersionPlan
|
|
41
|
+
def contiguous_plan: (untyped) -> RootVersionPlan
|
|
42
|
+
|
|
43
|
+
# An activity view still needs a root to reconstruct from even when no root
|
|
44
|
+
# version falls inside the window, because descendants may have moved.
|
|
45
|
+
# : () -> RootVersionPlan
|
|
46
|
+
def without_selection: () -> RootVersionPlan
|
|
47
|
+
|
|
48
|
+
# Each selected mutation is bounded by the version that reveals it, not by
|
|
49
|
+
# the next mutation that happened to be selected. Bounding by the next
|
|
50
|
+
# selection would fold anything filtered out in between into it, so the same
|
|
51
|
+
# edit would read differently depending on what followed it.
|
|
52
|
+
# : (untyped) -> RootVersionPlan
|
|
53
|
+
def filtered_plan: (untyped) -> RootVersionPlan
|
|
54
|
+
|
|
55
|
+
# : (Array[[untyped, untyped]], untyped) -> RootVersionPlan
|
|
56
|
+
def filtered_plan_for: (Array[[ untyped, untyped ]], untyped) -> RootVersionPlan
|
|
57
|
+
|
|
58
|
+
# Nothing can follow a destroy, so it never pairs into a step. It is still a
|
|
59
|
+
# selected mutation, and the activity view closes on the absence it leaves,
|
|
60
|
+
# so it has to stay a boundary or a filtered report loses the deletion
|
|
61
|
+
# entirely — the one event it can least afford to drop.
|
|
62
|
+
# : () -> Array[untyped]
|
|
63
|
+
def closing_versions: () -> Array[untyped]
|
|
64
|
+
|
|
65
|
+
# Everything the span passes through, filtered out or not. A replay that
|
|
66
|
+
# skipped the excluded versions would carry a stale state into the next
|
|
67
|
+
# reported step, so the two selected mutations either side of a gap would
|
|
68
|
+
# disagree about what the record looked like between them.
|
|
69
|
+
# : (Array[untyped]) -> Array[untyped]
|
|
70
|
+
def spanned: (Array[untyped]) -> Array[untyped]
|
|
71
|
+
|
|
72
|
+
# : (Array[untyped]) -> Array[untyped]
|
|
73
|
+
def chronological: (Array[untyped]) -> Array[untyped]
|
|
74
|
+
|
|
75
|
+
# : (untyped) -> untyped
|
|
76
|
+
def immediate_successor: (untyped) -> untyped
|
|
77
|
+
|
|
78
|
+
# A version left out by a filter is still the state the last selected change
|
|
79
|
+
# produced, so it is preferred over anything after the range.
|
|
80
|
+
# : () -> untyped
|
|
81
|
+
def revealing_version: () -> untyped
|
|
82
|
+
|
|
83
|
+
# A range closing on the record's own destruction needs no later version:
|
|
84
|
+
# nothing can follow it, so demanding one would reject the range forever.
|
|
85
|
+
# : () -> bool
|
|
86
|
+
def terminal_destroy?: () -> bool
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -13,8 +13,11 @@ module PaperTrailDiff
|
|
|
13
13
|
|
|
14
14
|
attr_reader diff: Diff
|
|
15
15
|
|
|
16
|
-
#
|
|
17
|
-
|
|
16
|
+
# `to_version` is nil for the one step that closes on current state, which a
|
|
17
|
+
# window reaching past the last recorded version has to do. Read
|
|
18
|
+
# `to_boundary` instead when a caller may have opted into that.
|
|
19
|
+
# : (from_version: untyped, to_version: untyped, diff: Diff, ?captured_at: untyped) -> void
|
|
20
|
+
def initialize: (from_version: untyped, to_version: untyped, diff: Diff, ?captured_at: untyped) -> void
|
|
18
21
|
|
|
19
22
|
# : () -> bool
|
|
20
23
|
def empty?: () -> bool
|
|
@@ -22,8 +22,19 @@ module PaperTrailDiff
|
|
|
22
22
|
|
|
23
23
|
@tree: AssociationTree
|
|
24
24
|
|
|
25
|
-
# : () -> [ActivityHistory,
|
|
26
|
-
def history_and_versions: () -> [ ActivityHistory,
|
|
25
|
+
# : () -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped]
|
|
26
|
+
def history_and_versions: () -> [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
|
|
27
|
+
|
|
28
|
+
# : (RootVersionPlan, untyped) -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped]
|
|
29
|
+
def history_for: (RootVersionPlan, untyped) -> [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
|
|
30
|
+
|
|
31
|
+
# One live read serves both the closing activity step and the endpoint the
|
|
32
|
+
# analysis reports, which would otherwise reconstruct the graph twice.
|
|
33
|
+
# : (RootVersionPlan) -> RecordSnapshot?
|
|
34
|
+
def current_snapshot: (RootVersionPlan) -> RecordSnapshot?
|
|
35
|
+
|
|
36
|
+
# : (Array[untyped], untyped) -> Array[ActivityEvent]
|
|
37
|
+
def events_through: (Array[untyped], untyped) -> Array[ActivityEvent]
|
|
27
38
|
|
|
28
39
|
# : (Array[untyped], Array[ActivityEvent]) -> ActivityHistory
|
|
29
40
|
def build_history: (Array[untyped], Array[ActivityEvent]) -> ActivityHistory
|
|
@@ -31,25 +42,40 @@ module PaperTrailDiff
|
|
|
31
42
|
# : (ActivityHistory, ActivityStep?) -> Array[ActivityStep]
|
|
32
43
|
def activity_steps: (ActivityHistory, ActivityStep?) -> Array[ActivityStep]
|
|
33
44
|
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# : (
|
|
40
|
-
def
|
|
45
|
+
# Root versions inside the window that the plan does not report are context
|
|
46
|
+
# rather than mutations: the version appended to reveal the last selected
|
|
47
|
+
# change, and anything a filter excluded but the replay still walks. Neither
|
|
48
|
+
# may be counted as a selected mutation. Descendant events are not filtered,
|
|
49
|
+
# so window membership is the whole test for them.
|
|
50
|
+
# : (Array[ActivityEvent], RootVersionPlan) -> Array[ActivityEvent]
|
|
51
|
+
def selected_events: (Array[ActivityEvent], RootVersionPlan) -> Array[ActivityEvent]
|
|
52
|
+
|
|
53
|
+
# A window either closes on the absence its final destruction leaves, or on
|
|
54
|
+
# current state when it reaches past everything recorded. Both are boundaries
|
|
55
|
+
# no version can supply.
|
|
56
|
+
# : (ActivityHistory, ActivityEvent?, RootVersionPlan, untyped, RecordSnapshot?) -> ActivityStep?
|
|
57
|
+
def closing_step: (ActivityHistory, ActivityEvent?, RootVersionPlan, untyped, RecordSnapshot?) -> ActivityStep?
|
|
58
|
+
|
|
59
|
+
# : (ActivityHistory, ActivityEvent) -> ActivityStep
|
|
60
|
+
def destroyed_step: (ActivityHistory, ActivityEvent) -> ActivityStep
|
|
61
|
+
|
|
62
|
+
# The last boundary reached is where current state is compared from, which
|
|
63
|
+
# is the final event rather than the final root version once descendants
|
|
64
|
+
# have moved since it.
|
|
65
|
+
# : (ActivityHistory, untyped, untyped, RecordSnapshot?) -> ActivityStep?
|
|
66
|
+
def current_step: (ActivityHistory, untyped, untyped, RecordSnapshot?) -> ActivityStep?
|
|
41
67
|
|
|
42
68
|
# : (ActivityEvent?) -> bool
|
|
43
69
|
def terminal_destroy?: (ActivityEvent?) -> bool
|
|
44
70
|
|
|
45
|
-
# : (Array[untyped]) -> void
|
|
46
|
-
def prepare_history: (Array[untyped]) -> void
|
|
71
|
+
# : (Array[untyped], ?end_at: untyped) -> void
|
|
72
|
+
def prepare_history: (Array[untyped], ?end_at: untyped) -> void
|
|
47
73
|
|
|
48
|
-
# : (Array[untyped]) -> Array[ActivityEvent]
|
|
49
|
-
def collect_events: (Array[untyped]) -> Array[ActivityEvent]
|
|
74
|
+
# : (Array[untyped], ?range_end: untyped) -> Array[ActivityEvent]
|
|
75
|
+
def collect_events: (Array[untyped], ?range_end: untyped) -> Array[ActivityEvent]
|
|
50
76
|
|
|
51
|
-
# : (Array[ActivityEvent], Array[ActivityEvent]) -> bool
|
|
52
|
-
def time_events?: (Array[ActivityEvent], Array[ActivityEvent]) -> bool
|
|
77
|
+
# : (Array[ActivityEvent], Array[ActivityEvent], RootVersionPlan) -> bool
|
|
78
|
+
def time_events?: (Array[ActivityEvent], Array[ActivityEvent], RootVersionPlan) -> bool
|
|
53
79
|
|
|
54
80
|
# : (ActivityEvent, Array[ActivityEvent]) -> bool
|
|
55
81
|
def later_event?: (ActivityEvent, Array[ActivityEvent]) -> bool
|
|
@@ -3,28 +3,28 @@
|
|
|
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, ?live_endpoint: untyped) -> void
|
|
7
|
+
def initialize: (untyped, time_range: TimeRange, ?version_scope: untyped, ?live_endpoint: 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
|
|
|
17
|
+
@live_endpoint: untyped
|
|
18
|
+
|
|
14
19
|
@record: untyped
|
|
15
20
|
|
|
16
21
|
@time_range: TimeRange
|
|
17
22
|
|
|
23
|
+
@version_scope: untyped
|
|
24
|
+
|
|
18
25
|
# : () -> untyped
|
|
19
26
|
def versions_relation: () -> untyped
|
|
20
27
|
|
|
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
28
|
# : (untyped) -> untyped
|
|
29
29
|
def trailing_version: (untyped) -> untyped
|
|
30
30
|
|
|
@@ -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, ?versions: Array[untyped]?) -> void
|
|
7
|
-
def initialize: (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?versions: Array[untyped]?) -> void
|
|
6
|
+
# : (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> void
|
|
7
|
+
def initialize: (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> void
|
|
8
8
|
|
|
9
9
|
# : () -> Array[Step]
|
|
10
10
|
def build: () -> Array[Step]
|
|
@@ -20,13 +20,16 @@ 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
|
+
|
|
26
|
+
# : (RootVersionPlan) -> Array[Step]
|
|
27
|
+
def build_steps: (RootVersionPlan) -> Array[Step]
|
|
25
28
|
|
|
26
29
|
# : () -> [Array[Step], nil, nil]
|
|
27
30
|
def empty_history: () -> [ Array[Step], nil, nil ]
|
|
28
31
|
|
|
29
|
-
# : () ->
|
|
30
|
-
def selected_versions: () ->
|
|
32
|
+
# : () -> RootVersionPlan
|
|
33
|
+
def selected_versions: () -> RootVersionPlan
|
|
31
34
|
end
|
|
32
35
|
end
|
|
@@ -11,8 +11,8 @@ module PaperTrailDiff
|
|
|
11
11
|
|
|
12
12
|
attr_reader time_range: TimeRange?
|
|
13
13
|
|
|
14
|
-
# : (untyped, from: untyped, to: untyped, within: untyped, ?versions: Array[untyped]?) -> void
|
|
15
|
-
def initialize: (untyped, from: untyped, to: untyped, within: untyped, ?versions: Array[untyped]?) -> void
|
|
14
|
+
# : (untyped, from: untyped, to: untyped, within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> void
|
|
15
|
+
def initialize: (untyped, from: untyped, to: untyped, within: untyped, ?versions: Array[untyped]?, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> 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.
|
|
@@ -21,6 +21,11 @@ module PaperTrailDiff
|
|
|
21
21
|
|
|
22
22
|
# A batch may have selected these versions already, in which case reselecting
|
|
23
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
|
+
|
|
24
29
|
# : (?context_required: bool) -> Array[untyped]
|
|
25
30
|
def select: (?context_required: bool) -> Array[untyped]
|
|
26
31
|
|
|
@@ -37,6 +42,10 @@ module PaperTrailDiff
|
|
|
37
42
|
|
|
38
43
|
@from: untyped
|
|
39
44
|
|
|
45
|
+
@live_endpoint: untyped
|
|
46
|
+
|
|
47
|
+
@plan: RootVersionPlan?
|
|
48
|
+
|
|
40
49
|
@record: untyped
|
|
41
50
|
|
|
42
51
|
@requested_from: untyped
|
|
@@ -45,6 +54,8 @@ module PaperTrailDiff
|
|
|
45
54
|
|
|
46
55
|
@to: untyped
|
|
47
56
|
|
|
57
|
+
@version_scope: untyped
|
|
58
|
+
|
|
48
59
|
@versions: Array[untyped]?
|
|
49
60
|
|
|
50
61
|
# : () -> Array[untyped]
|
|
@@ -3,15 +3,24 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Gives TimelineBuilder one-argument access to a range-prepared snapshot store.
|
|
5
5
|
class TimelineSnapshotProvider
|
|
6
|
-
# : (HistoricalSnapshotStore) -> void
|
|
7
|
-
def initialize: (HistoricalSnapshotStore) -> void
|
|
6
|
+
# : (HistoricalSnapshotStore, ?live_snapshotter: untyped) -> void
|
|
7
|
+
def initialize: (HistoricalSnapshotStore, ?live_snapshotter: untyped) -> void
|
|
8
8
|
|
|
9
9
|
# : (untyped, Array[untyped]) -> void
|
|
10
10
|
def prepare: (untyped, Array[untyped]) -> void
|
|
11
11
|
|
|
12
|
+
# A window closing on current state ends at the live record rather than at a
|
|
13
|
+
# version, which is the one endpoint no version history can reconstruct.
|
|
12
14
|
# : (untyped) -> RecordSnapshot?
|
|
13
15
|
def call: (untyped) -> RecordSnapshot?
|
|
14
16
|
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# : (untyped) -> RecordSnapshot?
|
|
20
|
+
def live_snapshot: (untyped) -> RecordSnapshot?
|
|
21
|
+
|
|
22
|
+
@live_snapshotter: untyped
|
|
23
|
+
|
|
15
24
|
@store: HistoricalSnapshotStore
|
|
16
25
|
end
|
|
17
26
|
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
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/version_scope_filter.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Applies a caller's version filter to a range, naming which of its versions
|
|
5
|
+
# count as selected mutations. The unfiltered versions stay available to the
|
|
6
|
+
# caller, because the one that reveals the last selected mutation is drawn
|
|
7
|
+
# from them.
|
|
8
|
+
class VersionScopeFilter
|
|
9
|
+
# : (untyped) -> void
|
|
10
|
+
def initialize: (untyped) -> void
|
|
11
|
+
|
|
12
|
+
# : (untyped, Array[untyped]) -> Array[untyped]
|
|
13
|
+
def call: (untyped, Array[untyped]) -> Array[untyped]
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
@scope: untyped
|
|
18
|
+
|
|
19
|
+
# : (untyped) -> untyped
|
|
20
|
+
def validated: (untyped) -> untyped
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -17,24 +17,24 @@ module PaperTrailDiff
|
|
|
17
17
|
def self.compare_many: (Array[comparison_input], ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> comparison_results
|
|
18
18
|
|
|
19
19
|
# Compares every adjacent reconstructed state in an inclusive version range.
|
|
20
|
-
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option) -> Array[Step]
|
|
21
|
-
def self.timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option) -> Array[Step]
|
|
20
|
+
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?version_scope: untyped, ?close_on: Symbol?) -> Array[Step]
|
|
21
|
+
def self.timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?version_scope: untyped, ?close_on: Symbol?) -> Array[Step]
|
|
22
22
|
|
|
23
23
|
# Compares adjacent root and selected-descendant activity boundaries.
|
|
24
24
|
# `reload_live_endpoints:` applies only when `to:` is a current record; the
|
|
25
25
|
# other range forms never read live state.
|
|
26
|
-
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> Array[ActivityStep]
|
|
27
|
-
def self.activity_timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> Array[ActivityStep]
|
|
26
|
+
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Array[ActivityStep]
|
|
27
|
+
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?) -> Array[ActivityStep]
|
|
28
28
|
|
|
29
29
|
# Builds an endpoint diff and root-checkpoint timeline while normalizing each version once.
|
|
30
|
-
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool) -> Analysis
|
|
31
|
-
def self.analyze: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool) -> Analysis
|
|
30
|
+
# : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Analysis
|
|
31
|
+
def self.analyze: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Analysis
|
|
32
32
|
|
|
33
33
|
# Analyzes many roots over one shared time window, preparing their selected
|
|
34
34
|
# history once for the batch instead of once per record. Roots with no
|
|
35
35
|
# versions in the window return an empty `Analysis`.
|
|
36
|
-
# : (Array[untyped], ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool) -> Hash[identity, Analysis]
|
|
37
|
-
def self.analyze_many: (Array[untyped], ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool) -> Hash[identity, Analysis]
|
|
36
|
+
# : (Array[untyped], ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Hash[identity, Analysis]
|
|
37
|
+
def self.analyze_many: (Array[untyped], ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> Hash[identity, Analysis]
|
|
38
38
|
|
|
39
39
|
# Returns the association macros this release can normalize.
|
|
40
40
|
# : () -> Array[Symbol]
|