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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -0
  3. data/QUICKSTART.md +149 -79
  4. data/README.md +179 -1367
  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 +146 -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 +9 -37
  31. data/lib/paper_trail_diff/branch_snapshot_refresher.rb +7 -8
  32. data/lib/paper_trail_diff/deleted_analysis_batch.rb +93 -0
  33. data/lib/paper_trail_diff/direct_record_state_loader.rb +81 -0
  34. data/lib/paper_trail_diff/endpoint.rb +10 -0
  35. data/lib/paper_trail_diff/engine.rb +4 -2
  36. data/lib/paper_trail_diff/errors.rb +9 -0
  37. data/lib/paper_trail_diff/historical_association_reifier.rb +26 -1
  38. data/lib/paper_trail_diff/historical_filter.rb +181 -0
  39. data/lib/paper_trail_diff/historical_snapshot_store.rb +51 -35
  40. data/lib/paper_trail_diff/instrumentation.rb +2 -2
  41. data/lib/paper_trail_diff/nested_comparator.rb +59 -24
  42. data/lib/paper_trail_diff/paper_trail_adapter.rb +109 -55
  43. data/lib/paper_trail_diff/prepared_activity_index.rb +121 -0
  44. data/lib/paper_trail_diff/prepared_edge_loader.rb +11 -11
  45. data/lib/paper_trail_diff/prepared_history.rb +21 -30
  46. data/lib/paper_trail_diff/prepared_history_loader.rb +14 -2
  47. data/lib/paper_trail_diff/prepared_record_index.rb +102 -29
  48. data/lib/paper_trail_diff/prepared_snapshot_builder.rb +64 -0
  49. data/lib/paper_trail_diff/prepared_state_association_reader.rb +77 -0
  50. data/lib/paper_trail_diff/root_version_plan.rb +25 -2
  51. data/lib/paper_trail_diff/root_version_selection.rb +16 -5
  52. data/lib/paper_trail_diff/scoped_analysis.rb +17 -2
  53. data/lib/paper_trail_diff/scoped_analysis_builder.rb +47 -0
  54. data/lib/paper_trail_diff/scoped_root_selection.rb +106 -13
  55. data/lib/paper_trail_diff/snapshot.rb +9 -0
  56. data/lib/paper_trail_diff/snapshot_normalizer.rb +41 -2
  57. data/lib/paper_trail_diff/step.rb +10 -5
  58. data/lib/paper_trail_diff/stored_time_precision.rb +68 -0
  59. data/lib/paper_trail_diff/support.rb +13 -3
  60. data/lib/paper_trail_diff/time_activity_timeline_builder.rb +19 -7
  61. data/lib/paper_trail_diff/time_range.rb +22 -2
  62. data/lib/paper_trail_diff/time_version_range.rb +1 -15
  63. data/lib/paper_trail_diff/timeline_builder.rb +5 -4
  64. data/lib/paper_trail_diff/timeline_range.rb +25 -43
  65. data/lib/paper_trail_diff/timeline_snapshot_provider.rb +13 -2
  66. data/lib/paper_trail_diff/traversal.rb +1 -1
  67. data/lib/paper_trail_diff/value_objects.rb +66 -0
  68. data/lib/paper_trail_diff/version.rb +1 -1
  69. data/lib/paper_trail_diff/version_association_candidate_scope.rb +1 -1
  70. data/lib/paper_trail_diff/version_boundary.rb +48 -0
  71. data/lib/paper_trail_diff/version_query.rb +33 -0
  72. data/lib/paper_trail_diff/version_range.rb +10 -38
  73. data/lib/paper_trail_diff.rb +81 -43
  74. data/sig/generated/paper_trail_diff/activity_belongs_to_event_applier.rbs +0 -3
  75. data/sig/generated/paper_trail_diff/activity_boundary.rbs +9 -4
  76. data/sig/generated/paper_trail_diff/activity_child_candidate_loader.rbs +11 -0
  77. data/sig/generated/paper_trail_diff/activity_collection_event_applier.rbs +4 -4
  78. data/sig/generated/paper_trail_diff/activity_collection_route_updater.rbs +0 -3
  79. data/sig/generated/paper_trail_diff/activity_event_record_resolver.rbs +19 -3
  80. data/sig/generated/paper_trail_diff/activity_event_snapshot_refresher.rbs +2 -2
  81. data/sig/generated/paper_trail_diff/activity_grouping.rbs +3 -0
  82. data/sig/generated/paper_trail_diff/activity_history.rbs +6 -2
  83. data/sig/generated/paper_trail_diff/activity_snapshot_sequence.rbs +7 -2
  84. data/sig/generated/paper_trail_diff/activity_timeline_builder.rbs +8 -3
  85. data/sig/generated/paper_trail_diff/activity_version_collector.rbs +4 -8
  86. data/sig/generated/paper_trail_diff/analysis.rbs +2 -2
  87. data/sig/generated/paper_trail_diff/analysis_batch.rbs +14 -3
  88. data/sig/generated/paper_trail_diff/association_traversal.rbs +11 -0
  89. data/sig/generated/paper_trail_diff/batch_boundary_resolver.rbs +7 -10
  90. data/sig/generated/paper_trail_diff/batch_safety.rbs +24 -0
  91. data/sig/generated/paper_trail_diff/batched_root_analyzer.rbs +8 -2
  92. data/sig/generated/paper_trail_diff/batched_root_versions.rbs +4 -16
  93. data/sig/generated/paper_trail_diff/branch_snapshot_refresher.rbs +2 -2
  94. data/sig/generated/paper_trail_diff/deleted_analysis_batch.rbs +44 -0
  95. data/sig/generated/paper_trail_diff/direct_record_state_loader.rbs +42 -0
  96. data/sig/generated/paper_trail_diff/endpoint.rbs +5 -0
  97. data/sig/generated/paper_trail_diff/engine.rbs +4 -2
  98. data/sig/generated/paper_trail_diff/errors.rbs +12 -0
  99. data/sig/generated/paper_trail_diff/historical_association_reifier.rbs +8 -0
  100. data/sig/generated/paper_trail_diff/historical_filter.rbs +75 -0
  101. data/sig/generated/paper_trail_diff/historical_snapshot_store.rbs +17 -4
  102. data/sig/generated/paper_trail_diff/nested_comparator.rbs +31 -18
  103. data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +36 -15
  104. data/sig/generated/paper_trail_diff/prepared_activity_index.rbs +56 -0
  105. data/sig/generated/paper_trail_diff/prepared_edge_loader.rbs +3 -3
  106. data/sig/generated/paper_trail_diff/prepared_history.rbs +12 -14
  107. data/sig/generated/paper_trail_diff/prepared_history_loader.rbs +2 -0
  108. data/sig/generated/paper_trail_diff/prepared_record_index.rbs +43 -6
  109. data/sig/generated/paper_trail_diff/prepared_snapshot_builder.rbs +24 -0
  110. data/sig/generated/paper_trail_diff/prepared_state_association_reader.rbs +33 -0
  111. data/sig/generated/paper_trail_diff/root_version_plan.rbs +15 -2
  112. data/sig/generated/paper_trail_diff/root_version_selection.rbs +8 -1
  113. data/sig/generated/paper_trail_diff/scoped_analysis.rbs +14 -2
  114. data/sig/generated/paper_trail_diff/scoped_analysis_builder.rbs +22 -0
  115. data/sig/generated/paper_trail_diff/scoped_root_selection.rbs +46 -6
  116. data/sig/generated/paper_trail_diff/snapshot.rbs +4 -0
  117. data/sig/generated/paper_trail_diff/snapshot_normalizer.rbs +13 -0
  118. data/sig/generated/paper_trail_diff/step.rbs +8 -2
  119. data/sig/generated/paper_trail_diff/stored_time_precision.rbs +36 -0
  120. data/sig/generated/paper_trail_diff/support.rbs +7 -0
  121. data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +7 -2
  122. data/sig/generated/paper_trail_diff/time_range.rbs +9 -0
  123. data/sig/generated/paper_trail_diff/time_version_range.rbs +0 -6
  124. data/sig/generated/paper_trail_diff/timeline_builder.rbs +2 -2
  125. data/sig/generated/paper_trail_diff/timeline_range.rbs +10 -14
  126. data/sig/generated/paper_trail_diff/timeline_snapshot_provider.rbs +2 -0
  127. data/sig/generated/paper_trail_diff/value_objects.rbs +45 -0
  128. data/sig/generated/paper_trail_diff/version_boundary.rbs +23 -0
  129. data/sig/generated/paper_trail_diff/version_query.rbs +15 -0
  130. data/sig/generated/paper_trail_diff/version_range.rbs +5 -14
  131. data/sig/generated/paper_trail_diff.rbs +37 -27
  132. metadata +31 -5
