Class: RailsErrorDashboard::Services::ErrorBroadcaster

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_error_dashboard/services/error_broadcaster.rb

Overview

Infrastructure service: Turbo Stream broadcasting for real-time UI updates

Handles broadcasting new errors, error updates, and stats refreshes via Turbo Streams. Safely no-ops when Turbo/ActionCable is unavailable.

IMPORTANT: Broadcasting failures MUST NOT block error logging. All public methods rescue exceptions and log them.

NOTE: Turbo broadcasts render partials via ApplicationController.render, which is the HOST app’s controller — engine route helpers (error_path, etc.) are NOT available there. We render via the engine’s own controller renderer and pass pre-rendered HTML to the broadcast to ensure route helpers work.

Class Method Summary collapse

Class Method Details

.available?Boolean

Check if broadcasting infrastructure is available

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
103
104
105
# File 'lib/rails_error_dashboard/services/error_broadcaster.rb', line 95

def self.available?
  return false unless defined?(Turbo)
  return false unless defined?(ActionCable)

  Rails.cache.write("rails_error_dashboard_broadcast_test", true, expires_in: 1.second)
  Rails.cache.delete("rails_error_dashboard_broadcast_test")
  true
rescue => e
  Rails.logger.debug("[RailsErrorDashboard] Broadcast not available: #{e.message}")
  false
end

.broadcast_new(error_log) ⇒ Object

Broadcast a new error (prepend to error list + refresh stats)

Parameters:

  • error_log (ErrorLog)

    The newly created error



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/rails_error_dashboard/services/error_broadcaster.rb', line 20

def self.broadcast_new(error_log)
  return unless error_log
  return unless available?

  platforms = ErrorLog.distinct.pluck(:platform).compact
  show_platform = platforms.size > 1

  html = render_partial("rails_error_dashboard/errors/error_row",
    error: error_log, show_platform: show_platform)

  Turbo::StreamsChannel.broadcast_prepend_to(
    "error_list",
    target: "error_list",
    html: html
  )
  broadcast_stats
rescue => e
  Rails.logger.error("[RailsErrorDashboard] Failed to broadcast new error: #{e.class} - #{e.message}")
  Rails.logger.debug("[RailsErrorDashboard] Backtrace: #{e.backtrace&.first(3)&.join("\n")}")
end

.broadcast_statsObject

Broadcast stats refresh



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/rails_error_dashboard/services/error_broadcaster.rb', line 65

def self.broadcast_stats
  return unless available?

  stats = Queries::DashboardStats.call
  return unless stats.is_a?(Hash) && stats.present?

  html = render_partial("rails_error_dashboard/errors/stats", stats: stats)

  Turbo::StreamsChannel.broadcast_replace_to(
    "error_list",
    target: "dashboard_stats",
    html: html
  )
rescue => e
  Rails.logger.error("[RailsErrorDashboard] Failed to broadcast stats update: #{e.class} - #{e.message}")
  Rails.logger.debug("[RailsErrorDashboard] Backtrace: #{e.backtrace&.first(3)&.join("\n")}")
end

.broadcast_update(error_log) ⇒ Object

Broadcast an error update (replace in error list + refresh stats)

Parameters:

  • error_log (ErrorLog)

    The updated error



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rails_error_dashboard/services/error_broadcaster.rb', line 43

def self.broadcast_update(error_log)
  return unless error_log
  return unless available?

  platforms = ErrorLog.distinct.pluck(:platform).compact
  show_platform = platforms.size > 1

  html = render_partial("rails_error_dashboard/errors/error_row",
    error: error_log, show_platform: show_platform)

  Turbo::StreamsChannel.broadcast_replace_to(
    "error_list",
    target: "error_#{error_log.id}",
    html: html
  )
  broadcast_stats
rescue => e
  Rails.logger.error("[RailsErrorDashboard] Failed to broadcast error update: #{e.class} - #{e.message}")
  Rails.logger.debug("[RailsErrorDashboard] Backtrace: #{e.backtrace&.first(3)&.join("\n")}")
end

.render_partial(partial, **locals) ⇒ Object

Render a partial using the engine’s controller renderer. This ensures engine route helpers (error_path, etc.) are available, unlike Turbo’s default ApplicationController.render which uses the host app’s context.



86
87
88
89
90
91
# File 'lib/rails_error_dashboard/services/error_broadcaster.rb', line 86

def self.render_partial(partial, **locals)
  RailsErrorDashboard::ApplicationController.render(
    partial: partial,
    locals: locals
  )
end