Module: RailsErrorDashboard::Services::NotificationHelpers
- Defined in:
- lib/rails_error_dashboard/services/notification_helpers.rb
Overview
Shared helper methods for notification payload builders
Pure functions: no side effects, no HTTP calls, no database access. Used by all notification payload builders to avoid duplication.
Class Method Summary collapse
-
.app_name(error_log) ⇒ String
Application name for notifications.
-
.dashboard_url(error_log) ⇒ String
Generate dashboard URL for an error.
-
.error_source(error_log) ⇒ String
Error source description for PagerDuty.
-
.extract_backtrace(backtrace, limit = 20) ⇒ Array<String>
Extract backtrace lines as an array.
-
.extract_first_backtrace_line(backtrace, length = 100, locale: nil) ⇒ String
Extract first backtrace line (truncated).
-
.field(label, value, locale) ⇒ Object
A "Label:\nvalue" Slack mrkdwn field.
-
.format_datetime(time, locale) ⇒ String
A human-readable timestamp for a notification body.
-
.format_datetime_or_na(time, locale) ⇒ Object
A localized timestamp, or the localized "N/A" for nil.
-
.format_time(time) ⇒ String
Format time for display.
-
.label(label, locale) ⇒ Object
One label from red.notifications.error_alert.labels.
-
.not_available(locale) ⇒ String
The localized "N/A" placeholder.
-
.parse_request_params(params_json) ⇒ Hash
Parse request params JSON safely.
-
.platform_emoji(platform) ⇒ String
Platform emoji for Slack/text notifications.
-
.t(key, locale, **options) ⇒ String
Translate for a notification payload.
-
.truncate_message(message, length = 500) ⇒ String
Truncate a message to a maximum length.
-
.unknown(locale) ⇒ String
The localized "Unknown" placeholder.
Class Method Details
.app_name(error_log) ⇒ String
Application name for notifications
178 179 180 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 178 def app_name(error_log) error_log.application&.name || "Unknown" end |
.dashboard_url(error_log) ⇒ String
Generate dashboard URL for an error
90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 90 def dashboard_url(error_log) base_url = (RailsErrorDashboard.configuration.dashboard_base_url || "http://localhost:3000").chomp("/") mount_path = RailsErrorDashboard.configuration.engine_mount_path # Avoid doubling the mount path when base_url already includes it. # e.g., base_url="https://app.com/red" + mount_path="/red" → don't produce "/red/red" if mount_path.present? && base_url.end_with?(mount_path.chomp("/")) "#{base_url}/errors/#{error_log.id}" else "#{base_url}#{mount_path}/errors/#{error_log.id}" end end |
.error_source(error_log) ⇒ String
Error source description for PagerDuty
185 186 187 188 189 190 191 192 193 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 185 def error_source(error_log) if error_log.controller_name && error_log.action_name "#{error_log.controller_name}##{error_log.action_name}" elsif error_log.request_url error_log.request_url else error_log.platform || "Rails Application" end end |
.extract_backtrace(backtrace, limit = 20) ⇒ Array<String>
Extract backtrace lines as an array
116 117 118 119 120 121 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 116 def extract_backtrace(backtrace, limit = 20) return [] if backtrace.nil? lines = backtrace.is_a?(String) ? backtrace.lines : backtrace lines.first(limit).map(&:strip) end |
.extract_first_backtrace_line(backtrace, length = 100, locale: nil) ⇒ String
Extract first backtrace line (truncated)
The line itself is diagnostic output and is never translated — only the "N/A" shown in its absence is, and only when a locale is supplied. Callers that pass no locale keep the literal, so existing non-i18n callers are unaffected.
134 135 136 137 138 139 140 141 142 143 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 134 def extract_first_backtrace_line(backtrace, length = 100, locale: nil) placeholder = locale ? not_available(locale) : "N/A" return placeholder if backtrace.nil? lines = backtrace.is_a?(String) ? backtrace.lines : backtrace first_line = lines.first&.strip return placeholder if first_line.nil? first_line.length > length ? "#{first_line[0...length]}..." : first_line end |
.field(label, value, locale) ⇒ Object
A "Label:\nvalue" Slack mrkdwn field.
The bold-label-newline-value shape is Slack markup, not language, so it stays here rather than being baked into each translation — a translator editing "%label:" could break the formatting for every field at once. Only the label itself is translated.
34 35 36 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 34 def field(label, value, locale) "*#{t("red.notifications.error_alert.labels.#{label}", locale)}:*\n#{value}" end |
.format_datetime(time, locale) ⇒ String
A human-readable timestamp for a notification body.
Uses the locale's date format and month names rather than strftime's, which are always English (see MailerI18nHelper for the same problem). Falls back to the machine format if anything goes wrong — a wrong-looking timestamp is better than a lost notification.
66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 66 def format_datetime(time, locale) return "" if time.nil? utc = time.respond_to?(:utc) ? time.utc : time pattern = I18nStore.translate("red.time.formats.full", locale: locale) # A miss returns humanized key text, which would be a nonsense strftime # pattern. Detect it the same way red_time_format does. pattern = I18nStore.translate("red.time.formats.full", locale: I18nStore::DEFAULT_LOCALE) unless pattern.include?("%") LocalizedTimeFormatter.call(utc, pattern: pattern, locale: locale) rescue StandardError format_time(time) end |
.format_datetime_or_na(time, locale) ⇒ Object
A localized timestamp, or the localized "N/A" for nil.
81 82 83 84 85 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 81 def format_datetime_or_na(time, locale) return not_available(locale) if time.nil? format_datetime(time, locale) end |
.format_time(time) ⇒ String
Format time for display
160 161 162 163 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 160 def format_time(time) return "N/A" if time.nil? time.strftime("%Y-%m-%d %H:%M:%S UTC") end |
.label(label, locale) ⇒ Object
One label from red.notifications.error_alert.labels.
Discord wants a bare field name, Slack wants it wrapped in its mrkdwn bold-and-newline shape — hence this and #field rather than one method.
44 45 46 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 44 def label(label, locale) t("red.notifications.error_alert.labels.#{label}", locale) end |
.not_available(locale) ⇒ String
Returns the localized "N/A" placeholder.
54 55 56 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 54 def not_available(locale) t("red.notifications.shared.not_available", locale) end |
.parse_request_params(params_json) ⇒ Hash
Parse request params JSON safely
168 169 170 171 172 173 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 168 def parse_request_params(params_json) return {} if params_json.nil? JSON.parse(params_json) rescue JSON::ParserError {} end |
.platform_emoji(platform) ⇒ String
Platform emoji for Slack/text notifications
148 149 150 151 152 153 154 155 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 148 def platform_emoji(platform) case platform&.downcase when "ios" then "📱" when "android" then "🤖" when "api" then "🔌" else "💻" end end |
.t(key, locale, **options) ⇒ String
Translate for a notification payload.
Positional locale rather than the ambient one: a payload is built inside a job, where Current.locale is nil at best and an unrelated request's value at worst (P4-T1). Every builder threads the locale it was handed.
No escaping — these values go into JSON, not HTML.
22 23 24 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 22 def t(key, locale, **) I18nStore.translate(key, locale: locale, **) end |
.truncate_message(message, length = 500) ⇒ String
Truncate a message to a maximum length
107 108 109 110 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 107 def (, length = 500) return "" unless .length > length ? "#{[0...length]}..." : end |
.unknown(locale) ⇒ String
Returns the localized "Unknown" placeholder.
49 50 51 |
# File 'lib/rails_error_dashboard/services/notification_helpers.rb', line 49 def unknown(locale) t("red.notifications.shared.unknown", locale) end |