@@ -5,16 +5,20 @@ module PaperTrailDiff
5
5
  class TimelineRange
6
6
  BOUNDARY_SYMBOLS: untyped
7
7
 
8
+ # `:last` once named the last version, whose recorded state is the one
9
+ # *before* the latest change, so a range ending there silently dropped it.
10
+ LAST_REMOVED: ::String
11
+
8
12
  attr_reader from: untyped
9
13
 
10
14
  attr_reader to: untyped
11
15
 
12
16
  attr_reader time_range: TimeRange?
13
17
 
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
18
+ # : (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> void
19
+ def initialize: (untyped, from: untyped, to: untyped, within: untyped, ?version_scope: untyped, ?plan: RootVersionPlan?, ?live_endpoint: untyped) -> void
16
20
 
17
- # A record with no versions has no first or last boundary to resolve, which
21
+ # A record with no versions has no first boundary to resolve, which
18
22
  # is an empty history rather than a bad request.
19
23
  # : () -> bool
20
24
  def unresolved?: () -> bool
@@ -26,9 +30,6 @@ module PaperTrailDiff
26
30
  # : (?context_required: bool) -> RootVersionPlan
27
31
  def select_plan: (?context_required: bool) -> RootVersionPlan
28
32
 
29
- # : (?context_required: bool) -> Array[untyped]
30
- def select: (?context_required: bool) -> Array[untyped]
31
-
32
33
  # : () -> bool
