paper_trail_diff 0.1.0 → 0.3.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -0
  3. data/QUICKSTART.md +302 -0
  4. data/README.md +258 -19
  5. data/lib/paper_trail_diff/activity_boundary.rb +34 -3
  6. data/lib/paper_trail_diff/activity_child_candidate_loader.rb +73 -0
  7. data/lib/paper_trail_diff/activity_event_snapshot_refresher.rb +261 -45
  8. data/lib/paper_trail_diff/activity_history.rb +136 -0
  9. data/lib/paper_trail_diff/activity_range.rb +28 -12
  10. data/lib/paper_trail_diff/activity_snapshot_delta.rb +64 -0
  11. data/lib/paper_trail_diff/activity_snapshot_sequence.rb +3 -3
  12. data/lib/paper_trail_diff/activity_timeline_builder.rb +54 -45
  13. data/lib/paper_trail_diff/activity_version_collector.rb +14 -17
  14. data/lib/paper_trail_diff/association_diff_traversal.rb +184 -0
  15. data/lib/paper_trail_diff/branch_snapshot_refresher.rb +5 -3
  16. data/lib/paper_trail_diff/collection_comparator.rb +155 -0
  17. data/lib/paper_trail_diff/collection_identity_index.rb +53 -0
  18. data/lib/paper_trail_diff/collection_transition.rb +25 -0
  19. data/lib/paper_trail_diff/comparison_batch.rb +151 -0
  20. data/lib/paper_trail_diff/engine.rb +5 -45
  21. data/lib/paper_trail_diff/errors.rb +9 -0
  22. data/lib/paper_trail_diff/historical_snapshot_store.rb +55 -3
  23. data/lib/paper_trail_diff/instrumentation.rb +30 -0
  24. data/lib/paper_trail_diff/live_endpoint_batch_loader.rb +133 -0
  25. data/lib/paper_trail_diff/live_endpoint_provider.rb +48 -0
  26. data/lib/paper_trail_diff/live_graph_collector.rb +52 -0
  27. data/lib/paper_trail_diff/paper_trail_adapter.rb +61 -34
  28. data/lib/paper_trail_diff/preloaded_endpoint_batch_loader.rb +48 -0
  29. data/lib/paper_trail_diff/prepared_edge_loader.rb +54 -17
  30. data/lib/paper_trail_diff/prepared_history.rb +14 -4
  31. data/lib/paper_trail_diff/prepared_history_loader.rb +17 -9
  32. data/lib/paper_trail_diff/prepared_record_index.rb +138 -10
  33. data/lib/paper_trail_diff/snapshot.rb +66 -3
  34. data/lib/paper_trail_diff/snapshot_traversal.rb +97 -0
  35. data/lib/paper_trail_diff/step.rb +9 -0
  36. data/lib/paper_trail_diff/support.rb +18 -0
  37. data/lib/paper_trail_diff/time_activity_timeline_builder.rb +111 -0
  38. data/lib/paper_trail_diff/time_range.rb +85 -0
  39. data/lib/paper_trail_diff/time_version_range.rb +53 -0
  40. data/lib/paper_trail_diff/timeline_builder.rb +13 -7
  41. data/lib/paper_trail_diff/timeline_range.rb +72 -0
  42. data/lib/paper_trail_diff/traversal.rb +80 -0
  43. data/lib/paper_trail_diff/traversal_emitter.rb +42 -0
  44. data/lib/paper_trail_diff/traversal_entry.rb +94 -0
  45. data/lib/paper_trail_diff/version.rb +1 -1
  46. data/lib/paper_trail_diff/version_association_candidate_scope.rb +73 -0
  47. data/lib/paper_trail_diff.rb +84 -13
  48. data/sig/generated/paper_trail_diff/activity_boundary.rbs +17 -2
  49. data/sig/generated/paper_trail_diff/activity_child_candidate_loader.rbs +40 -0
  50. data/sig/generated/paper_trail_diff/activity_event_snapshot_refresher.rbs +49 -8
  51. data/sig/generated/paper_trail_diff/activity_history.rbs +79 -0
  52. data/sig/generated/paper_trail_diff/activity_range.rbs +14 -5
  53. data/sig/generated/paper_trail_diff/activity_snapshot_delta.rbs +33 -0
  54. data/sig/generated/paper_trail_diff/activity_snapshot_sequence.rbs +2 -2
  55. data/sig/generated/paper_trail_diff/activity_timeline_builder.rbs +15 -10
  56. data/sig/generated/paper_trail_diff/activity_version_collector.rbs +2 -5
  57. data/sig/generated/paper_trail_diff/association_diff_traversal.rbs +58 -0
  58. data/sig/generated/paper_trail_diff/branch_snapshot_refresher.rbs +2 -2
  59. data/sig/generated/paper_trail_diff/collection_comparator.rbs +59 -0
  60. data/sig/generated/paper_trail_diff/collection_identity_index.rbs +29 -0
  61. data/sig/generated/paper_trail_diff/collection_transition.rbs +18 -0
  62. data/sig/generated/paper_trail_diff/comparison_batch.rbs +65 -0
  63. data/sig/generated/paper_trail_diff/engine.rbs +0 -12
  64. data/sig/generated/paper_trail_diff/errors.rbs +12 -0
  65. data/sig/generated/paper_trail_diff/historical_snapshot_store.rbs +22 -2
  66. data/sig/generated/paper_trail_diff/instrumentation.rbs +12 -0
  67. data/sig/generated/paper_trail_diff/live_endpoint_batch_loader.rbs +52 -0
  68. data/sig/generated/paper_trail_diff/live_endpoint_provider.rbs +23 -0
  69. data/sig/generated/paper_trail_diff/live_graph_collector.rbs +27 -0
  70. data/sig/generated/paper_trail_diff/paper_trail_adapter.rbs +21 -10
  71. data/sig/generated/paper_trail_diff/preloaded_endpoint_batch_loader.rbs +24 -0
  72. data/sig/generated/paper_trail_diff/prepared_edge_loader.rbs +22 -8
  73. data/sig/generated/paper_trail_diff/prepared_history.rbs +6 -0
  74. data/sig/generated/paper_trail_diff/prepared_history_loader.rbs +4 -2
  75. data/sig/generated/paper_trail_diff/prepared_record_index.rbs +61 -6
  76. data/sig/generated/paper_trail_diff/snapshot.rbs +37 -2
  77. data/sig/generated/paper_trail_diff/snapshot_traversal.rbs +29 -0
  78. data/sig/generated/paper_trail_diff/step.rbs +7 -0
  79. data/sig/generated/paper_trail_diff/support.rbs +6 -0
  80. data/sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs +46 -0
  81. data/sig/generated/paper_trail_diff/time_range.rbs +41 -0
  82. data/sig/generated/paper_trail_diff/time_version_range.rbs +27 -0
  83. data/sig/generated/paper_trail_diff/timeline_builder.rbs +6 -5
  84. data/sig/generated/paper_trail_diff/timeline_range.rbs +41 -0
  85. data/sig/generated/paper_trail_diff/traversal.rbs +41 -0
  86. data/sig/generated/paper_trail_diff/traversal_emitter.rbs +16 -0
  87. data/sig/generated/paper_trail_diff/traversal_entry.rbs +52 -0
  88. data/sig/generated/paper_trail_diff/version_association_candidate_scope.rbs +32 -0
  89. data/sig/generated/paper_trail_diff.rbs +24 -8
  90. metadata +49 -4
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module PaperTrailDiff
5
+ # Shared entry construction for internal traversal collaborators.
6
+ class TraversalEmitter
7
+ #: (^(TraversalEntry) -> void) -> void
8
+ def initialize(receiver)
9
+ @receiver = receiver
10
+ end
11
+
12
+ private
13
+
14
+ # @rbs @receiver: ^(TraversalEntry) -> void
15
+
16
+ #: (kind: Symbol, context: traversal_context, association_path: traversal_association_path, record_path: traversal_record_path, value: untyped, ?association_kind: Symbol?, ?state: traversal_state?, ?attribute: String?) -> void
17
+ def emit( # rubocop:disable Metrics/ParameterLists
18
+ kind:,
19
+ context:,
20
+ association_path:,
21
+ record_path:,
22
+ value:,
23
+ association_kind: nil,
24
+ state: nil,
25
+ attribute: nil
26
+ )
27
+ @receiver.call(
28
+ TraversalEntry.new(
29
+ kind: kind,
30
+ context: context,
31
+ association_path: association_path,
32
+ record_path: record_path,
33
+ association_kind: association_kind,
34
+ state: state,
35
+ attribute: attribute,
36
+ value: value
37
+ )
38
+ )
39
+ end
40
+ end
41
+ private_constant :TraversalEmitter
42
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module PaperTrailDiff
5
+ # One immutable location and value emitted while walking a Diff tree.
6
+ class TraversalEntry
7
+ CONTEXTS = %i[change included_state].freeze
8
+ STATES = %i[before after].freeze
9
+ private_constant :CONTEXTS, :STATES
10
+
11
+ attr_reader :kind #: Symbol
12
+ attr_reader :context #: traversal_context
13
+ attr_reader :association_path #: traversal_association_path
14
+ attr_reader :record_path #: traversal_record_path
15
+ attr_reader :association_kind #: Symbol?
16
+ attr_reader :state #: traversal_state?
17
+ attr_reader :attribute #: String?
18
+ attr_reader :value #: untyped
19
+
20
+ #: (kind: Symbol, context: traversal_context, ?association_path: traversal_association_path, ?record_path: traversal_record_path, ?association_kind: Symbol?, ?state: traversal_state?, ?attribute: String?, ?value: untyped) -> void
21
+ def initialize( # rubocop:disable Metrics/ParameterLists
22
+ kind:,
23
+ context:,
24
+ association_path: [],
25
+ record_path: [],
26
+ association_kind: nil,
27
+ state: nil,
28
+ attribute: nil,
29
+ value: nil
30
+ )
31
+ validate!(context, state)
32
+ @kind = kind
33
+ @context = context
34
+ @association_path = immutable_names(association_path)
35
+ @record_path = Support.immutable_copy(record_path)
36
+ @association_kind = association_kind
37
+ @state = state
38
+ @attribute = Support.immutable_copy(attribute&.to_s)
39
+ @value = Support.immutable_copy(value)
40
+ freeze
41
+ end
42
+
43
+ #: () -> bool
44
+ def change?
45
+ context == :change
46
+ end
47
+
48
+ #: () -> bool
49
+ def included_state?
50
+ context == :included_state
51
+ end
52
+
53
+ #: () -> String?
54
+ def association
55
+ association_path.last
56
+ end
57
+
58
+ #: () -> RecordReference?
59
+ def record
60
+ record_path.last
61
+ end
62
+
63
+ #: () -> Hash[Symbol, untyped]
64
+ def to_h
65
+ {
66
+ kind: kind,
67
+ context: context,
68
+ association_path: Support.serialize(association_path),
69
+ record_path: Support.serialize(record_path),
70
+ association_kind: association_kind,
71
+ state: state,
72
+ attribute: attribute,
73
+ value: Support.serialize(value)
74
+ }
75
+ end
76
+
77
+ private
78
+
79
+ #: (Symbol, Symbol?) -> void
80
+ def validate!(candidate_context, candidate_state)
81
+ unless CONTEXTS.include?(candidate_context)
82
+ raise ArgumentError, "unsupported traversal context: #{candidate_context.inspect}"
83
+ end
84
+ return if candidate_state.nil? || STATES.include?(candidate_state)
85
+
86
+ raise ArgumentError, "unsupported traversal state: #{candidate_state.inspect}"
87
+ end
88
+
89
+ #: (traversal_association_path) -> traversal_association_path
90
+ def immutable_names(names)
91
+ names.map { |name| Support.immutable_copy(name.to_s) }.freeze
92
+ end
93
+ end
94
+ end
@@ -2,5 +2,5 @@
2
2
  # rbs_inline: enabled
