Class: ChronoForge::Dashboard::BranchPresenter

Inherits:
Object
  • Object
show all
Defined in:
app/presenters/chrono_forge/dashboard/branch_presenter.rb

Overview

Health of a single branch (a branch$<name> execution log) for the parent’s detail page. Every child count is CAPPED and index-only on (parent_execution_log_id, state) — a branch can hold hundreds of thousands of children, so we never count the full set, only up to CAP (shown “CAP+”).

Constant Summary collapse

CAP =
5000
BLOCKED_STATES =
%i[failed stalled].map { |s| ChronoForge::Workflow.states[s] }.freeze
POLL_OVERDUE_GRACE =

A scheduled next poll this far past due means the BranchMergeJob poller likely never ran (queue latency aside) — a heuristic, hence “potential”.

120

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(log, merge_state = nil) ⇒ BranchPresenter

merge_state: :merged | :merging | nil (not yet merged)



11
12
13
14
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 11

def initialize(log, merge_state = nil)
  @log = log
  @merge_state = merge_state
end

Instance Attribute Details

#logObject (readonly)

Returns the value of attribute log.



16
17
18
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 16

def log
  @log
end

#merge_stateObject (readonly)

Returns the value of attribute merge_state.



16
17
18
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 16

def merge_state
  @merge_state
end

Instance Method Details

#blockedObject



25
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 25

def blocked = capped(children.where(state: BLOCKED_STATES))

#capObject



27
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 27

def cap = CAP

#dispatchedObject



23
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 23

def dispatched = capped(children)

#last_polled_atObject



38
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 38

def last_polled_at = parse_time(poll&.dig("last_polled_at"))

#nameObject



18
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 18

def name = StepNameParser.parse(@log.step_name).name

#next_poll_atObject



39
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 39

def next_poll_at = parse_time(poll&.dig("next_poll_at"))

#pendingObject



24
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 24

def pending = capped(children.where.not(state: ChronoForge::Workflow.states[:completed]))

#poll_overdue?Boolean

next_poll_at is nil once the merge completes, so a finished merge never looks overdue; a non-nil time well in the past = the poller is likely dead.

Returns:

  • (Boolean)


44
45
46
47
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 44

def poll_overdue?
  t = next_poll_at
  t.present? && t < Time.current - POLL_OVERDUE_GRACE
end

#polled?Boolean

The BranchMergeJob stamps its poll state onto the branch log’s metadata (it can’t be queried from the backend; ActiveJob has no such API).

Returns:

  • (Boolean)


37
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 37

def polled? = poll.present?

#pollsObject



40
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 40

def polls = poll&.dig("polls").to_i

#sealed?Boolean

The branch is “sealed” once its block closed (done dispatching children).

Returns:

  • (Boolean)


21
# File 'app/presenters/chrono_forge/dashboard/branch_presenter.rb', line 21

def sealed? = @log.completed?