33
34
  def time?: () -> bool
34
35
 
@@ -56,15 +57,13 @@ module PaperTrailDiff
56
57
 
57
58
  @version_scope: untyped
58
59
 
59
- @versions: Array[untyped]?
60
-
61
- # : () -> Array[untyped]
62
- def empty_versions: () -> Array[untyped]
60
+ # : () -> RootVersionPlan
61
+ def explicit_plan: () -> RootVersionPlan
63
62
 
64
63
  # : (untyped) -> bool
65
64
  def symbolic?: (untyped) -> bool
66
65
 
67
- # Resolving `:first` and `:last` here keeps callers from depending on the
66
+ # Resolving `:first` here keeps callers from depending on the
68
67
  # order PaperTrail happens to give its versions association, which a caller
69
68
  # is also free to reorder.
70
69
  # : (untyped) -> untyped
@@ -73,9 +72,6 @@ module PaperTrailDiff
73
72
  # : () -> untyped
74
73
  def ordered_versions: () -> untyped
75
74
 
76
- # : () -> untyped
77
- def versions_relation: () -> untyped
78
-
79
75
  # : (untyped) -> TimeRange?
80
76
  def build_time_range: (untyped) -> TimeRange?
81
77
 
@@ -21,6 +21,8 @@ module PaperTrailDiff
21
21
 
22
22
  @live_snapshotter: untyped
23
23
 
24
+ @snapshots: Hash[Array[untyped], RecordSnapshot?]
25
+
24
26
  @store: HistoricalSnapshotStore
25
27
  end
26
28
  end
@@ -14,6 +14,51 @@ module PaperTrailDiff
14
14
  def to_h: () -> Hash[Symbol, untyped]
15
15
  end
16
16
 
17
+ # A change to an array inside a value the database stores whole, reported by
18
+ # membership rather than by position.
19
+ #
20
+ # Position would be the obvious thing to report and the wrong one: elements
21
+ # here carry no identity, so inserting at the front makes every later index
22
+ # look changed, and one insertion is described as several edits. Membership is
23
+ # answerable without claiming any pairing -- "gained \"saturn v\"" is true
24
+ # whether the array is a set, a queue, or a ranked list.
25
+ #
26
+ # That leaves one case membership cannot see, so it is named rather than
27
+ # hidden: the same elements in a different order. `reordered?` says so.
28
+ #
29
+ # An element that is itself a Hash or Array is reported whole. Saying which
30
+ # field of which object changed would require pairing before-elements with
31
+ # after-elements, and nothing in the value licenses that pairing.
32
+ class ArrayChange
33
+ attr_reader from: Array[untyped]
34
+
35
+ attr_reader to: Array[untyped]
36
+
37
+ attr_reader added: Array[untyped]
38
+
39
+ attr_reader removed: Array[untyped]
40
+
41
+ # : (from: Array[untyped], to: Array[untyped]) -> void
42
+ def initialize: (from: Array[untyped], to: Array[untyped]) -> void
43
+
44
+ # Elements of `left` with no counterpart left in `right`, matching by value
45
+ # and respecting duplicates, so ["a", "a"] -> ["a"] reports one removal
46
+ # rather than none.
47
+ # : (Array[untyped], Array[untyped]) -> Array[untyped]
48
+ def self.surplus: (Array[untyped], Array[untyped]) -> Array[untyped]
49
+
50
+ # Same elements, different sequence. Only meaningful for a change that was
51
+ # recorded at all, which this only is when the two sides differ.
52
+ # : () -> bool
53
+ def reordered?: () -> bool
54
+
55
+ # : () -> bool
56
+ def empty?: () -> bool
57
+
58
+ # : () -> Hash[Symbol, untyped]
59
+ def to_h: () -> Hash[Symbol, untyped]
60
+ end
61
+
17
62
  # Attribute and nested-association changes for a record whose identity did not change.