3
3
 
4
4
  module PaperTrailDiff
5
- VERSION = '0.1.0'
5
+ VERSION = '0.3.0'
6
6
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module PaperTrailDiff
5
+ # Selects association activity from a boundary forward plus the state at that boundary.
6
+ class VersionAssociationCandidateScope
7
+ #: (untyped, untyped, ?end_at: untyped) -> void
8
+ def initialize(version_class, start_at, end_at: nil)
9
+ @version_class = version_class
10
+ @start_at = start_at
11
+ @end_at = end_at
12
+ end
13
+
14
+ #: (untyped) -> untyped
15
+ def call(relation)
16
+ return relation unless @start_at
17
+
18
+ relation.where(candidate_condition)
19
+ end
20
+
21
+ private
22
+
23
+ # @rbs @version_class: untyped
24
+ # @rbs @start_at: untyped
25
+ # @rbs @end_at: untyped
26
+
27
+ #: () -> String
28
+ def candidate_condition # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
29
+ connection = @version_class.connection
30
+ outer = connection.quote_table_name(@version_class.table_name)
31
+ later = connection.quote_table_name('paper_trail_diff_later_versions')
32
+ start_at = connection.quote(@start_at)
33
+ destroyed = connection.quote('destroy')
34
+ activity = activity_condition(outer, start_at)
35
+ compact_sql(<<~SQL)
36
+ (
37
+ #{activity}
38
+ OR (
39
+ #{column(outer, 'created_at')} < #{start_at}
40
+ AND #{column(outer, 'event')} != #{destroyed}
41
+ AND NOT EXISTS (
42
+ SELECT 1
43
+ FROM #{outer} #{later}
44
+ WHERE #{column(later, 'item_type')} = #{column(outer, 'item_type')}
45
+ AND #{column(later, 'item_id')} = #{column(outer, 'item_id')}
46
+ AND #{column(later, 'created_at')} < #{start_at}
47
+ AND #{column(later, 'created_at')} > #{column(outer, 'created_at')}
48
+ )
49
+ )
50
+ )
51
+ SQL
52
+ end
53
+
54
+ #: (String, String) -> String
55
+ def activity_condition(table, start_at)
56
+ condition = "#{column(table, 'created_at')} >= #{start_at}"
57
+ return condition unless @end_at
58
+
59
+ end_at = @version_class.connection.quote(@end_at)
60
+ "(#{condition} AND #{column(table, 'created_at')} <= #{end_at})"
61
+ end
62
+
63
+ #: (String, String) -> String
64
+ def column(table, name)
65
+ "#{table}.#{@version_class.connection.quote_column_name(name)}"
66
+ end
67
+
68
+ #: (String) -> String
69
+ def compact_sql(sql)
70
+ sql.gsub(/\s+/, ' ').strip
71
+ end
72
+ end
73
+ end
@@ -5,37 +5,59 @@ require 'paper_trail'
5
5
 
