paper_trail_diff 0.12.0 → 0.13.1

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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +158 -0
  3. data/QUICKSTART.md +149 -79
  4. data/README.md +179 -1388
  5. data/docs/architecture.md +88 -0
  6. data/docs/performance.md +309 -0
  7. data/docs/reference.md +977 -0
  8. data/docs/testing.md +169 -0
  9. data/lib/paper_trail_diff/activity_belongs_to_event_applier.rb +2 -12
  10. data/lib/paper_trail_diff/activity_boundary.rb +20 -8
  11. data/lib/paper_trail_diff/activity_child_candidate_loader.rb +29 -0
  12. data/lib/paper_trail_diff/activity_collection_event_applier.rb +10 -7
  13. data/lib/paper_trail_diff/activity_collection_route_updater.rb +1 -11
  14. data/lib/paper_trail_diff/activity_event_record_normalizer.rb +4 -0
  15. data/lib/paper_trail_diff/activity_event_record_resolver.rb +66 -6
  16. data/lib/paper_trail_diff/activity_event_snapshot_refresher.rb +6 -4
  17. data/lib/paper_trail_diff/activity_grouping.rb +12 -0
  18. data/lib/paper_trail_diff/activity_history.rb +9 -3
  19. data/lib/paper_trail_diff/activity_relationship.rb +2 -0
  20. data/lib/paper_trail_diff/activity_root_snapshot_refresher.rb +1 -0
  21. data/lib/paper_trail_diff/activity_snapshot_sequence.rb +9 -2
  22. data/lib/paper_trail_diff/activity_timeline_builder.rb +39 -35
  23. data/lib/paper_trail_diff/activity_version_collector.rb +27 -18
  24. data/lib/paper_trail_diff/analysis.rb +9 -4
  25. data/lib/paper_trail_diff/analysis_batch.rb +28 -7
  26. data/lib/paper_trail_diff/association_traversal.rb +23 -0
  27. data/lib/paper_trail_diff/batch_boundary_resolver.rb +18 -26
  28. data/lib/paper_trail_diff/batch_safety.rb +55 -0
  29. data/lib/paper_trail_diff/batched_root_analyzer.rb +14 -5
  30. data/lib/paper_trail_diff/batched_root_versions.rb +10 -38
  31. data/lib/paper_trail_diff/branch_snapshot_refresher.rb +7 -8
  32. data/lib/paper_trail_diff/collection_comparator.rb +18 -20
  33. data/lib/paper_trail_diff/collection_identity_index.rb +8 -12
  34. data/lib/paper_trail_diff/deleted_analysis_batch.rb +94 -0
  35. data/lib/paper_trail_diff/direct_record_state_loader.rb +81 -0
  36. data/lib/paper_trail_diff/endpoint.rb +10 -0
  37. data/lib/paper_trail_diff/engine.rb +4 -2
  38. data/lib/paper_trail_diff/errors.rb +9 -0
  39. data/lib/paper_trail_diff/historical_association_reifier.rb +26 -1
  40. data/lib/paper_trail_diff/historical_filter.rb +176 -0
  41. data/lib/paper_trail_diff/historical_snapshot_store.rb +51 -35
  42. data/lib/paper_trail_diff/instrumentation.rb +2 -2
  43. data/lib/paper_trail_diff/nested_comparator.rb +39 -15
  44. data/lib/paper_trail_diff/paper_trail_adapter.rb +109 -55
  45. data/lib/paper_trail_diff/prepared_activity_index.rb +121 -0
  46. data/lib/paper_trail_diff/prepared_edge_loader.rb +11 -11
  47. data/lib/paper_trail_diff/prepared_history.rb +21 -30
  48. data/lib/paper_trail_diff/prepared_history_loader.rb +14 -2
  49. data/lib/paper_trail_diff/prepared_record_index.rb +102 -29
  50. data/lib/paper_trail_diff/prepared_snapshot_builder.rb +64 -0
  51. data/lib/paper_trail_diff/prepared_state_association_reader.rb +77 -0
  52. data/lib/paper_trail_diff/root_version_plan.rb +35 -3
  53. data/lib/paper_trail_diff/root_version_selection.rb +16 -5
  54. data/lib/paper_trail_diff/scoped_analysis.rb +17 -2
  55. data/lib/paper_trail_diff/scoped_analysis_builder.rb +47 -0
  56. data/lib/paper_trail_diff/scoped_root_selection.rb +106 -13
  57. data/lib/paper_trail_diff/snapshot.rb +14 -1
  58. data/lib/paper_trail_diff/snapshot_normalizer.rb +75 -40
  59. data/lib/paper_trail_diff/step.rb +10 -5
  60. data/lib/paper_trail_diff/stored_time_precision.rb +68 -0
  61. data/lib/paper_trail_diff/support.rb +13 -3
  62. data/lib/paper_trail_diff/time_activity_timeline_builder.rb +19 -7
  63. data/lib/paper_trail_diff/time_range.rb +21 -2
  64. data/lib/paper_trail_diff/time_version_range.rb +2 -16
  65. data/lib/paper_trail_diff/timeline_builder.rb +5 -4
  66. data/lib/paper_trail_diff/timeline_range.rb +29 -46
  67. data/lib/paper_trail_diff/timeline_snapshot_provider.rb +13 -2
  68. data/lib/paper_trail_diff/traversal.rb +1 -1
  69. data/lib/paper_trail_diff/version.rb +1 -1
  70. data/lib/paper_trail_diff/version_association_candidate_scope.rb +1 -1
  71. data/lib/paper_trail_diff/version_boundary.rb +48 -0
  72. data/lib/paper_trail_diff/version_query.rb +41 -0
  73. data/lib/paper_trail_diff/version_range.rb +10 -38
  74. data/lib/paper_trail_diff.rb +78 -42
  75. data/sig/generated/paper_trail_diff/activity_belongs_to_event_applier.rbs +0 -3
  76. data/sig/generated/paper_trail_diff/activity_boundary.rbs +9 -4
  77. data/sig/generated/paper_trail_diff/activity_child_candidate_loader.rbs +11 -0
  78. data/sig/generated/paper_trail_diff/activity_collection_event_applier.rbs +4 -4
  79. data/sig/generated/paper_trail_diff/activity_collection_route_updater.rbs +0 -3
  80. data/sig/generated/paper_trail_diff/activity_event_record_resolver.rbs +19 -3
  81. data/sig/generated/paper_trail_diff/activity_event_snapshot_refresher.rbs +2 -2
  82. data/sig/generated/paper_trail_diff/activity_grouping.rbs +3 -0
  83. data/sig/generated/paper_trail_diff/activity_history.rbs +6 -2
  84. data/sig/generated/paper_trail_diff/activity_snapshot_sequence.rbs +7 -2
  85. data/sig/generated/paper_trail_diff/activity_timeline_builder.rbs +8 -3
  86. data/sig/generated/paper_trail_diff/activity_version_collector.rbs +4 -8
  87. data/sig/generated/paper_trail_diff/analysis.rbs +2 -2
  88. data/sig/generated/paper_trail_diff/analysis_batch.rbs +14 -3
  89. data/sig/generated/paper_trail_diff/association_traversal.rbs +11 -0
  90. data/sig/generated/paper_trail_diff/batch_boundary_resolver.rbs +7 -10
  91. data/sig/generated/paper_trail_diff/batch_safety.rbs +24 -0
  92. data/sig/generated/paper_trail_diff/batched_root_analyzer.rbs +8 -2
  93. data/sig/generated/paper_trail_diff/batched_root_versions.rbs +4 -16
  94. data/sig/generated/paper_trail_diff/branch_snapshot_refresher.rbs +2 -2
  95. data/sig/generated/paper_trail_diff/collection_comparator.rbs +6 -3
  96. data/sig/generated/paper_trail_diff/collection_identity_index.rbs +3 -3
  97. data/sig/generated/paper_trail_diff/deleted_analysis_batch.rbs +44 -0
  98. data/sig/generated/paper_trail_diff/direct_record_state_loader.rbs +42 -0
  99. data/sig/generated/paper_trail_diff/endpoint.rbs +5 -0
  100. data/sig/generated/paper_trail_diff/engine.rbs +4 -2
  101. data/sig/generated/paper_trail_diff/errors.rbs +12 -0
  102. data/sig/generated/paper_trail_diff/historical_association_reifier.rbs +8 -0
  103. data/sig/generated/paper_trail_diff/historical_filter.rbs +75 -0
  104. data/sig/generated/paper_trail_diff/historical_snapshot_store.rbs +17 -4
  105. data/sig/generated/paper_trail_diff/nested_comparator.rbs +15 -7
  106. data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +36 -15
  107. data/sig/generated/paper_trail_diff/prepared_activity_index.rbs +56 -0
  108. data/sig/generated/paper_trail_diff/prepared_edge_loader.rbs +3 -3
  109. data/sig/generated/paper_trail_diff/prepared_history.rbs +12 -14
  110. data/sig/generated/paper_trail_diff/prepared_history_loader.rbs +2 -0
  111. data/sig/generated/paper_trail_diff/prepared_record_index.rbs +43 -6
  112. data/sig/generated/paper_trail_diff/prepared_snapshot_builder.rbs +24 -0
  113. data/sig/generated/paper_trail_diff/prepared_state_association_reader.rbs +33 -0
  114. data/sig/generated/paper_trail_diff/root_version_plan.rbs +22 -2
  115. data/sig/generated/paper_trail_diff/root_version_selection.rbs +8 -1
  116. data/sig/generated/paper_trail_diff/scoped_analysis.rbs +14 -2
  117. data/sig/generated/paper_trail_diff/scoped_analysis_builder.rbs +22 -0
  118. data/sig/generated/paper_trail_diff/scoped_root_selection.rbs +46 -6
  119. data/sig/generated/paper_trail_diff/snapshot.rbs +4 -0
  120. data/sig/generated/paper_trail_diff/snapshot_normalizer.rbs +34 -11
  121. data/sig/generated/paper_trail_diff/step.rbs +8 -2
  122. data/sig/generated/paper_trail_diff/stored_time_precision.rbs +36 -0
  123. data/sig/generated/paper_trail_diff/support.rbs +7 -0
  124. data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +7 -2
  125. data/sig/generated/paper_trail_diff/time_range.rbs +9 -0
  126. data/sig/generated/paper_trail_diff/time_version_range.rbs +0 -6
  127. data/sig/generated/paper_trail_diff/timeline_builder.rbs +2 -2
  128. data/sig/generated/paper_trail_diff/timeline_range.rbs +12 -15
  129. data/sig/generated/paper_trail_diff/timeline_snapshot_provider.rbs +2 -0
  130. data/sig/generated/paper_trail_diff/version_boundary.rbs +23 -0
  131. data/sig/generated/paper_trail_diff/version_query.rbs +21 -0
  132. data/sig/generated/paper_trail_diff/version_range.rbs +5 -14
  133. data/sig/generated/paper_trail_diff.rbs +31 -25
  134. metadata +31 -5
