paper_trail_diff 0.11.0 → 0.13.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 +144 -0
- data/QUICKSTART.md +149 -79
- data/README.md +179 -1367
- data/docs/architecture.md +88 -0
- data/docs/performance.md +309 -0
- data/docs/reference.md +977 -0
- data/docs/testing.md +146 -0
- data/lib/paper_trail_diff/activity_belongs_to_event_applier.rb +2 -12
- data/lib/paper_trail_diff/activity_boundary.rb +20 -8
- data/lib/paper_trail_diff/activity_child_candidate_loader.rb +29 -0
- data/lib/paper_trail_diff/activity_collection_event_applier.rb +10 -7
- data/lib/paper_trail_diff/activity_collection_route_updater.rb +1 -11
- data/lib/paper_trail_diff/activity_event_record_normalizer.rb +4 -0
- data/lib/paper_trail_diff/activity_event_record_resolver.rb +66 -6
- data/lib/paper_trail_diff/activity_event_snapshot_refresher.rb +6 -4
- data/lib/paper_trail_diff/activity_grouping.rb +12 -0
- data/lib/paper_trail_diff/activity_history.rb +9 -3
- data/lib/paper_trail_diff/activity_relationship.rb +2 -0
- data/lib/paper_trail_diff/activity_root_snapshot_refresher.rb +1 -0
- data/lib/paper_trail_diff/activity_snapshot_sequence.rb +9 -2
- data/lib/paper_trail_diff/activity_timeline_builder.rb +39 -35
- data/lib/paper_trail_diff/activity_version_collector.rb +27 -18
- data/lib/paper_trail_diff/analysis.rb +9 -4
- data/lib/paper_trail_diff/analysis_batch.rb +28 -7
- data/lib/paper_trail_diff/association_traversal.rb +23 -0
- data/lib/paper_trail_diff/batch_boundary_resolver.rb +18 -26
- data/lib/paper_trail_diff/batch_safety.rb +55 -0
- data/lib/paper_trail_diff/batched_root_analyzer.rb +14 -5
- data/lib/paper_trail_diff/batched_root_versions.rb +9 -37
- data/lib/paper_trail_diff/branch_snapshot_refresher.rb +7 -8
- data/lib/paper_trail_diff/deleted_analysis_batch.rb +93 -0
- data/lib/paper_trail_diff/direct_record_state_loader.rb +81 -0
- data/lib/paper_trail_diff/endpoint.rb +10 -0
- data/lib/paper_trail_diff/engine.rb +4 -2
- data/lib/paper_trail_diff/errors.rb +9 -0
- data/lib/paper_trail_diff/historical_association_reifier.rb +26 -1
- data/lib/paper_trail_diff/historical_filter.rb +181 -0
- data/lib/paper_trail_diff/historical_snapshot_store.rb +51 -35
- data/lib/paper_trail_diff/instrumentation.rb +2 -2
- data/lib/paper_trail_diff/nested_comparator.rb +59 -24
- data/lib/paper_trail_diff/paper_trail_adapter.rb +109 -55
- data/lib/paper_trail_diff/prepared_activity_index.rb +121 -0
- data/lib/paper_trail_diff/prepared_edge_loader.rb +11 -11
- data/lib/paper_trail_diff/prepared_history.rb +21 -30
- data/lib/paper_trail_diff/prepared_history_loader.rb +14 -2
- data/lib/paper_trail_diff/prepared_record_index.rb +102 -29
- data/lib/paper_trail_diff/prepared_snapshot_builder.rb +64 -0
- data/lib/paper_trail_diff/prepared_state_association_reader.rb +77 -0
- data/lib/paper_trail_diff/root_version_plan.rb +25 -2
- data/lib/paper_trail_diff/root_version_selection.rb +16 -5
- data/lib/paper_trail_diff/scoped_analysis.rb +17 -2
- data/lib/paper_trail_diff/scoped_analysis_builder.rb +47 -0
- data/lib/paper_trail_diff/scoped_root_selection.rb +106 -13
- data/lib/paper_trail_diff/snapshot.rb +9 -0
- data/lib/paper_trail_diff/snapshot_normalizer.rb +41 -2
- data/lib/paper_trail_diff/step.rb +10 -5
- data/lib/paper_trail_diff/stored_time_precision.rb +68 -0
- data/lib/paper_trail_diff/support.rb +13 -3
- data/lib/paper_trail_diff/time_activity_timeline_builder.rb +19 -7
- data/lib/paper_trail_diff/time_range.rb +22 -2
- data/lib/paper_trail_diff/time_version_range.rb +1 -15
- data/lib/paper_trail_diff/timeline_builder.rb +5 -4
- data/lib/paper_trail_diff/timeline_range.rb +25 -43
- data/lib/paper_trail_diff/timeline_snapshot_provider.rb +13 -2
- data/lib/paper_trail_diff/traversal.rb +1 -1
- data/lib/paper_trail_diff/value_objects.rb +66 -0
- data/lib/paper_trail_diff/version.rb +1 -1
- data/lib/paper_trail_diff/version_association_candidate_scope.rb +1 -1
- data/lib/paper_trail_diff/version_boundary.rb +48 -0
- data/lib/paper_trail_diff/version_query.rb +33 -0
- data/lib/paper_trail_diff/version_range.rb +10 -38
- data/lib/paper_trail_diff.rb +81 -43
- data/sig/generated/paper_trail_diff/activity_belongs_to_event_applier.rbs +0 -3
- data/sig/generated/paper_trail_diff/activity_boundary.rbs +9 -4
- data/sig/generated/paper_trail_diff/activity_child_candidate_loader.rbs +11 -0
- data/sig/generated/paper_trail_diff/activity_collection_event_applier.rbs +4 -4
- data/sig/generated/paper_trail_diff/activity_collection_route_updater.rbs +0 -3
- data/sig/generated/paper_trail_diff/activity_event_record_resolver.rbs +19 -3
- data/sig/generated/paper_trail_diff/activity_event_snapshot_refresher.rbs +2 -2
- data/sig/generated/paper_trail_diff/activity_grouping.rbs +3 -0
- data/sig/generated/paper_trail_diff/activity_history.rbs +6 -2
- data/sig/generated/paper_trail_diff/activity_snapshot_sequence.rbs +7 -2
- data/sig/generated/paper_trail_diff/activity_timeline_builder.rbs +8 -3
- data/sig/generated/paper_trail_diff/activity_version_collector.rbs +4 -8
- data/sig/generated/paper_trail_diff/analysis.rbs +2 -2
- data/sig/generated/paper_trail_diff/analysis_batch.rbs +14 -3
- data/sig/generated/paper_trail_diff/association_traversal.rbs +11 -0
- data/sig/generated/paper_trail_diff/batch_boundary_resolver.rbs +7 -10
- data/sig/generated/paper_trail_diff/batch_safety.rbs +24 -0
- data/sig/generated/paper_trail_diff/batched_root_analyzer.rbs +8 -2
- data/sig/generated/paper_trail_diff/batched_root_versions.rbs +4 -16
- data/sig/generated/paper_trail_diff/branch_snapshot_refresher.rbs +2 -2
- data/sig/generated/paper_trail_diff/deleted_analysis_batch.rbs +44 -0
- data/sig/generated/paper_trail_diff/direct_record_state_loader.rbs +42 -0
- data/sig/generated/paper_trail_diff/endpoint.rbs +5 -0
- data/sig/generated/paper_trail_diff/engine.rbs +4 -2
- data/sig/generated/paper_trail_diff/errors.rbs +12 -0
- data/sig/generated/paper_trail_diff/historical_association_reifier.rbs +8 -0
- data/sig/generated/paper_trail_diff/historical_filter.rbs +75 -0
- data/sig/generated/paper_trail_diff/historical_snapshot_store.rbs +17 -4
- data/sig/generated/paper_trail_diff/nested_comparator.rbs +31 -18
- data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +36 -15
- data/sig/generated/paper_trail_diff/prepared_activity_index.rbs +56 -0
- data/sig/generated/paper_trail_diff/prepared_edge_loader.rbs +3 -3
- data/sig/generated/paper_trail_diff/prepared_history.rbs +12 -14
- data/sig/generated/paper_trail_diff/prepared_history_loader.rbs +2 -0
- data/sig/generated/paper_trail_diff/prepared_record_index.rbs +43 -6
- data/sig/generated/paper_trail_diff/prepared_snapshot_builder.rbs +24 -0
- data/sig/generated/paper_trail_diff/prepared_state_association_reader.rbs +33 -0
- data/sig/generated/paper_trail_diff/root_version_plan.rbs +15 -2
- data/sig/generated/paper_trail_diff/root_version_selection.rbs +8 -1
- data/sig/generated/paper_trail_diff/scoped_analysis.rbs +14 -2
- data/sig/generated/paper_trail_diff/scoped_analysis_builder.rbs +22 -0
- data/sig/generated/paper_trail_diff/scoped_root_selection.rbs +46 -6
- data/sig/generated/paper_trail_diff/snapshot.rbs +4 -0
- data/sig/generated/paper_trail_diff/snapshot_normalizer.rbs +13 -0
- data/sig/generated/paper_trail_diff/step.rbs +8 -2
- data/sig/generated/paper_trail_diff/stored_time_precision.rbs +36 -0
- data/sig/generated/paper_trail_diff/support.rbs +7 -0
- data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +7 -2
- data/sig/generated/paper_trail_diff/time_range.rbs +9 -0
- data/sig/generated/paper_trail_diff/time_version_range.rbs +0 -6
- data/sig/generated/paper_trail_diff/timeline_builder.rbs +2 -2
- data/sig/generated/paper_trail_diff/timeline_range.rbs +10 -14
- data/sig/generated/paper_trail_diff/timeline_snapshot_provider.rbs +2 -0
- data/sig/generated/paper_trail_diff/value_objects.rbs +45 -0
- data/sig/generated/paper_trail_diff/version_boundary.rbs +23 -0
- data/sig/generated/paper_trail_diff/version_query.rbs +15 -0
- data/sig/generated/paper_trail_diff/version_range.rbs +5 -14
- data/sig/generated/paper_trail_diff.rbs +37 -27
- metadata +31 -5
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
module PaperTrailDiff
|
|
4
4
|
# Batched record states and relationship candidates for one activity range.
|
|
5
5
|
class PreparedHistory
|
|
6
|
+
attr_reader activity_index: PreparedActivityIndex?
|
|
7
|
+
|
|
6
8
|
DIRECT_RESOLVERS: untyped
|
|
7
9
|
|
|
8
|
-
# : (PreparedRecordIndex) -> void
|
|
9
|
-
def initialize: (PreparedRecordIndex) -> void
|
|
10
|
+
# : (PreparedRecordIndex, ?activity_index: PreparedActivityIndex?) -> void
|
|
11
|
+
def initialize: (PreparedRecordIndex, ?activity_index: PreparedActivityIndex?) -> void
|
|
10
12
|
|
|
11
13
|
# : (untyped, untyped, Hash[String, Hash[Symbol, untyped]]) -> void
|
|
12
14
|
def merge_edge: (untyped, untyped, Hash[String, Hash[Symbol, untyped]]) -> void
|
|
@@ -23,6 +25,12 @@ module PaperTrailDiff
|
|
|
23
25
|
# : (untyped, untyped, untyped) -> [Hash[untyped, untyped], Hash[untyped, untyped]]?
|
|
24
26
|
def record_transition: (untyped, untyped, untyped) -> [ Hash[untyped, untyped], Hash[untyped, untyped] ]?
|
|
25
27
|
|
|
28
|
+
# : (untyped, untyped, untyped, ?direct: bool) -> [bool, untyped]
|
|
29
|
+
def record_after: (untyped, untyped, untyped, ?direct: bool) -> [ bool, untyped ]
|
|
30
|
+
|
|
31
|
+
# : (PreparedRecordState, untyped, untyped) -> [bool, Array[PreparedRecordState]]
|
|
32
|
+
def snapshot_states: (PreparedRecordState, untyped, untyped) -> [ bool, Array[PreparedRecordState] ]
|
|
33
|
+
|
|
26
34
|
# : (untyped, untyped, untyped, habtm_boundary: untyped) -> [bool, Array[untyped]]
|
|
27
35
|
def resolve: (untyped, untyped, untyped, habtm_boundary: untyped) -> [ bool, Array[untyped] ]
|
|
28
36
|
|
|
@@ -34,14 +42,13 @@ module PaperTrailDiff
|
|
|
34
42
|
|
|
35
43
|
@records: PreparedRecordIndex
|
|
36
44
|
|
|
45
|
+
@state_reader: PreparedStateAssociationReader
|
|
46
|
+
|
|
37
47
|
@unsupported: Hash[Array[String], bool]
|
|
38
48
|
|
|
39
49
|
# : (untyped, untyped) -> bool
|
|
40
50
|
def supported?: (untyped, untyped) -> bool
|
|
41
51
|
|
|
42
|
-
# : (untyped) -> bool
|
|
43
|
-
def default_scoped?: (untyped) -> bool
|
|
44
|
-
|
|
45
52
|
# : (untyped, untyped, untyped, untyped) -> Array[untyped]?
|
|
46
53
|
def direct_records: (untyped, untyped, untyped, untyped) -> Array[untyped]?
|
|
47
54
|
|
|
@@ -51,9 +58,6 @@ module PaperTrailDiff
|
|
|
51
58
|
# : (untyped, untyped, untyped, untyped) -> Array[untyped]
|
|
52
59
|
def resolve_children: (untyped, untyped, untyped, untyped) -> Array[untyped]
|
|
53
60
|
|
|
54
|
-
# : (untyped, untyped, untyped, untyped) -> Array[untyped]?
|
|
55
|
-
def resolve_habtm: (untyped, untyped, untyped, untyped) -> Array[untyped]?
|
|
56
|
-
|
|
57
61
|
# : (untyped, untyped, untyped) -> Array[untyped]?
|
|
58
62
|
def belongs_to_records: (untyped, untyped, untyped) -> Array[untyped]?
|
|
59
63
|
|
|
@@ -78,9 +82,6 @@ module PaperTrailDiff
|
|
|
78
82
|
# : (untyped) -> String?
|
|
79
83
|
def scalar_foreign_key: (untyped) -> String?
|
|
80
84
|
|
|
81
|
-
# : (untyped, untyped) -> untyped
|
|
82
|
-
def belongs_to_class: (untyped, untyped) -> untyped
|
|
83
|
-
|
|
84
85
|
# : (untyped, untyped) -> Array[String]
|
|
85
86
|
def owner_types: (untyped, untyped) -> Array[String]
|
|
86
87
|
|
|
@@ -92,8 +93,5 @@ module PaperTrailDiff
|
|
|
92
93
|
|
|
93
94
|
# : (untyped, untyped) -> Array[String]
|
|
94
95
|
def edge_key: (untyped, untyped) -> Array[String]
|
|
95
|
-
|
|
96
|
-
# : (Hash[String, Hash[Symbol, untyped]], Hash[String, Hash[Symbol, untyped]]) -> Hash[String, Hash[Symbol, untyped]]
|
|
97
|
-
def merge_groups: (Hash[String, Hash[Symbol, untyped]], Hash[String, Hash[Symbol, untyped]]) -> Hash[String, Hash[Symbol, untyped]]
|
|
98
96
|
end
|
|
99
97
|
end
|
|
@@ -23,6 +23,12 @@ module PaperTrailDiff
|
|
|
23
23
|
|
|
24
24
|
# : (untyped, Hash[untyped, untyped]) -> void
|
|
25
25
|
def initialize_attributes: (untyped, Hash[untyped, untyped]) -> void
|
|
26
|
+
|
|
27
|
+
# A live fallback and a version decoded from history must agree on what
|
|
28
|
+
# the stored history could represent, or the gap between them reads as a
|
|
29
|
+
# change.
|
|
30
|
+
# : (untyped, Hash[untyped, untyped]) -> Hash[untyped, untyped]
|
|
31
|
+
def stored_attributes: (untyped, Hash[untyped, untyped]) -> Hash[untyped, untyped]
|
|
26
32
|
end
|
|
27
33
|
|
|
28
34
|
# Extracts a version's scalar state without constructing a disposable AR object.
|
|
@@ -62,9 +68,19 @@ module PaperTrailDiff
|
|
|
62
68
|
# : (untyped) -> untyped
|
|
63
69
|
def record_before: (untyped) -> untyped
|
|
64
70
|
|
|
71
|
+
# : (untyped) -> PreparedRecordState?
|
|
72
|
+
def state_before: (untyped) -> PreparedRecordState?
|
|
73
|
+
|
|
74
|
+
# A handled nil is known absence, not permission to query the current row.
|
|
75
|
+
# : (untyped, ?direct: bool) -> [bool, untyped]
|
|
76
|
+
def record_after: (untyped, ?direct: bool) -> [ bool, untyped ]
|
|
77
|
+
|
|
65
78
|
# : () -> Array[untyped]
|
|
66
79
|
def records: () -> Array[untyped]
|
|
67
80
|
|
|
81
|
+
# : () -> Array[PreparedRecordState]
|
|
82
|
+
def states: () -> Array[PreparedRecordState]
|
|
83
|
+
|
|
68
84
|
# : (untyped) -> [Hash[untyped, untyped], Hash[untyped, untyped]]?
|
|
69
85
|
def transition: (untyped) -> [ Hash[untyped, untyped], Hash[untyped, untyped] ]?
|
|
70
86
|
|
|
@@ -87,6 +103,11 @@ module PaperTrailDiff
|
|
|
87
103
|
# boundary's transaction. The first is monotonic over the chronological
|
|
88
104
|
# list and the second is indexed, so neither rescans a long history once
|
|
89
105
|
# per boundary.
|
|
106
|
+
#
|
|
107
|
+
# "At or after" is decided by the version order rather than the timestamp
|
|
108
|
+
# alone. A version written in the same instant as the boundary but before
|
|
109
|
+
# it already took effect there; reading its pre-change state would put the
|
|
110
|
+
# boundary on the wrong side of a change it follows.
|
|
90
111
|
# : (untyped) -> Integer?
|
|
91
112
|
def boundary_index: (untyped) -> Integer?
|
|
92
113
|
|
|
@@ -108,20 +129,42 @@ module PaperTrailDiff
|
|
|
108
129
|
# : (untyped, Array[untyped]) -> void
|
|
109
130
|
def load: (untyped, Array[untyped]) -> void
|
|
110
131
|
|
|
132
|
+
# : (untyped, Array[untyped], Array[untyped]) -> void
|
|
133
|
+
def seed: (untyped, Array[untyped], Array[untyped]) -> void
|
|
134
|
+
|
|
111
135
|
# : (untyped, untyped, untyped) -> untyped
|
|
112
136
|
def record_before: (untyped, untyped, untyped) -> untyped
|
|
113
137
|
|
|
138
|
+
# : (untyped, untyped, untyped) -> [bool, PreparedRecordState?]
|
|
139
|
+
def snapshot_state_before: (untyped, untyped, untyped) -> [ bool, PreparedRecordState? ]
|
|
140
|
+
|
|
114
141
|
# : (untyped, untyped, untyped) -> [Hash[untyped, untyped], Hash[untyped, untyped]]?
|
|
115
142
|
def transition: (untyped, untyped, untyped) -> [ Hash[untyped, untyped], Hash[untyped, untyped] ]?
|
|
116
143
|
|
|
144
|
+
# : (untyped, untyped, untyped, ?direct: bool) -> [bool, untyped]
|
|
145
|
+
def record_after: (untyped, untyped, untyped, ?direct: bool) -> [ bool, untyped ]
|
|
146
|
+
|
|
117
147
|
# : (untyped, Array[untyped]) -> Array[untyped]
|
|
118
148
|
def records_for: (untyped, Array[untyped]) -> Array[untyped]
|
|
119
149
|
|
|
150
|
+
# : (untyped, Array[untyped], String) -> Array[untyped]
|
|
151
|
+
def attribute_values_for: (untyped, Array[untyped], String) -> Array[untyped]
|
|
152
|
+
|
|
153
|
+
# : (untyped, untyped) -> Array[untyped]
|
|
154
|
+
def versions_for_id: (untyped, untyped) -> Array[untyped]
|
|
155
|
+
|
|
120
156
|
# : (untyped, untyped) -> bool
|
|
121
157
|
def loaded?: (untyped, untyped) -> bool
|
|
122
158
|
|
|
123
159
|
private
|
|
124
160
|
|
|
161
|
+
# : (untyped, String) -> bool
|
|
162
|
+
def generated_reader?: (untyped, String) -> bool
|
|
163
|
+
|
|
164
|
+
@direct_loader: DirectRecordStateLoader
|
|
165
|
+
|
|
166
|
+
@direct_states: Hash[PreparedRecordState, PreparedRecordState?]
|
|
167
|
+
|
|
125
168
|
@end_time: untyped
|
|
126
169
|
|
|
127
170
|
@seeded_live_records: Hash[Array[String], untyped]
|
|
@@ -157,12 +200,6 @@ module PaperTrailDiff
|
|
|
157
200
|
# : (untyped) -> untyped
|
|
158
201
|
def window_condition: (untyped) -> untyped
|
|
159
202
|
|
|
160
|
-
# : (untyped) -> untyped
|
|
161
|
-
def first_after_range: (untyped) -> untyped
|
|
162
|
-
|
|
163
|
-
# : (untyped, untyped) -> untyped
|
|
164
|
-
def preceding_trailing_version: (untyped, untyped) -> untyped
|
|
165
|
-
|
|
166
203
|
# : (untyped, Array[untyped]) -> Array[untyped]
|
|
167
204
|
def available_live_records: (untyped, Array[untyped]) -> Array[untyped]
|
|
168
205
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/prepared_snapshot_builder.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Normalizes supported recorded graphs without historical ActiveRecord instances.
|
|
5
|
+
class PreparedSnapshotBuilder
|
|
6
|
+
# : (AssociationTraversal) -> void
|
|
7
|
+
def initialize: (AssociationTraversal) -> void
|
|
8
|
+
|
|
9
|
+
# : (untyped, untyped, tree: AssociationTree, normalizer: SnapshotNormalizer, history: PreparedHistory?) -> [bool, RecordSnapshot?]
|
|
10
|
+
def call: (untyped, untyped, tree: AssociationTree, normalizer: SnapshotNormalizer, history: PreparedHistory?) -> [ bool, RecordSnapshot? ]
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
@loader: DirectRecordStateLoader
|
|
15
|
+
|
|
16
|
+
@traversal: AssociationTraversal
|
|
17
|
+
|
|
18
|
+
# : (PreparedRecordState, untyped, AssociationTree, SnapshotNormalizer, PreparedHistory?, path: String, ?incoming: untyped) -> [bool, RecordSnapshot?]
|
|
19
|
+
def build: (PreparedRecordState, untyped, AssociationTree, SnapshotNormalizer, PreparedHistory?, path: String, ?incoming: untyped) -> [ bool, RecordSnapshot? ]
|
|
20
|
+
|
|
21
|
+
# : (PreparedRecordState, untyped, untyped, AssociationTree, SnapshotNormalizer, PreparedHistory, String) -> [bool, AssociationSnapshot?]
|
|
22
|
+
def association: (PreparedRecordState, untyped, untyped, AssociationTree, SnapshotNormalizer, PreparedHistory, String) -> [ bool, AssociationSnapshot? ]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/prepared_state_association_reader.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Reads plain direct edges without creating AR records for their members.
|
|
5
|
+
class PreparedStateAssociationReader
|
|
6
|
+
# : (PreparedRecordIndex, untyped) -> void
|
|
7
|
+
def initialize: (PreparedRecordIndex, untyped) -> void
|
|
8
|
+
|
|
9
|
+
# : (PreparedRecordState, untyped, untyped) -> [bool, Array[PreparedRecordState]]
|
|
10
|
+
def call: (PreparedRecordState, untyped, untyped) -> [ bool, Array[PreparedRecordState] ]
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
@edges: untyped
|
|
15
|
+
|
|
16
|
+
@records: PreparedRecordIndex
|
|
17
|
+
|
|
18
|
+
# : (untyped) -> bool
|
|
19
|
+
def supported?: (untyped) -> bool
|
|
20
|
+
|
|
21
|
+
# : (PreparedRecordState, untyped, untyped) -> [bool, Array[PreparedRecordState]]
|
|
22
|
+
def children: (PreparedRecordState, untyped, untyped) -> [ bool, Array[PreparedRecordState] ]
|
|
23
|
+
|
|
24
|
+
# : (untyped, PreparedRecordState) -> Array[untyped]
|
|
25
|
+
def ids_for: (untyped, PreparedRecordState) -> Array[untyped]
|
|
26
|
+
|
|
27
|
+
# : (PreparedRecordState, PreparedRecordState, untyped) -> bool
|
|
28
|
+
def member?: (PreparedRecordState, PreparedRecordState, untyped) -> bool
|
|
29
|
+
|
|
30
|
+
# : (PreparedRecordState, untyped, untyped) -> bool
|
|
31
|
+
def matching_type?: (PreparedRecordState, untyped, untyped) -> bool
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -32,6 +32,11 @@ module PaperTrailDiff
|
|
|
32
32
|
# version present only to reveal what the last of them produced.
|
|
33
33
|
attr_reader mutations: Array[untyped]
|
|
34
34
|
|
|
35
|
+
# Whether a wall-clock window closes on the root's own destruction. Its
|
|
36
|
+
# final state is then the absence the destroy left, just as a window
|
|
37
|
+
# closing on current state ends at the live record.
|
|
38
|
+
attr_reader closes_on_destroy: bool
|
|
39
|
+
|
|
35
40
|
# Adjacent boundaries, which is what an unfiltered range reports.
|
|
36
41
|
# : (Array[untyped], ?context_version: untyped) -> RootVersionPlan
|
|
37
42
|
def self.contiguous: (Array[untyped], ?context_version: untyped) -> RootVersionPlan
|
|
@@ -39,12 +44,20 @@ module PaperTrailDiff
|
|
|
39
44
|
# : () -> RootVersionPlan
|
|
40
45
|
def self.empty: () -> RootVersionPlan
|
|
41
46
|
|
|
42
|
-
#
|
|
43
|
-
|
|
47
|
+
# No root version to report or replay: only descendants moved, and the
|
|
48
|
+
# live record is the root's state throughout.
|
|
49
|
+
# : (untyped) -> RootVersionPlan
|
|
50
|
+
def self.closing_on: (untyped) -> RootVersionPlan
|
|
51
|
+
|
|
52
|
+
# : (versions: Array[untyped], steps: Array[[untyped, untyped]], ?context_version: untyped, ?reconstruction_versions: Array[untyped]?, ?mutations: Array[untyped]?, ?closing_record: untyped, ?closes_on_destroy: bool) -> void
|
|
53
|
+
def initialize: (versions: Array[untyped], steps: Array[[ untyped, untyped ]], ?context_version: untyped, ?reconstruction_versions: Array[untyped]?, ?mutations: Array[untyped]?, ?closing_record: untyped, ?closes_on_destroy: bool) -> void
|
|
44
54
|
|
|
45
55
|
# : () -> bool
|
|
46
56
|
def empty?: () -> bool
|
|
47
57
|
|
|
58
|
+
# : () -> RootVersionPlan
|
|
59
|
+
def closing_on_destroy: () -> RootVersionPlan
|
|
60
|
+
|
|
48
61
|
# What the range's final state is read from, which is the live record when
|
|
49
62
|
# the window closes on current state and the last version otherwise.
|
|
50
63
|
# : () -> untyped
|
|
@@ -44,7 +44,9 @@ module PaperTrailDiff
|
|
|
44
44
|
def contiguous_plan: (untyped) -> RootVersionPlan
|
|
45
45
|
|
|
46
46
|
# An activity view still needs a root to reconstruct from even when no root
|
|
47
|
-
# version falls inside the window, because descendants may have moved.
|
|
47
|
+
# version falls inside the window, because descendants may have moved. With
|
|
48
|
+
# no later version, a window closing on current state reads the root from
|
|
49
|
+
# the live record, which is then its state throughout the window.
|
|
48
50
|
# : () -> RootVersionPlan
|
|
49
51
|
def without_selection: () -> RootVersionPlan
|
|
50
52
|
|
|
@@ -83,6 +85,11 @@ module PaperTrailDiff
|
|
|
83
85
|
# : () -> untyped
|
|
84
86
|
def revealing_version: () -> untyped
|
|
85
87
|
|
|
88
|
+
# A window with nothing after its last selected mutation can only close on
|
|
89
|
+
# that mutation being the record's destruction.
|
|
90
|
+
# : (untyped) -> bool
|
|
91
|
+
def closes_on_destroy?: (untyped) -> bool
|
|
92
|
+
|
|
86
93
|
# A range closing on the record's own destruction needs no later version:
|
|
87
94
|
# nothing can follow it, so demanding one would reject the range forever.
|
|
88
95
|
# : () -> bool
|
|
@@ -16,9 +16,21 @@ module PaperTrailDiff
|
|
|
16
16
|
|
|
17
17
|
attr_reader unreachable: Array[Array[String]]
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
|
|
19
|
+
# The live records the selection loaded, in the order it loaded them. The
|
|
20
|
+
# gem already has them; withholding them only makes a caller query the same
|
|
21
|
+
# rows again to attach its own presentation data. A destroyed root has no
|
|
22
|
+
# record to hand back, so `roots` and `unreachable` stay complementary.
|
|
23
|
+
attr_reader roots: Array[untyped]
|
|
21
24
|
|
|
25
|
+
# Requested missing-root analyses, kept separate because live SQL conditions
|
|
26
|
+
# cannot prove which deleted rows matched. nil means no destroy history.
|
|
27
|
+
attr_reader deleted: Hash[Array[String], Analysis?]
|
|
28
|
+
|
|
29
|
+
# : (analyses: Hash[Array[String], Analysis], unreachable: Array[Array[String]], ?roots: Array[untyped], ?deleted: Hash[Array[String], Analysis?]) -> void
|
|
30
|
+
def initialize: (analyses: Hash[Array[String], Analysis], unreachable: Array[Array[String]], ?roots: Array[untyped], ?deleted: Hash[Array[String], Analysis?]) -> void
|
|
31
|
+
|
|
32
|
+
# Two elements, not three: `roots` is reached by name so that existing
|
|
33
|
+
# destructuring keeps working.
|
|
22
34
|
# : () -> [Hash[Array[String], Analysis], Array[Array[String]]]
|
|
23
35
|
def to_ary: () -> [ Hash[Array[String], Analysis], Array[Array[String]] ]
|
|
24
36
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/scoped_analysis_builder.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Selects both populations and checks their shared ceiling before analysis.
|
|
5
|
+
class ScopedAnalysisBuilder
|
|
6
|
+
# : (untyped, limit: Integer?, within: untyped, historical_filter: untyped, max_filter_versions: Integer, include_deleted: bool, analyzer: untyped, deleted_analyzer: untyped) -> void
|
|
7
|
+
def initialize: (untyped, limit: Integer?, within: untyped, historical_filter: untyped, max_filter_versions: Integer, include_deleted: bool, analyzer: untyped, deleted_analyzer: untyped) -> void
|
|
8
|
+
|
|
9
|
+
# : () -> ScopedAnalysis
|
|
10
|
+
def call: () -> ScopedAnalysis
|
|
11
|
+
|
|
12
|
+
@analyzer: untyped
|
|
13
|
+
|
|
14
|
+
@deleted_analyzer: untyped
|
|
15
|
+
|
|
16
|
+
@include_deleted: bool
|
|
17
|
+
|
|
18
|
+
@limit: Integer
|
|
19
|
+
|
|
20
|
+
@selection: ScopedRootSelection
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -25,6 +25,8 @@ module PaperTrailDiff
|
|
|
25
25
|
# window. `where(status: 'published')` selects what is published now, which is
|
|
26
26
|
# not the same set as what was published while the window was open.
|
|
27
27
|
class ScopedRootSelection
|
|
28
|
+
PAGE_SIZE: ::Integer
|
|
29
|
+
|
|
28
30
|
# A plain class rather than Data.define, which arrived in Ruby 3.2 while this
|
|
29
31
|
# gem supports 3.1.
|
|
30
32
|
class Result
|
|
@@ -36,20 +38,43 @@ module PaperTrailDiff
|
|
|
36
38
|
def initialize: (records: Array[untyped], unreachable: Array[Array[String]]) -> void
|
|
37
39
|
end
|
|
38
40
|
|
|
39
|
-
# : (untyped, time_range: TimeRange?, limit: Integer) -> void
|
|
40
|
-
def initialize: (untyped, time_range: TimeRange?, limit: Integer) -> void
|
|
41
|
+
# : (untyped, time_range: TimeRange?, limit: Integer, ?historical_filter: untyped, ?max_filter_versions: Integer) -> void
|
|
42
|
+
def initialize: (untyped, time_range: TimeRange?, limit: Integer, ?historical_filter: untyped, ?max_filter_versions: Integer) -> void
|
|
41
43
|
|
|
42
44
|
# : () -> Result
|
|
43
45
|
def call: () -> Result
|
|
44
46
|
|
|
45
47
|
private
|
|
46
48
|
|
|
49
|
+
@historical_filter: untyped
|
|
50
|
+
|
|
47
51
|
@limit: Integer
|
|
48
52
|
|
|
49
53
|
@scope: untyped
|
|
50
54
|
|
|
51
55
|
@time_range: TimeRange?
|
|
52
56
|
|
|
57
|
+
# Leaves the candidate set in the database. Plucking every root changed in
|
|
58
|
+
# the window would cost memory and a statement proportional to all activity
|
|
59
|
+
# in the window -- not to the relation or `limit:` -- so a report on one
|
|
60
|
+
# record could read every id in a busy table. A subquery keeps the work
|
|
61
|
+
# bounded by what the relation and the deletions actually contain.
|
|
62
|
+
# : () -> Result
|
|
63
|
+
def database_selection: () -> Result
|
|
64
|
+
|
|
65
|
+
# Comparing the two columns in SQL needs them to share a type: PostgreSQL
|
|
66
|
+
# rejects an integer key against a string item_id outright.
|
|
67
|
+
# : () -> bool
|
|
68
|
+
def comparable_keys?: () -> bool
|
|
69
|
+
|
|
70
|
+
# Changed in the window but without a row, found with an anti-join instead
|
|
71
|
+
# of comparing two plucked lists.
|
|
72
|
+
# : () -> Array[String]
|
|
73
|
+
def missing_ids: () -> Array[String]
|
|
74
|
+
|
|
75
|
+
# : () -> untyped
|
|
76
|
+
def window_versions: () -> untyped
|
|
77
|
+
|
|
53
78
|
# Accepts a model class as readily as a relation: `Article` and
|
|
54
79
|
# `Article.where(...)` both name a population, and `all` is what makes them
|
|
55
80
|
# the same kind of thing.
|
|
@@ -70,10 +95,25 @@ module PaperTrailDiff
|
|
|
70
95
|
# : (Array[String]) -> Array[String]
|
|
71
96
|
def live_ids: (Array[String]) -> Array[String]
|
|
72
97
|
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
|
|
98
|
+
# Deduplicate records rather than adding SQL DISTINCT: an otherwise valid
|
|
99
|
+
# relation may order by joined columns or select a PostgreSQL json column.
|
|
100
|
+
# Read bounded pages until the relation ends or too many unique roots appear.
|
|
101
|
+
# : (untyped) -> Array[untyped]
|
|
102
|
+
def selected_records: (untyped) -> Array[untyped]
|
|
103
|
+
|
|
104
|
+
# : (untyped, Integer) -> Array[untyped]
|
|
105
|
+
def record_page: (untyped, Integer) -> Array[untyped]
|
|
106
|
+
|
|
107
|
+
# Preserve explicit ordering: SQL Server rejects duplicate ORDER BY columns,
|
|
108
|
+
# even when the extra ordering would only have served as a tie-breaker.
|
|
109
|
+
# : () -> untyped
|
|
110
|
+
def ordered_scope: () -> untyped
|
|
111
|
+
|
|
112
|
+
# : (Integer) -> Integer
|
|
113
|
+
def page_limit: (Integer) -> Integer
|
|
114
|
+
|
|
115
|
+
# : (Integer) -> void
|
|
116
|
+
def reject_oversized!: (Integer) -> void
|
|
77
117
|
|
|
78
118
|
# : (String) -> Array[String]
|
|
79
119
|
def identity: (String) -> Array[String]
|
|
@@ -113,6 +113,10 @@ module PaperTrailDiff
|
|
|
113
113
|
# : () -> RecordReference
|
|
114
114
|
def reference: () -> RecordReference
|
|
115
115
|
|
|
116
|
+
# A copy holding a different state for one association.
|
|
117
|
+
# : (String, AssociationSnapshot) -> RecordSnapshot
|
|
118
|
+
def with_association: (String, AssociationSnapshot) -> RecordSnapshot
|
|
119
|
+
|
|
116
120
|
# : () -> Hash[Symbol, untyped]
|
|
117
121
|
def to_h: () -> Hash[Symbol, untyped]
|
|
118
122
|
|
|
@@ -37,8 +37,15 @@ module PaperTrailDiff
|
|
|
37
37
|
# : (untyped, tree: AssociationTree, path: String) -> snapshot_attributes
|
|
38
38
|
def attributes_for: (untyped, tree: AssociationTree, path: String) -> snapshot_attributes
|
|
39
39
|
|
|
40
|
+
# Same exclusions as AR normalization, applied to an already typed state.
|
|
41
|
+
# : (PreparedRecordState, tree: AssociationTree, path: String, ?incoming: untyped, ?associations: snapshot_associations) -> RecordSnapshot
|
|
42
|
+
def from_state: (PreparedRecordState, tree: AssociationTree, path: String, ?incoming: untyped, ?associations: snapshot_associations) -> RecordSnapshot
|
|
43
|
+
|
|
40
44
|
private
|
|
41
45
|
|
|
46
|
+
# : (String, RecordSnapshot) -> RecordSnapshot
|
|
47
|
+
def pool_state: (String, RecordSnapshot) -> RecordSnapshot
|
|
48
|
+
|
|
42
49
|
@excluded_attributes: Hash[Array[untyped], Array[String]]
|
|
43
50
|
|
|
44
51
|
@ignore_policy: IgnorePolicy
|
|
@@ -66,6 +73,12 @@ module PaperTrailDiff
|
|
|
66
73
|
# : (untyped, Array[untyped], String) -> Array[String]
|
|
67
74
|
def build_excluded_attributes: (untyped, Array[untyped], String) -> Array[String]
|
|
68
75
|
|
|
76
|
+
# `has_paper_trail skip:` columns are never written to a version, so history
|
|
77
|
+
# holds only their defaults. Compared with a live row they would report a
|
|
78
|
+
# change nobody recorded.
|
|
79
|
+
# : (untyped) -> Array[String]
|
|
80
|
+
def skipped_columns: (untyped) -> Array[String]
|
|
81
|
+
|
|
69
82
|
# : (Array[untyped]) -> Array[String]
|
|
70
83
|
def relationship_columns: (Array[untyped]) -> Array[String]
|
|
71
84
|
|
|
@@ -22,7 +22,13 @@ module PaperTrailDiff
|
|
|
22
22
|
# : () -> bool
|
|
23
23
|
def empty?: () -> bool
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
|
|
25
|
+
# The root event whose change this step reports.
|
|
26
|
+
# : () -> ActivityBoundary
|
|
27
|
+
def source_boundary: () -> ActivityBoundary
|
|
28
|
+
|
|
29
|
+
# The same shape as `ActivityStep#to_h`, so a consumer reads both step
|
|
30
|
+
# types alike; each boundary carries its own `version_id`.
|
|
31
|
+
# : (?metadata: bool) -> Hash[Symbol, untyped]
|
|
32
|
+
def to_h: (?metadata: bool) -> Hash[Symbol, untyped]
|
|
27
33
|
end
|
|
28
34
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Generated from lib/paper_trail_diff/stored_time_precision.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module PaperTrailDiff
|
|
4
|
+
# Matches time values to the precision PaperTrail actually stored.
|
|
5
|
+
#
|
|
6
|
+
# PaperTrail's JSON serializer, and a `json` or `jsonb` object column, write
|
|
7
|
+
# times through ActiveSupport's JSON encoder, which keeps only
|
|
8
|
+
# `ActiveSupport::JSON::Encoding.time_precision` fractional digits
|
|
9
|
+
# (milliseconds by default). The live row keeps the database's precision, so
|
|
10
|
+
# comparing the two would report every timestamp as changed even though
|
|
11
|
+
# nothing was edited. Truncating every side to the stored precision is
|
|
12
|
+
# idempotent, so values that came from history are unaffected and only the
|
|
13
|
+
# digits history could never have held are dropped.
|
|
14
|
+
module StoredTimePrecision
|
|
15
|
+
NANOSECOND_DIGITS: ::Integer
|
|
16
|
+
|
|
17
|
+
JSON_COLUMN_TYPES: untyped
|
|
18
|
+
|
|
19
|
+
# Fractional digits a model's history keeps for times, or nil when the
|
|
20
|
+
# history is stored losslessly.
|
|
21
|
+
# : (untyped) -> Integer?
|
|
22
|
+
def self?.digits: (untyped) -> Integer?
|
|
23
|
+
|
|
24
|
+
# : (Hash[untyped, untyped], Integer?) -> Hash[untyped, untyped]
|
|
25
|
+
def self?.apply: (Hash[untyped, untyped], Integer?) -> Hash[untyped, untyped]
|
|
26
|
+
|
|
27
|
+
# : (untyped, Integer) -> untyped
|
|
28
|
+
def self?.truncate: (untyped, Integer) -> untyped
|
|
29
|
+
|
|
30
|
+
# : (untyped) -> bool
|
|
31
|
+
def self?.json_history?: (untyped) -> bool
|
|
32
|
+
|
|
33
|
+
# : () -> bool
|
|
34
|
+
def self?.json_serializer?: () -> bool
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -43,6 +43,13 @@ module PaperTrailDiff
|
|
|
43
43
|
# : (untyped) -> bool
|
|
44
44
|
def self?.versioned?: (untyped) -> bool
|
|
45
45
|
|
|
46
|
+
# `versions.item_id` is often a string column -- PaperTrail's setup for UUID
|
|
47
|
+
# keys -- while the model's own key is an integer. An id read from history
|
|
48
|
+
# is cast to the key's type, or the same record read once from history and
|
|
49
|
+
# once from its live row would count as two.
|
|
50
|
+
# : (untyped, untyped) -> untyped
|
|
51
|
+
def self?.record_id: (untyped, untyped) -> untyped
|
|
52
|
+
|
|
46
53
|
# : (untyped) -> bool
|
|
47
54
|
def self?.sequential_id?: (untyped) -> bool
|
|
48
55
|
|
|
@@ -29,6 +29,9 @@ module PaperTrailDiff
|
|
|
29
29
|
# : () -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped]
|
|
30
30
|
def history_and_versions: () -> [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
|
|
31
31
|
|
|
32
|
+
# : () -> void
|
|
33
|
+
def reject_live_habtm!: () -> void
|
|
34
|
+
|
|
32
35
|
# : (RootVersionPlan, untyped) -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped]
|
|
33
36
|
def history_for: (RootVersionPlan, untyped) -> [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
|
|
34
37
|
|
|
@@ -40,8 +43,10 @@ module PaperTrailDiff
|
|
|
40
43
|
# : (Array[untyped], untyped) -> Array[ActivityEvent]
|
|
41
44
|
def events_through: (Array[untyped], untyped) -> Array[ActivityEvent]
|
|
42
45
|
|
|
43
|
-
#
|
|
44
|
-
|
|
46
|
+
# Events after the last root version read the root from the live record
|
|
47
|
+
# when the window closes on it, as an explicit `to: record` range does.
|
|
48
|
+
# : (Array[untyped], Array[ActivityEvent], ?current: untyped) -> ActivityHistory
|
|
49
|
+
def build_history: (Array[untyped], Array[ActivityEvent], ?current: untyped) -> ActivityHistory
|
|
45
50
|
|
|
46
51
|
# : (ActivityHistory, ActivityStep?) -> Array[ActivityStep]
|
|
47
52
|
def activity_steps: (ActivityHistory, ActivityStep?) -> Array[ActivityStep]
|
|
@@ -22,10 +22,19 @@ module PaperTrailDiff
|
|
|
22
22
|
# : (untyped) -> untyped
|
|
23
23
|
def trailing_scope: (untyped) -> untyped
|
|
24
24
|
|
|
25
|
+
# Each item's earliest version after the range, keyed by item id. One row
|
|
26
|
+
# per item comes back from a NOT EXISTS rather than a window function, so
|
|
27
|
+
# the query stays portable; a timestamp tie keeps the lower id.
|
|
28
|
+
# : (untyped) -> Hash[String, untyped]
|
|
29
|
+
def first_trailing_versions: (untyped) -> Hash[String, untyped]
|
|
30
|
+
|
|
25
31
|
private
|
|
26
32
|
|
|
27
33
|
@exclude_end: bool
|
|
28
34
|
|
|
35
|
+
# : (untyped) -> untyped
|
|
36
|
+
def after_end: (untyped) -> untyped
|
|
37
|
+
|
|
29
38
|
# : () -> Range[Time]
|
|
30
39
|
def query_range: () -> Range[Time]
|
|
31
40
|
|
|
@@ -6,9 +6,6 @@ module PaperTrailDiff
|
|
|
6
6
|
# : (untyped, time_range: TimeRange, ?version_scope: untyped, ?live_endpoint: untyped) -> void
|
|
7
7
|
def initialize: (untyped, time_range: TimeRange, ?version_scope: untyped, ?live_endpoint: untyped) -> void
|
|
8
8
|
|
|
9
|
-
# : (?context_required: bool) -> Array[untyped]
|
|
10
|
-
def select: (?context_required: bool) -> Array[untyped]
|
|
11
|
-
|
|
12
9
|
# : (?context_required: bool) -> RootVersionPlan
|
|
13
10
|
def select_plan: (?context_required: bool) -> RootVersionPlan
|
|
14
11
|
|
|
@@ -22,9 +19,6 @@ module PaperTrailDiff
|
|
|
22
19
|
|
|
23
20
|
@version_scope: untyped
|
|
24
21
|
|
|
25
|
-
# : () -> untyped
|
|
26
|
-
def versions_relation: () -> untyped
|
|
27
|
-
|
|
28
22
|
# : (untyped) -> untyped
|
|
29
23
|
def trailing_version: (untyped) -> untyped
|
|
30
24
|
|
|
@@ -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, ?
|
|
7
|
-
def initialize: (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?
|
|
6
|
+
# : (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> void
|
|
7
|
+
def initialize: (untyped, from: untyped, to: untyped, snapshotter: untyped, ?within: untyped, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> void
|
|
8
8
|
|
|
9
9
|
# : () -> Array[Step]
|
|
10
10
|
def build: () -> Array[Step]
|