6
6
  require_relative 'paper_trail_diff/version'
7
7
  require_relative 'paper_trail_diff/support'
8
+ require_relative 'paper_trail_diff/instrumentation'
8
9
  require_relative 'paper_trail_diff/errors'
9
10
  require_relative 'paper_trail_diff/configuration'
10
11
  require_relative 'paper_trail_diff/endpoint'
11
12
  require_relative 'paper_trail_diff/association_traversal'
12
13
  require_relative 'paper_trail_diff/association_discovery'
13
14
  require_relative 'paper_trail_diff/diagnostics'
15
+ require_relative 'paper_trail_diff/collection_identity_index'
16
+ require_relative 'paper_trail_diff/collection_transition'
14
17
  require_relative 'paper_trail_diff/snapshot'
18
+ require_relative 'paper_trail_diff/activity_snapshot_delta'
15
19
  require_relative 'paper_trail_diff/value_objects'
20
+ require_relative 'paper_trail_diff/traversal_entry'
21
+ require_relative 'paper_trail_diff/traversal_emitter'
22
+ require_relative 'paper_trail_diff/snapshot_traversal'
23
+ require_relative 'paper_trail_diff/association_diff_traversal'
24
+ require_relative 'paper_trail_diff/traversal'
25
+ require_relative 'paper_trail_diff/collection_comparator'
16
26
  require_relative 'paper_trail_diff/engine'