@@ -24,11 +24,15 @@ module PaperTrailDiff
24
24
  # one of them, so absence is its own value rather than nil.
25
25
  class NestedComparator
26
26
  # Stands in for a key that was not there at all.
27
- ABSENT: untyped
27
+ class Absent
28
+ # : () -> String
29
+ def inspect: () -> String
28
30
 
29
- def inspect: () -> untyped
31
+ # : () -> String
32
+ def to_s: () -> String
33
+ end
30
34
 
31
- def to_s: () -> untyped
35
+ ABSENT: untyped
32
36
 
33
37
  # : (untyped, untyped) -> nested_changes
34
38
  def self.call: (untyped, untyped) -> nested_changes
@@ -60,14 +64,18 @@ module PaperTrailDiff
60
64
  # : (Hash[untyped, untyped], Hash[untyped, untyped], Array[String], nested_changes) -> void
61
65
  def walk: (Hash[untyped, untyped], Hash[untyped, untyped], Array[String], nested_changes) -> void
62
66
 
67
+ # : (Hash[untyped, untyped]) -> Hash[String, untyped]
68
+ def normalize_keys: (Hash[untyped, untyped]) -> Hash[String, untyped]
69
+
63
70
  # Two arrays are a membership change; anything else is a change to the
64
71
  # value as a whole, including an array facing something that is not one.
