Class: Ace::Assign::Models::QueueState
- Inherits:
-
Object
- Object
- Ace::Assign::Models::QueueState
- Defined in:
- lib/ace/assign/models/queue_state.rb
Overview
Queue state model representing a snapshot of the work queue.
Pure data carrier with no business logic (ATOM pattern). Provides convenient accessors for queue analysis.
Instance Attribute Summary collapse
-
#assignment ⇒ Object
readonly
Returns the value of attribute assignment.
-
#steps ⇒ Object
readonly
Returns the value of attribute steps.
Instance Method Summary collapse
-
#active_branch_conflict_in_subtree?(root_number, extra_active: []) ⇒ Boolean
Check whether active steps inside a subtree fan out across more than one branch.
-
#active_fork_roots ⇒ Array<Step>
Get active fork roots in queue order.
-
#active_in_subtree(root_number) ⇒ Array<Step>
Get all active steps within a subtree.
-
#active_steps ⇒ Array<Step>
Get all active steps in queue order.
-
#all_numbers ⇒ Array<String>
Get all step numbers as an array.
-
#ancestor_chain(number) ⇒ Array<String>
Build ancestor chain from closest parent to root.
-
#assignment_state ⇒ Symbol
Computed assignment state based on step statuses.
-
#children_of(parent_number) ⇒ Array<Step>
Get all direct children of a step (O(1) via index).
-
#complete? ⇒ Boolean
Check if all steps are complete (no pending or active).
-
#current ⇒ Step?
Get the deterministic single-step active target.
-
#current_in_subtree(root_number) ⇒ Step?
Get the deterministic active target within a subtree.
-
#deepest_active ⇒ Step?
Get deepest active step in queue order.
-
#deepest_active_in_subtree(root_number) ⇒ Step?
Get the deepest active step within a subtree.
-
#descendants_of(parent_number) ⇒ Array<Step>
Get all descendants (children, grandchildren, etc.) of a step.
-
#done ⇒ Array<Step>
Get all done steps.
-
#empty? ⇒ Boolean
Check if queue is empty.
-
#failed ⇒ Array<Step>
Get all failed steps.
-
#find_by_number(number) ⇒ Step?
Get step by number.
-
#has_incomplete_children?(parent_number) ⇒ Boolean
Check if a step has any incomplete children.
-
#hierarchical ⇒ Array<Hash>
Build hierarchical structure for display.
-
#in_subtree?(root_number, step_number) ⇒ Boolean
Check whether a step number belongs to a subtree rooted at root_number.
-
#initialize(steps:, assignment:) ⇒ QueueState
constructor
A new instance of QueueState.
-
#last ⇒ Step?
Get last step in queue.
-
#last_done ⇒ Step?
Get last completed step.
-
#nearest_fork_ancestor(number) ⇒ Step?
Find nearest ancestor (or self) that has context: fork.
-
#next_pending ⇒ Step?
Get next pending step.
-
#next_workable(scope_root: nil) ⇒ Step?
Get next workable pending step considering hierarchy and active fork ownership.
-
#next_workable_in_subtree(root_number) ⇒ Step?
Get next workable step constrained to a subtree.
-
#pending ⇒ Array<Step>
Get all pending steps.
-
#recently_active?(threshold: 3600) ⇒ Boolean
Check if any active step has recent activity.
-
#size ⇒ Integer
Total step count.
-
#subtree_complete?(root_number) ⇒ Boolean
Check whether all steps in a subtree are complete.
-
#subtree_failed?(root_number) ⇒ Boolean
Check whether a subtree has at least one failed step.
-
#subtree_steps(root_number) ⇒ Array<Step>
Get all steps in a subtree (root + descendants), preserving queue order.
-
#summary ⇒ Hash
Summary for display.
-
#top_level ⇒ Array<Step>
Get top-level (root) steps only.
Constructor Details
#initialize(steps:, assignment:) ⇒ QueueState
Returns a new instance of QueueState.
20 21 22 23 24 |
# File 'lib/ace/assign/models/queue_state.rb', line 20 def initialize(steps:, assignment:) @steps = steps.freeze @assignment = assignment @children_index = build_children_index(steps) end |
Instance Attribute Details
#assignment ⇒ Object (readonly)
Returns the value of attribute assignment.
16 17 18 |
# File 'lib/ace/assign/models/queue_state.rb', line 16 def assignment @assignment end |
#steps ⇒ Object (readonly)
Returns the value of attribute steps.
16 17 18 |
# File 'lib/ace/assign/models/queue_state.rb', line 16 def steps @steps end |
Instance Method Details
#active_branch_conflict_in_subtree?(root_number, extra_active: []) ⇒ Boolean
Check whether active steps inside a subtree fan out across more than one branch.
309 310 311 312 313 314 315 |
# File 'lib/ace/assign/models/queue_state.rb', line 309 def active_branch_conflict_in_subtree?(root_number, extra_active: []) active_numbers = active_in_subtree(root_number).map(&:number) + Array(extra_active).map(&:to_s) unique_active = active_numbers.uniq unique_active.combination(2).any? do |left, right| !(in_subtree?(left, right) || in_subtree?(right, left)) end end |
#active_fork_roots ⇒ Array<Step>
Get active fork roots in queue order.
300 301 302 |
# File 'lib/ace/assign/models/queue_state.rb', line 300 def active_fork_roots active_steps.select(&:fork?) end |
#active_in_subtree(root_number) ⇒ Array<Step>
Get all active steps within a subtree.
220 221 222 223 |
# File 'lib/ace/assign/models/queue_state.rb', line 220 def active_in_subtree(root_number) subtree_steps(root_number) .select { |s| s.status == :active } end |
#active_steps ⇒ Array<Step>
Get all active steps in queue order.
39 40 41 |
# File 'lib/ace/assign/models/queue_state.rb', line 39 def active_steps steps.select { |s| s.status == :active } end |
#all_numbers ⇒ Array<String>
Get all step numbers as an array
319 320 321 |
# File 'lib/ace/assign/models/queue_state.rb', line 319 def all_numbers steps.map(&:number) end |
#ancestor_chain(number) ⇒ Array<String>
Build ancestor chain from closest parent to root.
245 246 247 248 249 250 251 252 253 |
# File 'lib/ace/assign/models/queue_state.rb', line 245 def ancestor_chain(number) chain = [] parent = Atoms::StepNumbering.parent_of(number) while parent chain << parent parent = Atoms::StepNumbering.parent_of(parent) end chain end |
#assignment_state ⇒ Symbol
Computed assignment state based on step statuses
States (checked in priority order):
-
:empty - No steps in queue
-
:completed - All steps complete (done or failed)
-
:failed - Has failed step(s) but NOT all complete (stuck)
-
:running - Has active step(s) with recent activity (< 1 hour)
-
:stalled - Has active step(s) but all are stale (> 1 hour)
-
:paused - Has pending but no active step (interrupted)
120 121 122 123 124 125 126 127 128 |
# File 'lib/ace/assign/models/queue_state.rb', line 120 def assignment_state return :empty if empty? return :completed if complete? return :failed if failed.any? return :running if active_steps.any? && recently_active? return :stalled if active_steps.any? :paused end |
#children_of(parent_number) ⇒ Array<Step>
Get all direct children of a step (O(1) via index)
161 162 163 |
# File 'lib/ace/assign/models/queue_state.rb', line 161 def children_of(parent_number) @children_index[parent_number] || [] end |
#complete? ⇒ Boolean
Check if all steps are complete (no pending or active)
75 76 77 |
# File 'lib/ace/assign/models/queue_state.rb', line 75 def complete? steps.all?(&:complete?) end |
#current ⇒ Step?
Get the deterministic single-step active target.
Returns the deepest active step in queue order. When several active branches exist globally, this is an internal convenience only; public status should use active_steps.
33 34 35 |
# File 'lib/ace/assign/models/queue_state.rb', line 33 def current deepest_active end |
#current_in_subtree(root_number) ⇒ Step?
Get the deterministic active target within a subtree.
212 213 214 |
# File 'lib/ace/assign/models/queue_state.rb', line 212 def current_in_subtree(root_number) deepest_active_in_subtree(root_number) end |
#deepest_active ⇒ Step?
Get deepest active step in queue order.
154 155 156 |
# File 'lib/ace/assign/models/queue_state.rb', line 154 def deepest_active deepest_active_from(active_steps) end |
#deepest_active_in_subtree(root_number) ⇒ Step?
Get the deepest active step within a subtree.
229 230 231 |
# File 'lib/ace/assign/models/queue_state.rb', line 229 def deepest_active_in_subtree(root_number) deepest_active_from(active_in_subtree(root_number)) end |
#descendants_of(parent_number) ⇒ Array<Step>
Get all descendants (children, grandchildren, etc.) of a step
168 169 170 |
# File 'lib/ace/assign/models/queue_state.rb', line 168 def descendants_of(parent_number) steps.select { |s| Atoms::StepNumbering.child_of?(s.number, parent_number) } end |
#done ⇒ Array<Step>
Get all done steps
51 52 53 |
# File 'lib/ace/assign/models/queue_state.rb', line 51 def done steps.select { |s| s.status == :done } end |
#empty? ⇒ Boolean
Check if queue is empty
69 70 71 |
# File 'lib/ace/assign/models/queue_state.rb', line 69 def empty? steps.empty? end |
#failed ⇒ Array<Step>
Get all failed steps
57 58 59 |
# File 'lib/ace/assign/models/queue_state.rb', line 57 def failed steps.select { |s| s.status == :failed } end |
#find_by_number(number) ⇒ Step?
Get step by number
82 83 84 85 86 87 88 89 |
# File 'lib/ace/assign/models/queue_state.rb', line 82 def find_by_number(number) # Normalize to string without leading zeros for comparison normalized = number.to_s.sub(/^0+/, "") steps.find do |s| next unless s.number s.number.sub(/^0+/, "") == normalized || s.number == number.to_s end end |
#has_incomplete_children?(parent_number) ⇒ Boolean
Check if a step has any incomplete children
276 277 278 |
# File 'lib/ace/assign/models/queue_state.rb', line 276 def has_incomplete_children?(parent_number) children_of(parent_number).any? { |s| s.status != :done } end |
#hierarchical ⇒ Array<Hash>
Build hierarchical structure for display
331 332 333 |
# File 'lib/ace/assign/models/queue_state.rb', line 331 def hierarchical build_hierarchy(nil) end |
#in_subtree?(root_number, step_number) ⇒ Boolean
Check whether a step number belongs to a subtree rooted at root_number.
177 178 179 |
# File 'lib/ace/assign/models/queue_state.rb', line 177 def in_subtree?(root_number, step_number) step_number == root_number || Atoms::StepNumbering.child_of?(step_number, root_number) end |
#last ⇒ Step?
Get last step in queue
93 94 95 |
# File 'lib/ace/assign/models/queue_state.rb', line 93 def last steps.last end |
#last_done ⇒ Step?
Get last completed step
99 100 101 |
# File 'lib/ace/assign/models/queue_state.rb', line 99 def last_done done.last end |
#nearest_fork_ancestor(number) ⇒ Step?
Find nearest ancestor (or self) that has context: fork.
259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/ace/assign/models/queue_state.rb', line 259 def nearest_fork_ancestor(number) step = find_by_number(number) return nil unless step return step if step.fork? ancestor_chain(number).each do |ancestor_number| ancestor = find_by_number(ancestor_number) return ancestor if ancestor&.fork? end nil end |
#next_pending ⇒ Step?
Get next pending step
63 64 65 |
# File 'lib/ace/assign/models/queue_state.rb', line 63 def next_pending pending.first end |
#next_workable(scope_root: nil) ⇒ Step?
Get next workable pending step considering hierarchy and active fork ownership. A step is workable if it’s pending and has no incomplete children.
Pending descendants under an active fork root are hidden unless the caller is explicitly scoped inside that root.
288 289 290 291 292 293 294 295 |
# File 'lib/ace/assign/models/queue_state.rb', line 288 def next_workable(scope_root: nil) candidate_steps = scope_root ? subtree_steps(scope_root) : steps candidate_steps .select { |s| s.status == :pending } .reject { |s| has_incomplete_children?(s.number) && !runnable_delegation_parent?(s, scope_root: scope_root) } .reject { |s| hidden_by_active_fork_root?(s.number, scope_root: scope_root) } .first end |
#next_workable_in_subtree(root_number) ⇒ Step?
Get next workable step constrained to a subtree.
237 238 239 |
# File 'lib/ace/assign/models/queue_state.rb', line 237 def next_workable_in_subtree(root_number) next_workable(scope_root: root_number) end |
#pending ⇒ Array<Step>
Get all pending steps
45 46 47 |
# File 'lib/ace/assign/models/queue_state.rb', line 45 def pending steps.select { |s| s.status == :pending } end |
#recently_active?(threshold: 3600) ⇒ Boolean
Check if any active step has recent activity.
133 134 135 136 137 |
# File 'lib/ace/assign/models/queue_state.rb', line 133 def recently_active?(threshold: 3600) active_steps.any? do |step| step.started_at && ((Time.now - step.started_at) < threshold) end end |
#size ⇒ Integer
Total step count
105 106 107 |
# File 'lib/ace/assign/models/queue_state.rb', line 105 def size steps.size end |
#subtree_complete?(root_number) ⇒ Boolean
Check whether all steps in a subtree are complete.
193 194 195 196 197 198 |
# File 'lib/ace/assign/models/queue_state.rb', line 193 def subtree_complete?(root_number) scoped = subtree_steps(root_number) return false if scoped.empty? scoped.all?(&:complete?) end |
#subtree_failed?(root_number) ⇒ Boolean
Check whether a subtree has at least one failed step.
204 205 206 |
# File 'lib/ace/assign/models/queue_state.rb', line 204 def subtree_failed?(root_number) subtree_steps(root_number).any? { |s| s.status == :failed } end |
#subtree_steps(root_number) ⇒ Array<Step>
Get all steps in a subtree (root + descendants), preserving queue order.
185 186 187 |
# File 'lib/ace/assign/models/queue_state.rb', line 185 def subtree_steps(root_number) steps.select { |s| in_subtree?(root_number, s.number) } end |
#summary ⇒ Hash
Summary for display
141 142 143 144 145 146 147 148 149 |
# File 'lib/ace/assign/models/queue_state.rb', line 141 def summary { total: size, done: done.size, active: active_steps.size, pending: pending.size, failed: failed.size } end |
#top_level ⇒ Array<Step>
Get top-level (root) steps only
325 326 327 |
# File 'lib/ace/assign/models/queue_state.rb', line 325 def top_level steps.select { |s| Atoms::StepNumbering.top_level?(s.number) } end |