error_radar 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57a9bb51c11180d90dfedb8bce99c6f6c989c725bc44991a5c6ca42812148713
4
- data.tar.gz: d4a31ea7b1e3317ddd392584099d5ce7811dd32468985a4d8f8e18c3498c05c0
3
+ metadata.gz: b7f170a894e4a6b29a0c95c2fc96630ba5ed1412eb7ee67bee2b547007bcc5c1
4
+ data.tar.gz: 89574f8768d969fbd5e73c56d50d6f502f700ead1e258bd475db9164c6d298df
5
5
  SHA512:
6
- metadata.gz: fc4dcc84da998c26f0f348149e9d50b36be365b91f37cd69769a66afd9f0fe8d94c9a5e630496930150acfdb9d9f5b0ec37daef145e00b090e3817ad6d77eacc
7
- data.tar.gz: fc4b1b73d7f3c4115c6ee3f00f12e8ab48420973ec41bd958ab9edafa27a36dce425d6c69e84a28eeacffc683d06f6cdce270dd06ab8759201b6c042aee44850
6
+ metadata.gz: beee5c8c764906d4bfcf82c3726b29a0c4e8c226174c68c166af5ff0ae622bce4167644b8d7b202e825b8c481084c1defb00b3088e9c9af55a9eaec4cfdca882
7
+ data.tar.gz: f337f436c786420fd9c9f13aec3d838e26d8f85365c13d4d30e3718204d2fb7166a692aa3667611388266fe8372a14ebfbd3cf908bca7e2d5e50cc1f87d7fd63
data/CHANGELOG.md CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.4] - 2026-07-03
6
+
7
+ ### Fixed
8
+ - **Rake integration: `SystemStackError` / infinite recursion on `rake db:migrate`**
9
+ Three compounding issues caused deployment to fail:
10
+
11
+ 1. **Double-patching infinite loop** — if the host app's `lib/tasks/error_radar.rake`
12
+ contained an older version of the Rake integration (using `alias_method`), the
13
+ gem's own `alias_method :error_radar_original_execute, :execute` in v1.0.3
14
+ overwrote the alias so both copies called each other indefinitely. Fixed by
15
+ switching to `prepend` with an `ancestors.include?` idempotency guard — `prepend`
16
+ does not touch any existing aliases and composes cleanly with prior patches.
17
+
18
+ 2. **Recursive capture** — if `ErrorRadar.capture` itself raised (e.g. because the
19
+ model wasn't loaded yet), the exception propagated back into the rescue block and
20
+ triggered `capture` again. Fixed with a thread-local `error_radar_rake_capturing`
21
+ guard and by never trying to capture `SystemStackError` / `SignalException` /
22
+ `NoMemoryError` (unrecoverable errors).
23
+
24
+ 3. **AR 7.1 `respond_to_missing?` recursion** — ActiveRecord 7.1's
25
+ `detect_enum_conflict!` calls `dangerous_class_method?` → `Base.respond_to?`
26
+ → `respond_to_missing?` which can recurse infinitely when the table does not
27
+ exist yet (first deploy). Added `_scopes: false` to all three enum declarations
28
+ so the class-method conflict-detection path is skipped entirely. Also changed
29
+ `rescue StandardError` → `rescue Exception` in `Tracking.capture` so
30
+ `SystemStackError` is always absorbed and never escapes `capture`.
31
+
32
+ - **`Api::StatsController` used enum-generated scopes** (`ErrorLog.status_open`,
33
+ etc.) which no longer exist with `_scopes: false`. Replaced with explicit
34
+ `where(status: ErrorLog.statuses[:open])` calls.
35
+
36
+ **If you have a `lib/tasks/error_radar.rake` file in your host app** (not inside
37
+ the gem), delete it — the engine loads the rake tasks automatically from the gem,
38
+ and keeping a copy in the host app causes tasks to be defined twice.
39
+
40
+ ## [1.0.3] - 2026-07-03
41
+
42
+ ### Fixed
43
+ - **`::Digest::SHA1` namespace clash**: `ErrorRadar::Digest` (our digest mailer
44
+ module) was shadowing Ruby stdlib's `Digest` inside `ErrorLog.build_fingerprint`,
45
+ causing `NameError: uninitialized constant ErrorRadar::Digest::SHA1` on every
46
+ captured error. Fixed by using `::Digest::SHA1` (root-level constant path).
47
+ - **`ErrorLog` unresolved in views**: ERB templates run in `ActionView::Base`
48
+ context — outside the `ErrorRadar` module — so bare `ErrorLog` raised
49
+ `NameError: uninitialized constant ErrorLog` on the All Errors filter bar.
50
+ Changed to fully-qualified `ErrorRadar::ErrorLog` in the view.
51
+
5
52
  ## [1.0.2] - 2026-07-03
6
53
 
7
54
  ### Fixed
@@ -7,10 +7,10 @@ module ErrorRadar
7
7
  def show
8
8
  render json: {
9
9
  total: ErrorLog.count,
10
- open: ErrorLog.status_open.count,
11
- in_progress: ErrorLog.status_in_progress.count,
12
- resolved: ErrorLog.status_resolved.count,
13
- ignored: ErrorLog.status_ignored.count,
10
+ open: ErrorLog.where(status: ErrorLog.statuses[:open]).count,
11
+ in_progress: ErrorLog.where(status: ErrorLog.statuses[:in_progress]).count,
12
+ resolved: ErrorLog.where(status: ErrorLog.statuses[:resolved]).count,
13
+ ignored: ErrorLog.where(status: ErrorLog.statuses[:ignored]).count,
14
14
  unresolved: ErrorLog.unresolved.count,
15
15
  by_severity: ErrorLog.group(:severity).count,
16
16
  by_category: ErrorLog.group(:category).count,
@@ -10,11 +10,15 @@ module ErrorRadar
10
10
  # registers via ErrorRadar.config). Read at class-load, which happens after
11
11
  # the host's initializer has run `ErrorRadar.configure`, so custom
12
12
  # categories are already merged in. See ErrorRadar::Configuration.
13
- enum category: ErrorRadar.config.categories, _prefix: :category
13
+ #
14
+ # _scopes: false avoids the class-method conflict-detection path in AR 7.1
15
+ # (detect_enum_conflict! → dangerous_class_method? → respond_to_missing?)
16
+ # that raises SystemStackError when the table does not yet exist.
17
+ enum category: ErrorRadar.config.categories, _prefix: :category, _scopes: false
14
18
 
15
- enum severity: { info: 0, warning: 1, error: 2, critical: 3 }, _prefix: :severity
19
+ enum severity: { info: 0, warning: 1, error: 2, critical: 3 }, _prefix: :severity, _scopes: false
16
20
 
17
- enum status: { open: 0, in_progress: 1, resolved: 2, ignored: 3 }, _prefix: :status
21
+ enum status: { open: 0, in_progress: 1, resolved: 2, ignored: 3 }, _prefix: :status, _scopes: false
18
22
 
19
23
  has_many :error_occurrences, class_name: 'ErrorRadar::ErrorOccurrence',
20
24
  foreign_key: :error_log_id,
@@ -95,7 +99,7 @@ module ErrorRadar
95
99
  .gsub(/0x[0-9a-f]+/i, '0x#') # object addresses
96
100
  .gsub(/[0-9a-f]{8}-[0-9a-f-]{27}/i, '#') # uuids
97
101
  .strip
98
- Digest::SHA1.hexdigest([category, error_class, source, normalized].join('|'))
102
+ ::Digest::SHA1.hexdigest([category, error_class, source, normalized].join('|'))
99
103
  end
100
104
 
101
105
  def self.severity_rank(value)
@@ -70,21 +70,21 @@
70
70
 
71
71
  <select name="status">
72
72
  <option value="">All statuses</option>
73
- <% ErrorLog.statuses.each_key do |s| %>
73
+ <% ErrorRadar::ErrorLog.statuses.each_key do |s| %>
74
74
  <option value="<%= s %>" <%= 'selected' if fp['status'] == s %>><%= s.humanize %></option>
75
75
  <% end %>
76
76
  </select>
77
77
 
78
78
  <select name="severity">
79
79
  <option value="">All severities</option>
80
- <% ErrorLog.severities.each_key do |s| %>
80
+ <% ErrorRadar::ErrorLog.severities.each_key do |s| %>
81
81
  <option value="<%= s %>" <%= 'selected' if fp['severity'] == s %>><%= s.capitalize %></option>
82
82
  <% end %>
83
83
  </select>
84
84
 
85
85
  <select name="category">
86
86
  <option value="">All categories</option>
87
- <% ErrorLog.categories.each_key do |s| %>
87
+ <% ErrorRadar::ErrorLog.categories.each_key do |s| %>
88
88
  <option value="<%= s %>" <%= 'selected' if fp['category'] == s.to_s %>><%= s.to_s.humanize %></option>
89
89
  <% end %>
90
90
  </select>
@@ -2,31 +2,44 @@
2
2
 
3
3
  module ErrorRadar
4
4
  module Integrations
5
- # Patches Rake::Task#execute so any exception raised inside a rake task is
6
- # automatically captured as an ErrorLog, then re-raised so rake's normal
7
- # failure handling (exit code, output) is unaffected.
8
5
  module Rake
9
- def self.install!
10
- return if @installed
11
-
12
- @installed = true
6
+ # Prepended into Rake::Task to auto-capture exceptions raised inside any
7
+ # task body. Uses `prepend` (not alias_method) so it composes safely with
8
+ # any other patches already in place — including older versions of this
9
+ # integration that may be in the host app's lib/tasks/error_radar.rake.
10
+ module Capture
11
+ def execute(args = nil)
12
+ super
13
+ rescue Exception => e # rubocop:disable Lint/RescueException
14
+ # Never try to capture signals or stack overflows — we can't recover.
15
+ raise if e.is_a?(SignalException) || e.is_a?(SystemStackError) || e.is_a?(NoMemoryError)
13
16
 
14
- ::Rake::Task.class_eval do
15
- alias_method :error_radar_original_execute, :execute
16
-
17
- def execute(args = nil)
18
- error_radar_original_execute(args)
19
- rescue Exception => e # rubocop:disable Lint/RescueException
20
- ErrorRadar.capture(
21
- e,
22
- source: "rake:#{name}",
23
- category: :background_job,
24
- context: { task: name }
25
- )
26
- raise
17
+ # Thread-local guard: if capture itself fails and re-raises, we must
18
+ # not enter an infinite rescue loop.
19
+ unless Thread.current[:error_radar_rake_capturing]
20
+ Thread.current[:error_radar_rake_capturing] = true
21
+ begin
22
+ ErrorRadar.capture(
23
+ e,
24
+ source: "rake:#{name}",
25
+ category: :background_job,
26
+ context: { task: name }
27
+ )
28
+ ensure
29
+ Thread.current[:error_radar_rake_capturing] = false
30
+ end
27
31
  end
32
+ raise
28
33
  end
29
34
  end
35
+
36
+ def self.install!
37
+ # `ancestors.include?` is idempotent across multiple calls AND across
38
+ # multiple versions of the gem that may both try to install.
39
+ return if ::Rake::Task.ancestors.include?(Capture)
40
+
41
+ ::Rake::Task.prepend(Capture)
42
+ end
30
43
  end
31
44
  end
32
45
  end
@@ -41,7 +41,7 @@ module ErrorRadar
41
41
  log = ErrorRadar::ErrorLog.record(**attrs)
42
42
  ErrorRadar::Notifier.dispatch(log) if log
43
43
  log
44
- rescue StandardError => e
44
+ rescue Exception => e # rubocop:disable Lint/RescueException
45
45
  warn_internal("capture failed: #{e.class}: #{e.message}")
46
46
  nil
47
47
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ErrorRadar
4
- VERSION = '1.0.2'
4
+ VERSION = '1.0.4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: error_radar
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - chienbn9x