65
72
  # : (untyped, untyped) -> nested_change
66
73
  def change_for: (untyped, untyped) -> nested_change
67
74
 
68
- # Sorted so a report reads the same twice, and stringified because a hash
69
- # loaded from JSON and one built in Ruby can key the same field differently.
70
- # : (Hash[untyped, untyped], Hash[untyped, untyped]) -> Array[untyped]
71
- def keys: (Hash[untyped, untyped], Hash[untyped, untyped]) -> Array[untyped]
75
+ # Sorted so a report reads the same twice. Both hashes are already keyed by
76
+ # strings, because a hash loaded from JSON and one built in Ruby can key the
77
+ # same field differently.
78
+ # : (Hash[String, untyped], Hash[String, untyped]) -> Array[String]
79
+ def keys: (Hash[String, untyped], Hash[String, untyped]) -> Array[String]
72
80
  end
73
81
  end
@@ -9,8 +9,8 @@ module PaperTrailDiff
9
9
  # The only thing a wall-clock window can close on besides a later version.
10
10
  CLOSE_ON_CURRENT: ::Symbol
11
11
 
12
- # : (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool) -> void
13
- def initialize: (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool) -> void
12
+ # : (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool, ?allow_expensive: bool) -> void
13
+ def initialize: (associations: Array[String | Symbol], ignore: ignore_option, ?reload_live_endpoints: bool, ?allow_expensive: bool) -> void
14
14
 