18
63
  class RecordChange
19
64
  attr_reader record: RecordReference
@@ -0,0 +1,23 @@
1
+ # Generated from lib/paper_trail_diff/version_boundary.rb with RBS::Inline
2
+
3
+ module PaperTrailDiff
4
+ # Places one version relative to another on the recorded timeline.
5
+ module VersionBoundary
6
+ # Whether `candidate` was recorded at or after `boundary`. Versions sharing
7
+ # a timestamp are ordered by id, which reflects insertion order only for
8
+ # sequential ids in one version table; any other tie cannot be ordered, and
9
+ # guessing would place a change on an arbitrary side of the boundary. A
10
+ # bare instant has no position among same-time versions, so it keeps them.
11
+ # : (untyped, untyped) -> bool
12
+ def self?.at_or_after?: (untyped, untyped) -> bool
13
+
14
+ # : (untyped) -> bool
15
+ def self?.version_record?: (untyped) -> bool
16
+
17
+ # : (untyped, untyped) -> bool
18
+ def self?.same_version?: (untyped, untyped) -> bool
19
+
20
+ # : (untyped, untyped) -> bool
21
+ def self?.orderable_tie?: (untyped, untyped) -> bool
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ # Generated from lib/paper_trail_diff/version_query.rb with RBS::Inline
2
+
3
+ module PaperTrailDiff
4
+ # Arel conditions shared by the version queries that need one row per item.
5
+ module VersionQuery
6
+ # Keeps each item's earliest version among those the block also accepts:
7
+ # no other version of the same item that the block accepts is earlier. A
8
+ # correlated NOT EXISTS stays portable where window functions are not.
9
+ # : (untyped) ?{ (untyped) -> untyped } -> untyped
10
+ def self?.no_earlier_version: (untyped) ?{ (untyped) -> untyped } -> untyped
11
+
12
+ # : (untyped, untyped) ?{ (untyped) -> untyped } -> untyped
13
+ def self?.earlier_version: (untyped, untyped) ?{ (untyped) -> untyped } -> untyped
14
+ end
15
+ end
@@ -6,18 +6,15 @@ module PaperTrailDiff
6
6
  # : (untyped, from: untyped, to: untyped, ?version_scope: untyped) -> void
7
7
  def initialize: (untyped, from: untyped, to: untyped, ?version_scope: untyped) -> void
8
8
 
9
- # Same selection as `select`, but keeping the step pairs a filter implies.
9
+ # Keep both reconstruction boundaries and the step pairs a filter implies.
10
10
  # : () -> RootVersionPlan
11
11
  def select_plan_for_range: () -> RootVersionPlan
12
12
 
13
13
  # : () -> untyped
14
14
  def validated_relation: () -> untyped
15
15
 
16
- # : () -> Array[untyped]
17
- def select: () -> Array[untyped]
18
-
19
- # : () -> Array[untyped]
20
- def select_through_latest: () -> Array[untyped]
16
+ # : (untyped) -> RootVersionPlan
17
+ def select_plan_through_current: (untyped) -> RootVersionPlan
21
18
 
22
19
  private
23
20
 
@@ -29,14 +26,8 @@ module PaperTrailDiff
29
26
 
30
27
  @version_scope: untyped
31
28
 
32
- # : () -> untyped
33
- def versions_relation: () -> untyped
34
-
35
- # : (untyped, ?through: untyped) -> Array[untyped]
36
- def select_relation: (untyped, ?through: untyped) -> Array[untyped]
37
-
38
- # : (untyped, ?through: untyped) -> RootVersionPlan
39
- def select_plan: (untyped, ?through: untyped) -> RootVersionPlan
29
+ # : (untyped, ?through: untyped, ?live_endpoint: untyped) -> RootVersionPlan
30
+ def select_plan: (untyped, ?through: untyped, ?live_endpoint: untyped) -> RootVersionPlan
40
31
 