17
27
  require_relative 'paper_trail_diff/historical_association_reifier'
18
28
  require_relative 'paper_trail_diff/prepared_record_index'
19
29
  require_relative 'paper_trail_diff/prepared_history'
30
+ require_relative 'paper_trail_diff/version_association_candidate_scope'
20
31
  require_relative 'paper_trail_diff/prepared_edge_loader'
21
32
  require_relative 'paper_trail_diff/prepared_history_loader'
22
33
  require_relative 'paper_trail_diff/prepared_association_reifier'
23
34
  require_relative 'paper_trail_diff/live_association_reader'
35
+ require_relative 'paper_trail_diff/live_endpoint_batch_loader'
36
+ require_relative 'paper_trail_diff/preloaded_endpoint_batch_loader'
37
+ require_relative 'paper_trail_diff/live_endpoint_provider'
38
+ require_relative 'paper_trail_diff/live_graph_collector'
39
+ require_relative 'paper_trail_diff/comparison_batch'
24
40
  require_relative 'paper_trail_diff/snapshot_normalizer'
25
41
  require_relative 'paper_trail_diff/historical_snapshot_store'
26
42
  require_relative 'paper_trail_diff/timeline_snapshot_provider'
27
43
  require_relative 'paper_trail_diff/activity_event_snapshot_refresher'
28
44
  require_relative 'paper_trail_diff/activity_root_snapshot_refresher'
29
45
  require_relative 'paper_trail_diff/branch_snapshot_refresher'
30
- require_relative 'paper_trail_diff/step'
31
46
  require_relative 'paper_trail_diff/activity_boundary'
47
+ require_relative 'paper_trail_diff/step'
32
48
  require_relative 'paper_trail_diff/analysis'
33
49
  require_relative 'paper_trail_diff/version_range'
50
+ require_relative 'paper_trail_diff/time_range'
51
+ require_relative 'paper_trail_diff/time_version_range'
52
+ require_relative 'paper_trail_diff/timeline_range'
34
53
  require_relative 'paper_trail_diff/timeline_builder'
35
54
  require_relative 'paper_trail_diff/activity_range'
36
55
  require_relative 'paper_trail_diff/activity_event'
56
+ require_relative 'paper_trail_diff/activity_child_candidate_loader'
37
57
  require_relative 'paper_trail_diff/activity_version_collector'
38
58
  require_relative 'paper_trail_diff/activity_snapshot_sequence'
59
+ require_relative 'paper_trail_diff/activity_history'
60
+ require_relative 'paper_trail_diff/time_activity_timeline_builder'
39
61
  require_relative 'paper_trail_diff/activity_timeline_builder'
40
62
  require_relative 'paper_trail_diff/paper_trail_adapter'
41
63
 
@@ -48,8 +70,14 @@ require_relative 'paper_trail_diff/paper_trail_adapter'
48
70
  # type association_diffs = Hash[String, association_diff]
49
71
  # type association_snapshots = Hash[String, AssociationSnapshot]
50
72
  # type identity = Array[untyped]
73
+ # type comparison_input = Hash[String | Symbol, untyped]
74
+ # type comparison_results = Hash[identity, Diff]
51
75
  # type ignore_option = Array[String | Symbol] | Hash[String | Symbol, untyped]
52
76
  # type reference_key = :type | :id | "type" | "id"
77
+ # type traversal_context = :change | :included_state
78
+ # type traversal_state = :before | :after
79
+ # type traversal_association_path = Array[String]
80
+ # type traversal_record_path = Array[RecordReference]
53
81
  # end
54
82
 
55
83
  # Structured version comparison for PaperTrail.
