Class: PaperTrailDiff::TimeActivityTimelineBuilder
- Inherits:
-
Object
- Object
- PaperTrailDiff::TimeActivityTimelineBuilder
- Defined in:
- lib/paper_trail_diff/time_activity_timeline_builder.rb,
sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs
Overview
Builds activity views for mutations selected by a wall-clock range.
Instance Method Summary collapse
-
#activity_steps(history, closing) ⇒ Array[ActivityStep]
: (ActivityHistory, ActivityStep?) -> Array.
-
#analyze ⇒ Analysis
: () -> Analysis.
-
#build ⇒ Array[ActivityStep]
: () -> Array.
- #build_history(root_versions, events) ⇒ ActivityHistory
-
#closing_step(history, event, plan, captured_at, snapshot) ⇒ ActivityStep?
A window either closes on the absence its final destruction leaves, or on current state when it reaches past everything recorded.
- #collect_events(root_versions, range_end: nil) ⇒ Array[ActivityEvent]
-
#current_snapshot(plan) ⇒ RecordSnapshot?
One live read serves both the closing activity step and the endpoint the analysis reports, which would otherwise reconstruct the graph twice.
-
#current_step(history, record, captured_at, snapshot) ⇒ ActivityStep?
The last boundary reached is where current state is compared from, which is the final event rather than the final root version once descendants have moved since it.
-
#destroyed_step(history, event) ⇒ ActivityStep
: (ActivityHistory, ActivityEvent) -> ActivityStep.
- #events_through(root_versions, captured_at) ⇒ Array[ActivityEvent]
-
#history_and_versions ⇒ [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
: () -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped].
-
#history_for(plan, captured_at) ⇒ [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
: (RootVersionPlan, untyped) -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped].
-
#initialize(record, range:, tree:, snapshotter:, snapshots: false) ⇒ TimeActivityTimelineBuilder
constructor
: (untyped, range: TimelineRange, tree: AssociationTree, snapshotter: untyped, ?snapshots: bool) -> void.
-
#later_event?(selected, events) ⇒ Boolean
: (ActivityEvent, Array) -> bool.
-
#prepare_history(root_versions, end_at: nil) ⇒ void
: (Array, ?end_at: untyped) -> void.
-
#selected_events(events, plan) ⇒ Array[ActivityEvent]
Root versions inside the window that the plan does not report are context rather than mutations: the version appended to reveal the last selected change, and anything a filter excluded but the replay still walks.
-
#terminal_destroy?(event) ⇒ Boolean
: (ActivityEvent?) -> bool.
- #time_events?(selected, events, plan) ⇒ Boolean
Constructor Details
#initialize(record, range:, tree:, snapshotter:, snapshots: false) ⇒ TimeActivityTimelineBuilder
: (untyped, range: TimelineRange, tree: AssociationTree, snapshotter: untyped, ?snapshots: bool) -> void
8 9 10 11 12 13 14 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 8 def initialize(record, range:, tree:, snapshotter:, snapshots: false) @snapshots = snapshots @record = record @range = range @tree = tree @snapshotter = snapshotter end |
Instance Method Details
#activity_steps(history, closing) ⇒ Array[ActivityStep]
: (ActivityHistory, ActivityStep?) -> Array
100 101 102 103 104 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 100 def activity_steps(history, closing) return history.steps unless closing (history.steps + [closing]).freeze end |
#analyze ⇒ Analysis
: () -> Analysis
23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 23 def analyze history, plan, closing, closing_snapshot, captured_at = history_and_versions final = closing_snapshot || history.last_snapshot Analysis.new( diff: Engine.compare(history.first_snapshot, final), timeline: ActivityRootSteps.call( plan, history.root_snapshots, closing_snapshot: closing_snapshot, captured_at: captured_at ), activity_timeline: activity_steps(history, closing), from_snapshot: history.first_snapshot, to_snapshot: final ) end |
#build ⇒ Array[ActivityStep]
: () -> Array
17 18 19 20 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 17 def build history, _plan, closing, = history_and_versions activity_steps(history, closing) end |
#build_history(root_versions, events) ⇒ ActivityHistory
89 90 91 92 93 94 95 96 97 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 89 def build_history(root_versions, events) ActivityHistoryBuilder.new( root_versions, events, @snapshotter, include_step: ->(event) { @range.include?(event.version) }, snapshots: @snapshots ).call end |
#closing_step(history, event, plan, captured_at, snapshot) ⇒ ActivityStep?
A window either closes on the absence its final destruction leaves, or on current state when it reaches past everything recorded. Both are boundaries no version can supply. : (ActivityHistory, ActivityEvent?, RootVersionPlan, untyped, RecordSnapshot?) -> ActivityStep?
124 125 126 127 128 129 130 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 124 def closing_step(history, event, plan, captured_at, snapshot) return unless event return destroyed_step(history, event) if terminal_destroy?(event) return unless plan.closing_record current_step(history, plan.closing_record, captured_at, snapshot) end |
#collect_events(root_versions, range_end: nil) ⇒ Array[ActivityEvent]
176 177 178 179 180 181 182 183 184 185 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 176 def collect_events(root_versions, range_end: nil) ActivityVersionCollector.new( @record, root_versions: root_versions, tree: @tree, traversal: AssociationTraversal.new(@tree), range_start: @range.begin_time, range_end: range_end || root_versions.last ).call end |
#current_snapshot(plan) ⇒ RecordSnapshot?
One live read serves both the closing activity step and the endpoint the analysis reports, which would otherwise reconstruct the graph twice. : (RootVersionPlan) -> RecordSnapshot?
75 76 77 78 79 80 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 75 def current_snapshot(plan) record = plan.closing_record return unless record @snapshotter.call(record, record) end |
#current_step(history, record, captured_at, snapshot) ⇒ ActivityStep?
The last boundary reached is where current state is compared from, which is the final event rather than the final root version once descendants have moved since it. : (ActivityHistory, untyped, untyped, RecordSnapshot?) -> ActivityStep?
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 147 def current_step(history, record, captured_at, snapshot) previous = history.steps.last&.to_boundary return unless previous ActivityStep.between( from_boundary: previous, to_boundary: ActivityBoundary.current(record, captured_at: captured_at), from_snapshot: history.last_snapshot, to_snapshot: snapshot, retain: @snapshots ) end |
#destroyed_step(history, event) ⇒ ActivityStep
: (ActivityHistory, ActivityEvent) -> ActivityStep
133 134 135 136 137 138 139 140 141 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 133 def destroyed_step(history, event) version = event.version ActivityStep.between( from_boundary: ActivityBoundary.from_version(version), to_boundary: ActivityBoundary.destroyed(version), from_snapshot: history.root_snapshots[ActivityRootSteps.version_key(version)], to_snapshot: nil, retain: @snapshots ) end |
#events_through(root_versions, captured_at) ⇒ Array[ActivityEvent]
83 84 85 86 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 83 def events_through(root_versions, captured_at) prepare_history(root_versions, end_at: captured_at) collect_events(root_versions, range_end: captured_at) end |
#history_and_versions ⇒ [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
: () -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped]
47 48 49 50 51 52 53 54 55 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 47 def history_and_versions plan = @range.select_plan(context_required: !@tree.empty?) return [ActivityHistory.empty, plan, nil, nil, nil] if plan.reconstruction_versions.empty? # A window closing on current state runs to the instant it is captured, # not to the last root version, or descendants that moved after that # version would be missing from a report that claims to reach now. history_for(plan, plan.closing_record ? Time.now.utc : nil) end |
#history_for(plan, captured_at) ⇒ [ ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped ]
: (RootVersionPlan, untyped) -> [ActivityHistory, RootVersionPlan, ActivityStep?, RecordSnapshot?, untyped]
58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 58 def history_for(plan, captured_at) root_versions = plan.reconstruction_versions events = events_through(root_versions, captured_at) selected = selected_events(events, plan) unless time_events?(selected, events, plan) return [ActivityHistory.empty, plan, nil, nil, nil] end history = build_history(root_versions, events) snapshot = current_snapshot(plan) closing = closing_step(history, selected.last, plan, captured_at, snapshot) [history, plan, closing, snapshot, captured_at] end |
#later_event?(selected, events) ⇒ Boolean
: (ActivityEvent, Array) -> bool
201 202 203 204 205 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 201 def later_event?(selected, events) events.any? do |event| Support.compare_versions(selected.version, event.version).negative? end end |
#prepare_history(root_versions, end_at: nil) ⇒ void
This method returns an undefined value.
: (Array, ?end_at: untyped) -> void
166 167 168 169 170 171 172 173 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 166 def prepare_history(root_versions, end_at: nil) return unless @snapshotter.respond_to?(:prepare) start_at = @range.begin_time return @snapshotter.prepare(@record, root_versions, start_at: start_at) unless end_at @snapshotter.prepare(@record, root_versions, start_at: start_at, end_at: end_at) end |
#selected_events(events, plan) ⇒ Array[ActivityEvent]
Root versions inside the window that the plan does not report are context rather than mutations: the version appended to reveal the last selected change, and anything a filter excluded but the replay still walks. Neither may be counted as a selected mutation. Descendant events are not filtered, so window membership is the whole test for them. : (Array, RootVersionPlan) -> Array
112 113 114 115 116 117 118 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 112 def selected_events(events, plan) events.select do |event| next false unless @range.include?(event.version) !event.root? || plan.mutation?(event.version) end end |
#terminal_destroy?(event) ⇒ Boolean
: (ActivityEvent?) -> bool
159 160 161 162 163 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 159 def terminal_destroy?(event) return false unless event event.root? && event.version.event.to_s == 'destroy' end |
#time_events?(selected, events, plan) ⇒ Boolean
188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/paper_trail_diff/time_activity_timeline_builder.rb', line 188 def time_events?(selected, events, plan) return false if selected.empty? return true if later_event?(selected.last, events) return true if terminal_destroy?(selected.last) return true if plan.closing_record = 'time range requires a later activity boundary to reconstruct its ' \ 'final change: pass close_on: :current to end at current state, ' \ 'or narrow the window to end before the last recorded boundary' raise IncompleteTimeRangeError, end |