41
32
  # : (Array[untyped]) -> Array[untyped]
42
33
  def ordered: (Array[untyped]) -> Array[untyped]
@@ -13,42 +13,39 @@ module PaperTrailDiff
13
13
  def self.compare: (untyped, untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> Diff
14
14
 
15
15
  # Compares many independent endpoint pairs while batching current-record loading.
16
- # : (Array[comparison_input], ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> comparison_results
17
- def self.compare_many: (Array[comparison_input], ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> comparison_results
16
+ # : (Array[comparison_input], ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?allow_expensive: bool) -> comparison_results
17
+ def self.compare_many: (Array[comparison_input], ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?allow_expensive: bool) -> comparison_results
18
18
 
19
- # Compares every adjacent reconstructed state in an inclusive version range.
19
+ # Compares every adjacent reconstructed state in an inclusive version range,
20
+ # by default the record's whole history through its current state.
20
21
  # : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?version_scope: untyped, ?close_on: Symbol?) -> Array[Step]
21
22
  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
23
 
23
24
  # Compares adjacent root and selected-descendant activity boundaries.
24
- # `reload_live_endpoints:` applies only when `to:` is a current record; the
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, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: 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, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool) -> Array[ActivityStep]
25
+ # `reload_live_endpoints:` applies whenever the range ends at the current
26
+ # record: an explicit `to:` record, the default range, or a window with no
27
+ # later version.
28
+ # : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Array[ActivityStep]
29
+ def self.activity_timeline: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Array[ActivityStep]
28
30
 
29
31
  # Builds an endpoint diff and root-checkpoint timeline while normalizing each version once.
30
32
  # : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Analysis
31
33
  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?, ?snapshots: bool, ?group: Symbol?) -> Analysis
32
34
 