@@ -64,40 +92,82 @@ module PaperTrailDiff
64
92
 
65
93
  class << self
66
94
  # Compares net state between explicit PaperTrail-version or current-record endpoints.
67
- #: (untyped, untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option) -> Diff
68
- def compare(from_version, to_version, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES)
69
- PaperTrailAdapter.new(associations: associations, ignore: ignore).compare(
95
+ #: (untyped, untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> Diff
96
+ def compare(
97
+ from_version,
98
+ to_version,
99
+ associations: [],
100
+ ignore: DEFAULT_IGNORED_ATTRIBUTES,
101
+ reload_live_endpoints: true
102
+ )
103
+ PaperTrailAdapter.new(
104
+ associations: associations,
105
+ ignore: ignore,
106
+ reload_live_endpoints: reload_live_endpoints
107
+ ).compare(
70
108
  from_version,
71
109
  to_version
72
110
  )
73
111
  end
74
112
 
113
+ # Compares many independent endpoint pairs while batching current-record loading.
114
+ #: (Array[comparison_input], ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> comparison_results
115
+ def compare_many(
116
+ comparisons,
117
+ associations: [],
118
+ ignore: DEFAULT_IGNORED_ATTRIBUTES,
119
+ reload_live_endpoints: true
120
+ )
121
+ PaperTrailAdapter.new(
122
+ associations: associations,
123
+ ignore: ignore,
124
+ reload_live_endpoints: reload_live_endpoints
125
+ ).compare_many(comparisons)
126
+ end
127
+
75
128
  # Compares every adjacent reconstructed state in an inclusive version range.
76
- #: (untyped, from: untyped, to: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option) -> Array[Step]
77
- def timeline(record, from:, to:, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES)
129
+ #: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option) -> Array[Step]
130
+ def timeline( # rubocop:disable Metrics/ParameterLists
131
+ record,
132
+ from: nil,
133
+ to: nil,
134
+ within: nil,
135
+ associations: [],
136
+ ignore: DEFAULT_IGNORED_ATTRIBUTES
137
+ )
78
138
  PaperTrailAdapter.new(associations: associations, ignore: ignore).timeline(
79
139
  record,
80
140
  from: from,
81
- to: to
141
+ to: to,
142
+ within: within
82
143
  )
83
144
  end
84
145
 
85
146
  # Compares adjacent root and selected-descendant activity boundaries.
86
- #: (untyped, from: untyped, to: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option) -> Array[ActivityStep]
87
- def activity_timeline(record, from:, to:, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES)
147
+ #: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option) -> Array[ActivityStep]
148
+ def activity_timeline( # rubocop:disable Metrics/ParameterLists
149
+ record,
150
+ from: nil,
151
+ to: nil,
152
+ within: nil,
153
+ associations: [],
154
+ ignore: DEFAULT_IGNORED_ATTRIBUTES
155
+ )
88
156
  PaperTrailAdapter.new(associations: associations, ignore: ignore).activity_timeline(
89
157
  record,
90
158
  from: from,
91
- to: to
159
+ to: to,
160
+ within: within
92
161
  )
93
162
  end
94
163
 
95
164
  # Builds an endpoint diff and root-checkpoint timeline while normalizing each version once.
96
- #: (untyped, from: untyped, to: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool) -> Analysis
165
+ #: (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool) -> Analysis
97
166
  def analyze( # rubocop:disable Metrics/ParameterLists
98
167
  record,
99
- from:,
100
- to:,
168
+ from: nil,
169
+ to: nil,
170
+ within: nil,
101
171
  associations: [],
102
172
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
103
173
  activity: false
@@ -106,6 +176,7 @@ module PaperTrailDiff
106
176
  record,
107
177
  from: from,
108
178
  to: to,
179
+ within: within,
109
180
  activity: activity
110
181
  )
111
182
  end
@@ -13,14 +13,26 @@ module PaperTrailDiff
13
13
 
14
14
  attr_reader recorded_at: untyped
15
15
 
16
+ attr_reader event: String?
17
+
18
+ attr_reader whodunnit: untyped
19
+
20
+ attr_reader record: RecordReference
21
+
16
22
  # : (untyped) -> ActivityBoundary
17
23
  def self.from_version: (untyped) -> ActivityBoundary
18
24
 
19
25
  # : (untyped, captured_at: untyped) -> ActivityBoundary
20
26
  def self.current: (untyped, captured_at: untyped) -> ActivityBoundary
21
27
 
22
- # : (kind: Symbol, version_id: untyped, item_type: untyped, item_id: untyped, recorded_at: untyped) -> void
23
- def initialize: (kind: Symbol, version_id: untyped, item_type: untyped, item_id: untyped, recorded_at: untyped) -> void
28
+ # : (kind: Symbol, version_id: untyped, item_type: untyped, item_id: untyped, recorded_at: untyped, ?event: untyped, ?whodunnit: untyped) -> void
29
+ def initialize: (kind: Symbol, version_id: untyped, item_type: untyped, item_id: untyped, recorded_at: untyped, ?event: untyped, ?whodunnit: untyped) -> void
30
+
31
+ # : () -> bool
32
+ def version?: () -> bool
33
+
34
+ # : () -> bool
35
+ def current?: () -> bool
24
36
 
25
37
  # : () -> Hash[Symbol, untyped]
26
38
  def to_h: () -> Hash[Symbol, untyped]
@@ -37,6 +49,9 @@ module PaperTrailDiff
37
49
  # : (from_boundary: ActivityBoundary, to_boundary: ActivityBoundary, diff: Diff) -> void
38
50
  def initialize: (from_boundary: ActivityBoundary, to_boundary: ActivityBoundary, diff: Diff) -> void
39
51
 
52
+ # : () -> bool
53
+ def empty?: () -> bool
54
+
40
55
  # : () -> Hash[Symbol, untyped]
41
56
  def to_h: () -> Hash[Symbol, untyped]
42
57
  end
@@ -0,0 +1,40 @@
1
+ # Generated from lib/paper_trail_diff/activity_child_candidate_loader.rb with RBS::Inline
2
+
3
+ module PaperTrailDiff
4
+ # Loads direct-child identities active at or after one activity boundary.
5
+ class ActivityChildCandidateLoader
6
+ # : (untyped, Array[untyped], untyped, ActivityRange) -> void
7
+ def initialize: (untyped, Array[untyped], untyped, ActivityRange) -> void
8
+
9
+ # : () -> Array[untyped]
10
+ def call: () -> Array[untyped]
11
+
12
+ private
13
+
14
+ @parent_class: untyped
15
+
16
+ @parent_ids: Array[untyped]
17
+
18
+ @range: ActivityRange
19
+
20
+ @reflection: untyped
21
+
22
+ # : () -> Array[untyped]
23
+ def historical_ids: () -> Array[untyped]
24
+
25
+ # : (untyped) -> untyped
26
+ def historical_relation: (untyped) -> untyped
27
+
28
+ # : (untyped) -> VersionAssociationCandidateScope
29
+ def candidate_scope: (untyped) -> VersionAssociationCandidateScope
30
+
31
+ # : () -> Array[untyped]
32
+ def current_ids: () -> Array[untyped]
33
+
34
+ # : () -> String
35
+ def child_item_type: () -> String
36
+
37
+ # : () -> Array[String?]
38
+ def parent_types: () -> Array[String?]
39
+ end
40
+ end
@@ -6,8 +6,8 @@ module PaperTrailDiff
6
6
  class ActivityEventSnapshotRefresher
7
7
  RECORD_AFTER_HANDLERS: untyped
8
8
 
9
- # : (traversal: AssociationTraversal, pool: SnapshotPool, components: untyped, ?association_reader: untyped) -> void
10
- def initialize: (traversal: AssociationTraversal, pool: SnapshotPool, components: untyped, ?association_reader: untyped) -> void
9
+ # : (traversal: AssociationTraversal, pool: SnapshotPool, components: untyped, ?association_reader: untyped, ?record_transition: untyped) -> void
10
+ def initialize: (traversal: AssociationTraversal, pool: SnapshotPool, components: untyped, ?association_reader: untyped, ?record_transition: untyped) -> void
11
11
 
12
12
  # : (untyped, untyped, RecordSnapshot, Array[String], ActivityEvent?) -> [bool, RecordSnapshot?]
13
13
  def call: (untyped, untyped, RecordSnapshot, Array[String], ActivityEvent?) -> [ bool, RecordSnapshot? ]
@@ -20,13 +20,30 @@ module PaperTrailDiff
20
20
 
21
21
  @pool: SnapshotPool
22
22
 
23
+ @record_transition: untyped
24
+
23
25
  @traversal: AssociationTraversal
24
26
 
25
27
  # : (Array[Array[untyped]], untyped) -> bool
26
28
  def unsafe_through_membership_event?: (Array[Array[untyped]], untyped) -> bool
27
29
 
28
- # : (untyped, untyped, RecordSnapshot, SnapshotNormalizer, Array[Array[untyped]], untyped) -> [bool, RecordSnapshot]
29
- def collection_target_snapshot: (untyped, untyped, RecordSnapshot, SnapshotNormalizer, Array[Array[untyped]], untyped) -> [ bool, RecordSnapshot ]
30
+ # : (Array[Array[untyped]], ActivitySnapshotDelta?) -> bool
31
+ def unsafe_nested_membership_delta?: (Array[Array[untyped]], ActivitySnapshotDelta?) -> bool
32
+
33
+ # : (untyped, untyped, RecordSnapshot, SnapshotNormalizer, Array[Array[untyped]], untyped, ActivitySnapshotDelta?) -> [bool, RecordSnapshot]
34
+ def collection_target_snapshot: (untyped, untyped, RecordSnapshot, SnapshotNormalizer, Array[Array[untyped]], untyped, ActivitySnapshotDelta?) -> [ bool, RecordSnapshot ]
35
+
36
+ # : (RecordSnapshot, Array[untyped], untyped, ActivitySnapshotDelta?, Array[untyped], SnapshotNormalizer, untyped, untyped) -> [untyped, RecordSnapshot?]
37
+ def collection_route_event_state: (RecordSnapshot, Array[untyped], untyped, ActivitySnapshotDelta?, Array[untyped], SnapshotNormalizer, untyped, untyped) -> [ untyped, RecordSnapshot? ]
38
+
39
+ # : (untyped, Array[untyped], SnapshotNormalizer, untyped, untyped) -> RecordSnapshot?
40
+ def normalized_collection_event_record: (untyped, Array[untyped], SnapshotNormalizer, untyped, untyped) -> RecordSnapshot?
41
+
42
+ # : (RecordSnapshot, Array[untyped], untyped, ActivitySnapshotDelta?) -> RecordSnapshot?
43
+ def existing_delta_record: (RecordSnapshot, Array[untyped], untyped, ActivitySnapshotDelta?) -> RecordSnapshot?
44
+
45
+ # : (RecordSnapshot, Array[untyped], ActivitySnapshotDelta) -> RecordSnapshot?
46
+ def nested_delta_owner: (RecordSnapshot, Array[untyped], ActivitySnapshotDelta) -> RecordSnapshot?
30
47
 
31
48
  # : (untyped, AssociationTree, String, ?path: String) -> Array[Array[untyped]]
32
49
  def direct_collection_routes: (untyped, AssociationTree, String, ?path: String) -> Array[Array[untyped]]
@@ -34,6 +51,18 @@ module PaperTrailDiff
34
51
  # : (RecordSnapshot, Array[untyped], untyped, untyped, RecordSnapshot?, ?depth: Integer) -> [RecordSnapshot, bool]
35
52
  def replace_collection_route: (RecordSnapshot, Array[untyped], untyped, untyped, RecordSnapshot?, ?depth: Integer) -> [ RecordSnapshot, bool ]
36
53
 
54
+ # : (AssociationSnapshot, untyped, untyped, untyped, RecordSnapshot, RecordSnapshot?) -> RecordSnapshot?
55
+ def collection_event_child: (AssociationSnapshot, untyped, untyped, untyped, RecordSnapshot, RecordSnapshot?) -> RecordSnapshot?
56
+
57
+ # : (AssociationSnapshot, Array[untyped], untyped, untyped, RecordSnapshot?, Integer) -> [Array[RecordSnapshot], RecordSnapshot?, RecordSnapshot?, bool]?
58
+ def replace_targeted_collection_parent: (AssociationSnapshot, Array[untyped], untyped, untyped, RecordSnapshot?, Integer) -> [ Array[RecordSnapshot], RecordSnapshot?, RecordSnapshot?, bool ]?
59
+
60
+ # : (Array[RecordSnapshot], Integer, RecordSnapshot, RecordSnapshot, bool) -> [Array[RecordSnapshot], RecordSnapshot?, RecordSnapshot?, bool]
61
+ def targeted_parent_replacement: (Array[RecordSnapshot], Integer, RecordSnapshot, RecordSnapshot, bool) -> [ Array[RecordSnapshot], RecordSnapshot?, RecordSnapshot?, bool ]
62
+
63
+ # : (AssociationSnapshot, untyped, untyped, untyped) -> Integer?
64
+ def nested_owner_position: (AssociationSnapshot, untyped, untyped, untyped) -> Integer?
65
+
37
66
  # : (untyped, untyped, RecordSnapshot, AssociationTree, SnapshotNormalizer, untyped) -> [bool, RecordSnapshot?]
38
67
  def belongs_to_target_snapshot: (untyped, untyped, RecordSnapshot, AssociationTree, SnapshotNormalizer, untyped) -> [ bool, RecordSnapshot? ]
39
68
 
@@ -77,8 +106,17 @@ module PaperTrailDiff
77
106
  # : (untyped, untyped, untyped) -> untyped
78
107
  def standard_changeset: (untyped, untyped, untyped) -> untyped
79
108
 
80
- # : (untyped, untyped, RecordSnapshot) -> bool
81
- def member_of_owner?: (untyped, untyped, RecordSnapshot) -> bool
109
+ # : (untyped) -> ActivitySnapshotDelta?
110
+ def activity_snapshot_delta: (untyped) -> ActivitySnapshotDelta?
111
+
112
+ # : (untyped, untyped, RecordSnapshot, ?state: Symbol) -> bool
113
+ def member_of_owner?: (untyped, untyped, RecordSnapshot, ?state: Symbol) -> bool
114
+
115
+ # : (untyped, untyped, state: Symbol) -> untyped
116
+ def event_attribute: (untyped, untyped, state: Symbol) -> untyped
117
+
118
+ # : (untyped, untyped, state: Symbol) -> untyped
119
+ def relationship_owner_id: (untyped, untyped, state: Symbol) -> untyped
82
120
 
83
121
  # : (untyped, untyped, AssociationTree, String, SnapshotNormalizer, untyped, untyped) -> RecordSnapshot?
84
122
  def normalize_event_record: (untyped, untyped, AssociationTree, String, SnapshotNormalizer, untyped, untyped) -> RecordSnapshot?
@@ -86,8 +124,11 @@ module PaperTrailDiff
86
124
  # : (untyped, habtm_version: untyped) -> HistoricalAssociationReifier
87
125
  def historical_reader: (untyped, habtm_version: untyped) -> HistoricalAssociationReifier
88
126
 
89
- # : (Array[RecordSnapshot], untyped, RecordSnapshot?) -> Array[RecordSnapshot]
90
- def replace_collection_record: (Array[RecordSnapshot], untyped, RecordSnapshot?) -> Array[RecordSnapshot]
127
+ # : (AssociationSnapshot, untyped, RecordSnapshot?) -> [Array[RecordSnapshot], RecordSnapshot?, RecordSnapshot?, bool]
128
+ def replace_collection_record: (AssociationSnapshot, untyped, RecordSnapshot?) -> [ Array[RecordSnapshot], RecordSnapshot?, RecordSnapshot?, bool ]
129
+
130
+ # : (Array[RecordSnapshot], Integer?, RecordSnapshot?) -> Array[RecordSnapshot]
131
+ def replacement_collection_records: (Array[RecordSnapshot], Integer?, RecordSnapshot?) -> Array[RecordSnapshot]
91
132
 
92
133
  # : (untyped, RecordSnapshot?) -> String
93
134
  def replacement_type: (untyped, RecordSnapshot?) -> String
@@ -0,0 +1,79 @@
1
+ # Generated from lib/paper_trail_diff/activity_history.rb with RBS::Inline
2
+
3
+ module PaperTrailDiff
4
+ # Immutable snapshots and steps produced by one activity-history pass.
5
+ class ActivityHistory
6
+ attr_reader steps: Array[ActivityStep]
7
+
8
+ attr_reader root_snapshots: Hash[Array[untyped], RecordSnapshot?]
9
+
10
+ attr_reader first_snapshot: RecordSnapshot?
11
+
12
+ attr_reader last_snapshot: RecordSnapshot?
13
+
14
+ # : (steps: Array[ActivityStep], root_snapshots: Hash[Array[untyped], RecordSnapshot?], first_snapshot: RecordSnapshot?, last_snapshot: RecordSnapshot?) -> void
15
+ def initialize: (steps: Array[ActivityStep], root_snapshots: Hash[Array[untyped], RecordSnapshot?], first_snapshot: RecordSnapshot?, last_snapshot: RecordSnapshot?) -> void
16
+
17
+ # : () -> ActivityHistory
18
+ def self.empty: () -> ActivityHistory
19
+ end
20
+
21
+ # Builds activity steps while retaining root snapshots for combined analysis.
22
+ class ActivityHistoryBuilder
23
+ # : (Array[untyped], Array[ActivityEvent], untyped, ?current: untyped, ?include_step: untyped) -> void
24
+ def initialize: (Array[untyped], Array[ActivityEvent], untyped, ?current: untyped, ?include_step: untyped) -> void
25
+
26
+ # : () -> ActivityHistory
27
+ def call: () -> ActivityHistory
28
+
29
+ private
30
+
31
+ @current: untyped
32
+
33
+ @events: Array[ActivityEvent]
34
+
35
+ @first_snapshot: RecordSnapshot?
36
+
37
+ @include_step: untyped
38
+
39
+ @previous_boundary: ActivityBoundary?
40
+
41
+ @previous_event: ActivityEvent?
42
+
43
+ @previous_snapshot: RecordSnapshot?
44
+
45
+ @root_snapshots: Hash[Array[untyped], RecordSnapshot?]
46
+
47
+ @root_versions: Array[untyped]
48
+
49
+ @selected_last_snapshot: RecordSnapshot?
50
+
51
+ @snapshotter: untyped
52
+
53
+ @steps: Array[ActivityStep]
54
+
55
+ # : () -> void
56
+ def reset: () -> void
57
+
58
+ # : () -> ActivitySnapshotSequence
59
+ def sequence: () -> ActivitySnapshotSequence
60
+
61
+ # : (ActivityEvent, RecordSnapshot?) -> void
62
+ def consume: (ActivityEvent, RecordSnapshot?) -> void
63
+
64
+ # : (RecordSnapshot?) -> void
65
+ def capture_first: (RecordSnapshot?) -> void
66
+
67
+ # : (ActivityBoundary, RecordSnapshot?) -> void
68
+ def append_step: (ActivityBoundary, RecordSnapshot?) -> void
69
+
70
+ # : () -> bool
71
+ def include_previous_step?: () -> bool
72
+
73
+ # : () -> RecordSnapshot?
74
+ def final_snapshot: () -> RecordSnapshot?
75
+
76
+ # : (untyped) -> Array[untyped]
77
+ def version_key: (untyped) -> Array[untyped]
78
+ end
79
+ end