15
15
  # : (untyped, untyped) -> Diff
16
16
  def compare: (untyped, untyped) -> Diff
@@ -29,18 +29,32 @@ module PaperTrailDiff
29
29
  def analyze: (untyped, from: untyped, to: untyped, within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Analysis
30
30
 
31
31
  # Analyzes many roots over one shared range, preparing their history once.
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]
32
+ # : (Array[untyped], within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Hash[identity, Analysis]
33
+ def analyze_many: (Array[untyped], within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Hash[identity, Analysis]
34
34
 
35
35
  # Selects the roots from a relation before running the ordinary batch, so a
36
36
  # caller reporting on a population does not have to rediscover which of its
37
37
  # members changed. The roots the relation could not reach come back named
38
38
  # rather than dropped -- see `PaperTrailDiff.analyze_scope`.
39
- # : (untyped, limit: Integer?, within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> ScopedAnalysis
40
- def analyze_scope: (untyped, limit: Integer?, within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> ScopedAnalysis
39
+ # : (untyped, limit: Integer?, within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?historical_filter: untyped, ?max_filter_versions: Integer, ?include_deleted: bool) -> ScopedAnalysis
40
+ def analyze_scope: (untyped, limit: Integer?, within: untyped, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?historical_filter: untyped, ?max_filter_versions: Integer, ?include_deleted: bool) -> ScopedAnalysis
41
41
 
42
42
  private
43
43
 
44
+ # : (Array[comparison_input], preparer: untyped) -> ComparisonBatch
45
+ def comparison_batch: (Array[comparison_input], preparer: untyped) -> ComparisonBatch
46
+
47
+ # : (Array[untyped]) -> Hash[identity, untyped]
48
+ def load_batch_endpoints: (Array[untyped]) -> Hash[identity, untyped]
49
+
50
+ # : (untyped, historical: bool) -> void
51
+ def prepare_batch_model: (untyped, historical: bool) -> void
52
+
53
+ @batch_safety: BatchSafety
54
+
55
+ # : (untyped, Array[Array[String]], Hash[Symbol, untyped]) -> Hash[Array[String], Analysis?]
56
+ def analyze_deleted: (untyped, Array[Array[String]], Hash[Symbol, untyped]) -> Hash[Array[String], Analysis?]
57
+
44
58
  @activity_snapshotter: ActivitySnapshotProvider
45
59
 
46
60
  @association_tree: AssociationTree
@@ -74,21 +88,31 @@ module PaperTrailDiff
74
88
  # : (untyped, untyped) -> void
75
89
  def notify_ambiguous_association_boundary: (untyped, untyped) -> void
76
90
 
77
- # `close_on:` names what ends a wall-clock window, so it is meaningless for a
78
- # range whose endpoints the caller already gave explicitly.
91
+ # A window with nothing recorded after its last change ends at the live
92
+ # record, which holds the state that change produced -- whether the window
93
+ # ended a moment ago or long before. `close_on: :current` asked for this
94
+ # before it was the default and is still accepted. It names what ends a
95
+ # wall-clock window, so it is meaningless for explicit endpoints.
79
96
  # : (Symbol?, untyped) -> bool
80
97
  def close_on_current?: (Symbol?, untyped) -> bool
81
98
 
82
- # A destroyed root has no current state to close on, and its own destroy
83
- # version already terminates the history.
99
+ # With neither a window nor endpoints, a range is the record's whole
100
+ # history through its current saved state; a missing endpoint takes that
101
+ # side of the same default.
102
+ # : (untyped, untyped, untyped, untyped) -> [untyped, untyped]
103
+ def default_range: (untyped, untyped, untyped, untyped) -> [ untyped, untyped ]
104
+
105
+ # Only a saved row has current state to close on. A destroyed root's own
106
+ # destroy version already terminates its history, and a reified or unsaved
107
+ # instance stands for a state that is not in the table.
84
108
  # : (untyped, Symbol?, untyped) -> untyped
85
109
  def live_endpoint_for: (untyped, Symbol?, untyped) -> untyped
86
110
 
87
111
  # : () -> void
88
112
  def build_snapshotters: () -> void
89
113
 
90
- # : (bool) -> BatchedRootAnalyzer
91
- def batched_root_analyzer: (bool) -> BatchedRootAnalyzer
114
+ # : (bool, within: untyped, snapshots: bool, group: Symbol?) -> BatchedRootAnalyzer
115
+ def batched_root_analyzer: (bool, within: untyped, snapshots: bool, group: Symbol?) -> BatchedRootAnalyzer
92
116
 
93
117
  # : () -> HistoricalSnapshotStore
94
118
  def build_historical_store: () -> HistoricalSnapshotStore
@@ -99,9 +123,6 @@ module PaperTrailDiff
99
123
  # : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?live_endpoint: untyped, ?snapshots: bool, ?group: Symbol?) -> ActivityTimelineBuilder
100
124
  def activity_builder: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?live_endpoint: untyped, ?snapshots: bool, ?group: Symbol?) -> ActivityTimelineBuilder
101
125
 
102
- # : (untyped) -> void
103
- def reject_live_habtm_activity!: (untyped) -> void
104
-
105
126
  # : (untyped) -> RecordSnapshot?
106
127
  def snapshot_for_endpoint: (untyped) -> RecordSnapshot?
107
128
 
@@ -0,0 +1,56 @@
1
+ # Generated from lib/paper_trail_diff/prepared_activity_index.rb with RBS::Inline
2
+
3
+ module PaperTrailDiff
4
+ # Shares association event discovery across every root in a prepared graph.
5
+ class PreparedActivityIndex
6
+ # : (PreparedRecordIndex, Array[untyped], start_at: untyped, end_at: untyped) -> void
7
+ def initialize: (PreparedRecordIndex, Array[untyped], start_at: untyped, end_at: untyped) -> void
8
+
9
+ # : (untyped, Array[untyped]) -> void
10
+ def register: (untyped, Array[untyped]) -> void
11
+
12
+ # : (untyped, Array[untyped]) -> Array[untyped]
13
+ def versions: (untyped, Array[untyped]) -> Array[untyped]
14
+
15
+ # : (untyped, Array[untyped], untyped, ActivityRange) -> Array[untyped]
16
+ def child_ids: (untyped, Array[untyped], untyped, ActivityRange) -> Array[untyped]
17
+
18
+ # : (untyped, Array[untyped], untyped, ?transactions: bool) -> Array[untyped]
19
+ def association_rows: (untyped, Array[untyped], untyped, ?transactions: bool) -> Array[untyped]
20
+
21
+ private
22
+
23
+ @children: Hash[Array[String], untyped]
24
+
25
+ @nodes: Hash[String, Array[untyped]]
26
+
27
+ @range: ActivityRange
28
+
29
+ @records: PreparedRecordIndex
30
+
31
+ @roots: Hash[Array[String], Array[untyped]]
32
+
33
+ @rows: Hash[Array[String], Hash[String, Array[untyped]]]
34
+
35
+ # : (untyped, Array[untyped], untyped, ActivityRange) -> Array[untyped]
36
+ def historical_ids: (untyped, Array[untyped], untyped, ActivityRange) -> Array[untyped]
37
+
38
+ # : (untyped) -> Array[untyped]
39
+ def node_ids: (untyped) -> Array[untyped]
40
+
41
+ # : (untyped, untyped) -> untyped
42
+ def child_rows: (untyped, untyped) -> untyped
43
+
44
+ # Mirrors VersionAssociationCandidateScope's before-window rule. The loaded
45
+ # series includes changes to other owners too, so an old membership cannot
46
+ # survive a later pre-window reassignment just because that owner is absent.
47
+ # : (untyped, Array[untyped], ActivityRange) -> bool
48
+ def eligible_child?: (untyped, Array[untyped], ActivityRange) -> bool
49
+
50
+ # : (untyped, untyped, bool) -> Hash[String, Array[untyped]]
51
+ def load_association_rows: (untyped, untyped, bool) -> Hash[String, Array[untyped]]
52
+
53
+ # : (Array[untyped], bool) -> Array[untyped]
54
+ def version_ids: (Array[untyped], bool) -> Array[untyped]
55
+ end
56
+ end
@@ -46,6 +46,9 @@ module PaperTrailDiff
46
46
  # : (untyped, Array[untyped], untyped, String) -> Hash[String, Array[untyped]]
47
47
  def historical_child_ids: (untyped, Array[untyped], untyped, String) -> Hash[String, Array[untyped]]
48
48
 
49
+ # : (untyped, Array[Array[untyped]]) -> Array[Array[untyped]]
50
+ def typed_child_ids: (untyped, Array[Array[untyped]]) -> Array[Array[untyped]]
51
+
49
52
  # : (untyped, Array[untyped], untyped, String) -> untyped
50
53
  def historical_child_relation: (untyped, Array[untyped], untyped, String) -> untyped
51
54
 
@@ -55,9 +58,6 @@ module PaperTrailDiff
55
58
  # : (untyped, Array[untyped], untyped, String) -> Hash[String, Array[untyped]]
56
59
  def live_child_ids: (untyped, Array[untyped], untyped, String) -> Hash[String, Array[untyped]]
57
60
 
58
- # : (untyped, untyped) -> untyped
59
- def target_class: (untyped, untyped) -> untyped
60
-
61
61
  # : (Hash[String, Hash[Symbol, untyped]], untyped, untyped) -> void
62
62
  def add_identity: (Hash[String, Hash[Symbol, untyped]], untyped, untyped) -> void
63
63
 
@@ -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,8 @@ module PaperTrailDiff
23
23
 
24
24
  @root_ids: Array[untyped]
25
25
 
26
+ @root_versions: Array[untyped]
27
+
26
28
  @traversal: AssociationTraversal
27
29
 
28
30
  @tree: AssociationTree
@@ -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
- # : (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
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
@@ -60,6 +73,13 @@ module PaperTrailDiff
60
73
  # : () -> Array[untyped]
61
74
  def default_mutations: () -> Array[untyped]
62
75
 
76
+ # A Set only makes the lookup in `mutation?` fast; an Array would answer the
77
+ # same, which is why mutant leaves this alone.
78
+ # mutant:disable
79
+ # : (Array[untyped]) -> Set[Array[untyped]]
80
+ def key_set: (Array[untyped]) -> Set[Array[untyped]]
81
+
82
+ # By class name, so a version still matches after its class is reloaded.
63
83
  # : (untyped) -> Array[untyped]
64
84
  def key: (untyped) -> Array[untyped]
65
85
  end
@@ -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
- # : (analyses: Hash[Array[String], Analysis], unreachable: Array[Array[String]]) -> void
20
- def initialize: (analyses: Hash[Array[String], Analysis], unreachable: Array[Array[String]]) -> void
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