33
- # Analyzes many roots over one shared time window, preparing their selected
34
- # history once for the batch instead of once per record. Roots with no
35
- # versions in the window return an empty `Analysis`.
36
- #
37
- # Pass `records` to analyze a list you assembled, which returns a Hash keyed
38
- # by identity. Pass `scope:` with a `limit:` to have the roots selected for
39
- # you from a relation, which returns a `ScopedAnalysis` -- the same Hash,
40
- # plus the roots the relation could not reach. See `analyze_scope` for why
41
- # that second collection exists.
42
- # : (?Array[untyped]?, ?scope: untyped, ?limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> (Hash[identity, Analysis] | ScopedAnalysis)
43
- def self.analyze_many: (?Array[untyped]?, ?scope: untyped, ?limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> (Hash[identity, Analysis] | ScopedAnalysis)
35
+ # Analyzes an array of records over one shared range, preparing their
36
+ # selected history once for the batch instead of once per record, and
37
+ # returns a frozen Hash keyed by identity. Roots with nothing to report in
38
+ # the window return an empty `Analysis`. To let a relation choose the
39
+ # records, use `analyze_scope`.
40
+ # : (Array[untyped], ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?allow_expensive: bool) -> Hash[identity, Analysis]
41
+ def self.analyze_many: (Array[untyped], ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?allow_expensive: bool) -> Hash[identity, Analysis]
44
42
 
45
43
  # Analyzes every root the relation reaches whose history moved inside the
46
- # window, selecting them in a fixed number of queries rather than making the
47
- # caller rediscover them.
44
+ # window. Joined duplicates are read in bounded pages and analyzed once.
48
45
  #
49
46
  # `limit:` is required and exceeding it raises. Selection moves into the gem
50
- # here, so the bound on how much work a page can ask for has to move with
51
- # it, and a truncated audit report is worse than a refused one.
47
+ # here. The relation's own limit/offset select the live page; this separate
48
+ # ceiling rejects too many unique roots rather than truncating that page.
52
49
  #
53
50
  # Returns a `ScopedAnalysis`, which destructures:
54
51
  #
@@ -57,7 +54,8 @@ module PaperTrailDiff
57
54
  # )
58
55
  #
59
56
  # `unreachable` names roots that changed in the window but have no live row
60
- # left. A relation's conditions are evaluated against the live table, so a
57
+ # left, independently of the live page's pagination and safety ceiling.
58
+ # A relation's conditions are evaluated against the live table, so a
61
59
  # destroyed root cannot be tested against them at all -- its history is
62
60
  # intact and the state it held at destruction may well have matched. Those
63
61
  # roots are reported rather than dropped so that a page auditing deletions
@@ -66,8 +64,14 @@ module PaperTrailDiff
66
64
  # Note also that a relation selects on current state, not on state during
67
65
  # the window: `where(status: 'published')` means published *now*, which is a
68
66
  # different set from what was published while the window was open.
69
- # : (untyped, limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> ScopedAnalysis
70
- def self.analyze_scope: (untyped, limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> ScopedAnalysis
67
+ # : (untyped, limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?historical_filter: untyped, ?max_filter_versions: Integer, ?include_deleted: bool, ?allow_expensive: bool) -> ScopedAnalysis
68
+ def self.analyze_scope: (untyped, limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?, ?historical_filter: untyped, ?max_filter_versions: Integer, ?include_deleted: bool, ?allow_expensive: bool) -> ScopedAnalysis
69
+
70
+ # Recent root and selected-child activity through the current saved state.
71
+ # Historical windows remain explicit on analyze_scope; this entry point is
72
+ # deliberately named for a report through now rather than an as-of query.
73
+ # : (untyped, since: untyped, limit: Integer, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?snapshots: bool, ?group: Symbol?, ?include_deleted: bool, ?historical_filter: untyped, ?max_filter_versions: Integer, ?allow_expensive: bool) -> ScopedAnalysis
74
+ def self.report: (untyped, since: untyped, limit: Integer, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?snapshots: bool, ?group: Symbol?, ?include_deleted: bool, ?historical_filter: untyped, ?max_filter_versions: Integer, ?allow_expensive: bool) -> ScopedAnalysis
71
75
 
72
76
  # Returns the association macros this release can normalize.
73
77
  # : () -> Array[Symbol]
@@ -99,8 +103,8 @@ module PaperTrailDiff
99
103
  # list that merely shifted would otherwise look changed throughout. A key
100
104
  # that was absent reads as `NestedComparator::ABSENT` rather than nil, which
101
105
  # JSON uses for a present null.
102
- # : (untyped, ?untyped) -> Hash[Array[String], ValueChange]
103
- def self.nested_changes: (untyped, ?untyped) -> Hash[Array[String], ValueChange]
106
+ # : (untyped, ?untyped) -> nested_changes
107
+ def self.nested_changes: (untyped, ?untyped) -> nested_changes
104
108
  end
105
109
 
106
110
  module PaperTrailDiff
@@ -114,6 +118,10 @@ module PaperTrailDiff
114
118
 
115
119
  type association_diffs = Hash[String, association_diff]
116
120
 
121
+ type nested_change = ValueChange | ArrayChange
122
+
123
+ type nested_changes = Hash[Array[String], nested_change]
124
+
117
125
  type association_snapshots = Hash[String, AssociationSnapshot]
118
126
 
119
127
  type identity = Array[untyped]
@@ -122,6 +130,8 @@ module PaperTrailDiff
122
130
 
123
131
  type comparison_results = Hash[identity, Diff]
124
132
 
133
+ type report_options = { within: untyped, activity: bool, version_scope: untyped, close_on: Symbol?, snapshots: bool, group: Symbol? }
134
+
125
135
  type ignore_option = Array[String | Symbol] | Hash[String | Symbol, untyped]
126
136
 
127
137
  type reference_key = :type | :id | "type" | "id"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paper_trail_diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Williams
@@ -42,6 +42,10 @@ files:
42
42
  - LICENSE
43
43
  - QUICKSTART.md
44
44
  - README.md
45
+ - docs/architecture.md
46
+ - docs/performance.md
47
+ - docs/reference.md
48
+ - docs/testing.md
45
49
  - lib/paper_trail_diff.rb
46
50
  - lib/paper_trail_diff/activity_belongs_to_event_applier.rb
47
51
  - lib/paper_trail_diff/activity_boundary.rb
@@ -72,6 +76,7 @@ files:
72
76
  - lib/paper_trail_diff/association_discovery.rb
73
77
  - lib/paper_trail_diff/association_traversal.rb
74
78
  - lib/paper_trail_diff/batch_boundary_resolver.rb
79
+ - lib/paper_trail_diff/batch_safety.rb
75
80
  - lib/paper_trail_diff/batched_root_analyzer.rb
76
81
  - lib/paper_trail_diff/batched_root_versions.rb
77
82
  - lib/paper_trail_diff/branch_snapshot_refresher.rb
@@ -80,11 +85,14 @@ files:
80
85
  - lib/paper_trail_diff/collection_transition.rb
81
86
  - lib/paper_trail_diff/comparison_batch.rb
82
87
  - lib/paper_trail_diff/configuration.rb
88
+ - lib/paper_trail_diff/deleted_analysis_batch.rb
83
89
  - lib/paper_trail_diff/diagnostics.rb
90
+ - lib/paper_trail_diff/direct_record_state_loader.rb
84
91
  - lib/paper_trail_diff/endpoint.rb
85
92
  - lib/paper_trail_diff/engine.rb
86
93
  - lib/paper_trail_diff/errors.rb
87
94
  - lib/paper_trail_diff/historical_association_reifier.rb
95
+ - lib/paper_trail_diff/historical_filter.rb
88
96
  - lib/paper_trail_diff/historical_snapshot_store.rb
89
97
  - lib/paper_trail_diff/instrumentation.rb
90
98
  - lib/paper_trail_diff/live_association_reader.rb
@@ -94,19 +102,24 @@ files:
94
102
  - lib/paper_trail_diff/nested_comparator.rb
95
103
  - lib/paper_trail_diff/paper_trail_adapter.rb
96
104
  - lib/paper_trail_diff/preloaded_endpoint_batch_loader.rb
105
+ - lib/paper_trail_diff/prepared_activity_index.rb
97
106
  - lib/paper_trail_diff/prepared_association_reifier.rb
98
107
  - lib/paper_trail_diff/prepared_edge_loader.rb
99
108
  - lib/paper_trail_diff/prepared_history.rb
100
109
  - lib/paper_trail_diff/prepared_history_loader.rb
101
110
  - lib/paper_trail_diff/prepared_record_index.rb
111
+ - lib/paper_trail_diff/prepared_snapshot_builder.rb
112
+ - lib/paper_trail_diff/prepared_state_association_reader.rb
102
113
  - lib/paper_trail_diff/root_version_plan.rb
103
114
  - lib/paper_trail_diff/root_version_selection.rb
104
115
  - lib/paper_trail_diff/scoped_analysis.rb
116
+ - lib/paper_trail_diff/scoped_analysis_builder.rb
105
117
  - lib/paper_trail_diff/scoped_root_selection.rb
106
118
  - lib/paper_trail_diff/snapshot.rb
107
119
  - lib/paper_trail_diff/snapshot_normalizer.rb
108
120
  - lib/paper_trail_diff/snapshot_traversal.rb
109
121
  - lib/paper_trail_diff/step.rb
122
+ - lib/paper_trail_diff/stored_time_precision.rb
110
123
  - lib/paper_trail_diff/support.rb
111
124
  - lib/paper_trail_diff/time_activity_timeline_builder.rb
112
125
  - lib/paper_trail_diff/time_range.rb
@@ -121,6 +134,8 @@ files:
121
134
  - lib/paper_trail_diff/value_objects.rb
122
135
  - lib/paper_trail_diff/version.rb
123
136
  - lib/paper_trail_diff/version_association_candidate_scope.rb
137
+ - lib/paper_trail_diff/version_boundary.rb
138
+ - lib/paper_trail_diff/version_query.rb
124
139
  - lib/paper_trail_diff/version_range.rb
125
140
  - lib/paper_trail_diff/version_scope_filter.rb
126
141
  - lib/paper_trail_diff/version_sequence_diagnostics.rb
@@ -154,6 +169,7 @@ files:
154
169
  - sig/generated/paper_trail_diff/association_discovery.rbs
155
170
  - sig/generated/paper_trail_diff/association_traversal.rbs
156
171
  - sig/generated/paper_trail_diff/batch_boundary_resolver.rbs
172
+ - sig/generated/paper_trail_diff/batch_safety.rbs
157
173
  - sig/generated/paper_trail_diff/batched_root_analyzer.rbs
158
174
  - sig/generated/paper_trail_diff/batched_root_versions.rbs
159
175
  - sig/generated/paper_trail_diff/branch_snapshot_refresher.rbs
@@ -162,11 +178,14 @@ files:
162
178
  - sig/generated/paper_trail_diff/collection_transition.rbs
163
179
  - sig/generated/paper_trail_diff/comparison_batch.rbs
164
180
  - sig/generated/paper_trail_diff/configuration.rbs
181
+ - sig/generated/paper_trail_diff/deleted_analysis_batch.rbs
165
182
  - sig/generated/paper_trail_diff/diagnostics.rbs
183
+ - sig/generated/paper_trail_diff/direct_record_state_loader.rbs
166
184
  - sig/generated/paper_trail_diff/endpoint.rbs
167
185
  - sig/generated/paper_trail_diff/engine.rbs
168
186
  - sig/generated/paper_trail_diff/errors.rbs
169
187
  - sig/generated/paper_trail_diff/historical_association_reifier.rbs
188
+ - sig/generated/paper_trail_diff/historical_filter.rbs
170
189
  - sig/generated/paper_trail_diff/historical_snapshot_store.rbs
171
190
  - sig/generated/paper_trail_diff/instrumentation.rbs
172
191
  - sig/generated/paper_trail_diff/live_association_reader.rbs
@@ -176,19 +195,24 @@ files:
176
195
  - sig/generated/paper_trail_diff/nested_comparator.rbs
177
196
  - sig/generated/paper_trail_diff/paper_trail_adapter.rbs
178
197
  - sig/generated/paper_trail_diff/preloaded_endpoint_batch_loader.rbs
198
+ - sig/generated/paper_trail_diff/prepared_activity_index.rbs
179
199
  - sig/generated/paper_trail_diff/prepared_association_reifier.rbs
180
200
  - sig/generated/paper_trail_diff/prepared_edge_loader.rbs
181
201
  - sig/generated/paper_trail_diff/prepared_history.rbs
182
202
  - sig/generated/paper_trail_diff/prepared_history_loader.rbs
183
203
  - sig/generated/paper_trail_diff/prepared_record_index.rbs
204
+ - sig/generated/paper_trail_diff/prepared_snapshot_builder.rbs
205
+ - sig/generated/paper_trail_diff/prepared_state_association_reader.rbs
184
206
  - sig/generated/paper_trail_diff/root_version_plan.rbs
185
207
  - sig/generated/paper_trail_diff/root_version_selection.rbs
186
208
  - sig/generated/paper_trail_diff/scoped_analysis.rbs
209
+ - sig/generated/paper_trail_diff/scoped_analysis_builder.rbs
187
210
  - sig/generated/paper_trail_diff/scoped_root_selection.rbs
188
211
  - sig/generated/paper_trail_diff/snapshot.rbs
189
212
  - sig/generated/paper_trail_diff/snapshot_normalizer.rbs
190
213
  - sig/generated/paper_trail_diff/snapshot_traversal.rbs
191
214
  - sig/generated/paper_trail_diff/step.rbs
215
+ - sig/generated/paper_trail_diff/stored_time_precision.rbs
192
216
  - sig/generated/paper_trail_diff/support.rbs
193
217
  - sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs
194
218
  - sig/generated/paper_trail_diff/time_range.rbs
@@ -203,6 +227,8 @@ files:
203
227
  - sig/generated/paper_trail_diff/value_objects.rbs
204
228
  - sig/generated/paper_trail_diff/version.rbs
205
229
  - sig/generated/paper_trail_diff/version_association_candidate_scope.rbs
230
+ - sig/generated/paper_trail_diff/version_boundary.rbs
231
+ - sig/generated/paper_trail_diff/version_query.rbs
206
232
  - sig/generated/paper_trail_diff/version_range.rbs
207
233
  - sig/generated/paper_trail_diff/version_scope_filter.rbs
208
234
  - sig/generated/paper_trail_diff/version_sequence_diagnostics.rbs
@@ -212,11 +238,11 @@ licenses:
212
238
  metadata:
213
239
  allowed_push_host: https://rubygems.org
214
240
  bug_tracker_uri: https://github.com/aheathwilliams/paper_trail_diff/issues
215
- changelog_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.11.0/CHANGELOG.md
216
- documentation_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.11.0/README.md
241
+ changelog_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.13.0/CHANGELOG.md
242
+ documentation_uri: https://github.com/aheathwilliams/paper_trail_diff/blob/v0.13.0/README.md
217
243
  homepage_uri: https://github.com/aheathwilliams/paper_trail_diff
218
244
  rubygems_mfa_required: 'true'
219
- source_code_uri: https://github.com/aheathwilliams/paper_trail_diff/tree/v0.11.0
245
+ source_code_uri: https://github.com/aheathwilliams/paper_trail_diff/tree/v0.13.0
220
246
  rdoc_options: []
221
247
  require_paths:
222
248
  - lib
@@ -231,7 +257,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
231
257
  - !ruby/object:Gem::Version
232
258
  version: '0'
233
259
  requirements: []
234
- rubygems_version: 4.0.16
260
+ rubygems_version: 4.0.20
235
261
  specification_version: 4
236
262
  summary: Structured endpoint and activity diffs for PaperTrail
237
263
  test_files: []