activerabbit-ai 0.6.1 → 0.6.3
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 +4 -4
- data/CHANGELOG.md +21 -0
- data/lib/active_rabbit/client/action_mailer_patch.rb +22 -6
- data/lib/active_rabbit/client/cron_monitor.rb +65 -0
- data/lib/active_rabbit/client/exception_tracker.rb +3 -0
- data/lib/active_rabbit/client/http_client.rb +14 -2
- data/lib/active_rabbit/client/version.rb +1 -1
- data/lib/active_rabbit/client.rb +19 -0
- metadata +4 -7
- data/activerabbit-ai-0.4.5.gem +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d02e83fd5992873b3b7aabb322ab9e3c914b91e7e64b4a80be335007561bc404
|
|
4
|
+
data.tar.gz: d60e55a0fcab7c981d7b7a214cff142825c613e7d32ddf15533bccd451cb6657
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32a57acfde857e55a97932f56ae1a449f04a7e51e7ad8903d2cf63380ddd338cf0930d18785559f2bf358250470ce1890f503dde670aaebddf1756f6b5675c2f
|
|
7
|
+
data.tar.gz: dad613e6edf130e26c185b2b17670366400c4a40a36ebf0d8017a77189f01c1647c0cbd3ef7f810727a47beccf46b5f482394b681424982ad64a020eb1bcd768
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.6.3] - 2026-03-30
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **HTTP client request id**: `make_request` sends a stable `X-Request-Id` header (same value across retries) so APIs can correlate or deduplicate requests after timeouts and transient failures.
|
|
9
|
+
- **Explicit `securerandom` load**: `HttpClient` requires `securerandom` so `SecureRandom` is always available regardless of load order.
|
|
10
|
+
|
|
11
|
+
## [0.6.2] - 2026-01-26
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- **ActionMailer deliver_later RuntimeError**: Fixed critical bug where `deliver_later` would raise `RuntimeError: You've accessed the message before asking to deliver it later`
|
|
15
|
+
- The patch was incorrectly accessing `message.subject` and `message.to` before calling `super`
|
|
16
|
+
- Rails requires that `message` is NOT accessed before `deliver_later` because only mailer method arguments are passed to the job
|
|
17
|
+
- Now only tracks safe metadata: `@mailer_class`, `@action`, and argument types
|
|
18
|
+
- Added error handling to ensure tracking failures don't break email delivery
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **ActionMailer patch tests**: 18 comprehensive tests for the ActionMailer integration
|
|
22
|
+
- Verifies `deliver_later` does NOT access the message object
|
|
23
|
+
- Verifies `deliver_now` correctly tracks message details
|
|
24
|
+
- Tests error handling and edge cases
|
|
25
|
+
|
|
5
26
|
## [0.6.1] - 2026-01-09
|
|
6
27
|
|
|
7
28
|
### Fixed
|
|
@@ -14,7 +14,8 @@ module ActiveRabbit
|
|
|
14
14
|
ActiveRabbit::Client.track_event(
|
|
15
15
|
"email_sent",
|
|
16
16
|
{
|
|
17
|
-
|
|
17
|
+
mailer_class: @mailer_class.name,
|
|
18
|
+
action: @action,
|
|
18
19
|
message_id: (message.message_id rescue nil),
|
|
19
20
|
subject: (message.subject rescue nil),
|
|
20
21
|
to: (Array(message.to).first rescue nil),
|
|
@@ -24,11 +25,26 @@ module ActiveRabbit
|
|
|
24
25
|
end
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
def deliver_later
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
def deliver_later(...)
|
|
29
|
+
# IMPORTANT: Do NOT access `message` here!
|
|
30
|
+
# Rails raises RuntimeError if you access the message before deliver_later
|
|
31
|
+
# because only mailer method arguments are passed to the job.
|
|
32
|
+
# We can only safely access metadata that doesn't touch the message object.
|
|
33
|
+
if ActiveRabbit::Client.configured?
|
|
34
|
+
begin
|
|
35
|
+
ActiveRabbit::Client.track_event(
|
|
36
|
+
"email_enqueued",
|
|
37
|
+
{
|
|
38
|
+
mailer_class: @mailer_class.name,
|
|
39
|
+
action: @action,
|
|
40
|
+
args: @args&.map { |a| a.class.name }
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
rescue => e
|
|
44
|
+
# Don't let tracking failures break email delivery
|
|
45
|
+
Rails.logger.error "[ActiveRabbit] Failed to track email_enqueued: #{e.message}" if defined?(Rails) && Rails.logger
|
|
46
|
+
end
|
|
47
|
+
end
|
|
32
48
|
super
|
|
33
49
|
end
|
|
34
50
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRabbit
|
|
4
|
+
module Client
|
|
5
|
+
# Sentry-style cron check-ins for Active Job: in_progress at start, ok on success, error on failure.
|
|
6
|
+
#
|
|
7
|
+
# class BackupJob < ApplicationJob
|
|
8
|
+
# include ActiveRabbit::Client::CronMonitor
|
|
9
|
+
# active_rabbit_cron slug: "nightly_backup"
|
|
10
|
+
#
|
|
11
|
+
# def perform
|
|
12
|
+
# # ...
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
module CronMonitor
|
|
16
|
+
def self.included(base)
|
|
17
|
+
base.extend(ClassMethods)
|
|
18
|
+
base.class_eval do
|
|
19
|
+
around_perform :_active_rabbit_cron_monitor_around
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module ClassMethods
|
|
24
|
+
# @param value [String, Symbol, Hash] string/symbol slug, or { slug: "..." }
|
|
25
|
+
def active_rabbit_cron(value = nil)
|
|
26
|
+
case value
|
|
27
|
+
when String, Symbol
|
|
28
|
+
@active_rabbit_cron_slug = value.to_s.strip
|
|
29
|
+
when Hash
|
|
30
|
+
s = value[:slug].to_s.strip
|
|
31
|
+
@active_rabbit_cron_slug = s.empty? ? nil : s
|
|
32
|
+
when nil
|
|
33
|
+
@active_rabbit_cron_slug = nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def active_rabbit_cron_monitor_slug
|
|
38
|
+
slug = instance_variable_defined?(:@active_rabbit_cron_slug) ? @active_rabbit_cron_slug : nil
|
|
39
|
+
return slug if slug.is_a?(String) && !slug.strip.empty?
|
|
40
|
+
|
|
41
|
+
name.to_s.underscore.tr("/", "_")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def _active_rabbit_cron_monitor_around(&block)
|
|
48
|
+
slug = self.class.active_rabbit_cron_monitor_slug
|
|
49
|
+
if slug.to_s.strip.empty? || !ActiveRabbit::Client.configured?
|
|
50
|
+
block.call
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
ActiveRabbit::Client.capture_cron_check_in(slug, :in_progress)
|
|
55
|
+
begin
|
|
56
|
+
block.call
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
ActiveRabbit::Client.capture_cron_check_in(slug, :error)
|
|
59
|
+
raise e
|
|
60
|
+
end
|
|
61
|
+
ActiveRabbit::Client.capture_cron_check_in(slug, :ok)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -131,6 +131,9 @@ module ActiveRabbit
|
|
|
131
131
|
structured_stack_trace: structured_frames,
|
|
132
132
|
culprit_frame: culprit_frame,
|
|
133
133
|
|
|
134
|
+
# Error source: backend (Ruby gem) vs frontend (JS SDK)
|
|
135
|
+
source: "backend",
|
|
136
|
+
|
|
134
137
|
# Timing and environment
|
|
135
138
|
occurred_at: Time.now.iso8601(3),
|
|
136
139
|
environment: configuration.environment || 'development',
|
|
@@ -6,6 +6,7 @@ require "json"
|
|
|
6
6
|
require "concurrent"
|
|
7
7
|
require "time"
|
|
8
8
|
require "uri"
|
|
9
|
+
require "securerandom"
|
|
9
10
|
|
|
10
11
|
module ActiveRabbit
|
|
11
12
|
module Client
|
|
@@ -140,6 +141,12 @@ module ActiveRabbit
|
|
|
140
141
|
{ success: false, error: e.message }
|
|
141
142
|
end
|
|
142
143
|
|
|
144
|
+
# Cron / heartbeat check-in (project token + monitor slug). Synchronous, not batched.
|
|
145
|
+
def post_cron_check_in(slug:, status: :ok)
|
|
146
|
+
payload = stringify_and_sanitize({ slug: slug.to_s, status: status.to_s })
|
|
147
|
+
make_request(:post, "/api/v1/cron/check_ins", payload)
|
|
148
|
+
end
|
|
149
|
+
|
|
143
150
|
def flush
|
|
144
151
|
return if @request_queue.empty?
|
|
145
152
|
|
|
@@ -225,12 +232,16 @@ module ActiveRabbit
|
|
|
225
232
|
log(:debug, "[ActiveRabbit] Request headers: X-Project-Token=#{configuration.api_key}, X-Project-ID=#{configuration.project_id}")
|
|
226
233
|
log(:debug, "[ActiveRabbit] Request body: #{safe_preview(data)}")
|
|
227
234
|
|
|
235
|
+
# Generate a stable request ID for dedup — same ID is reused across retries
|
|
236
|
+
# so the server can reject duplicates when we retry after a timeout.
|
|
237
|
+
request_id = SecureRandom.uuid
|
|
238
|
+
|
|
228
239
|
# Retry logic with exponential backoff
|
|
229
240
|
retries = 0
|
|
230
241
|
max_retries = configuration.retry_count
|
|
231
242
|
|
|
232
243
|
begin
|
|
233
|
-
response = perform_request(uri, method, data)
|
|
244
|
+
response = perform_request(uri, method, data, request_id: request_id)
|
|
234
245
|
log(:info, "[ActiveRabbit] Response status: #{response.code}")
|
|
235
246
|
log(:debug, "[ActiveRabbit] Response headers: #{response.to_hash.inspect}")
|
|
236
247
|
log(:debug, "[ActiveRabbit] Response body: #{response.body}")
|
|
@@ -273,7 +284,7 @@ module ActiveRabbit
|
|
|
273
284
|
end
|
|
274
285
|
end
|
|
275
286
|
|
|
276
|
-
def perform_request(uri, method, data)
|
|
287
|
+
def perform_request(uri, method, data, request_id: nil)
|
|
277
288
|
log(:debug, "[ActiveRabbit] Making HTTP request: #{method.upcase} #{uri}")
|
|
278
289
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
279
290
|
|
|
@@ -308,6 +319,7 @@ module ActiveRabbit
|
|
|
308
319
|
request['Accept'] = 'application/json'
|
|
309
320
|
request['User-Agent'] = "ActiveRabbit-Client/#{ActiveRabbit::Client::VERSION}"
|
|
310
321
|
request['X-Project-Token'] = configuration.api_key
|
|
322
|
+
request['X-Request-Id'] = request_id if request_id
|
|
311
323
|
|
|
312
324
|
if configuration.project_id
|
|
313
325
|
request['X-Project-ID'] = configuration.project_id
|
data/lib/active_rabbit/client.rb
CHANGED
|
@@ -24,6 +24,12 @@ rescue LoadError
|
|
|
24
24
|
# Sidekiq not available, skip integration
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# Active Job cron monitors (optional)
|
|
28
|
+
begin
|
|
29
|
+
require_relative "client/cron_monitor" if defined?(ActiveJob)
|
|
30
|
+
rescue LoadError
|
|
31
|
+
end
|
|
32
|
+
|
|
27
33
|
module ActiveRabbit
|
|
28
34
|
module Client
|
|
29
35
|
class Error < StandardError; end
|
|
@@ -144,6 +150,19 @@ module ActiveRabbit
|
|
|
144
150
|
http_client.post_release(payload)
|
|
145
151
|
end
|
|
146
152
|
|
|
153
|
+
# Cron / heartbeat monitor (uses project API token + slug). Status: :ok, :success, :in_progress, :error.
|
|
154
|
+
def capture_cron_check_in(slug, status = :ok)
|
|
155
|
+
return unless configured?
|
|
156
|
+
|
|
157
|
+
s = slug.to_s.strip
|
|
158
|
+
return nil if s.empty?
|
|
159
|
+
|
|
160
|
+
http_client.post_cron_check_in(slug: s, status: status)
|
|
161
|
+
rescue => e
|
|
162
|
+
log(:error, "[ActiveRabbit] capture_cron_check_in failed: #{e.class}: #{e.message}")
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
|
|
147
166
|
def log(level, message)
|
|
148
167
|
cfg = configuration
|
|
149
168
|
return if cfg.nil? || cfg.disable_console_logs
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerabbit-ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Shapalov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: concurrent-ruby
|
|
@@ -81,7 +80,6 @@ files:
|
|
|
81
80
|
- README.md
|
|
82
81
|
- Rakefile
|
|
83
82
|
- TESTING_GUIDE.md
|
|
84
|
-
- activerabbit-ai-0.4.5.gem
|
|
85
83
|
- check_api_data.rb
|
|
86
84
|
- examples/rails_app_testing.rb
|
|
87
85
|
- examples/rails_integration.rb
|
|
@@ -91,6 +89,7 @@ files:
|
|
|
91
89
|
- lib/active_rabbit/client/action_mailer_patch.rb
|
|
92
90
|
- lib/active_rabbit/client/active_job_extensions.rb
|
|
93
91
|
- lib/active_rabbit/client/configuration.rb
|
|
92
|
+
- lib/active_rabbit/client/cron_monitor.rb
|
|
94
93
|
- lib/active_rabbit/client/dedupe.rb
|
|
95
94
|
- lib/active_rabbit/client/error_reporter.rb
|
|
96
95
|
- lib/active_rabbit/client/event_processor.rb
|
|
@@ -121,7 +120,6 @@ metadata:
|
|
|
121
120
|
allowed_push_host: https://rubygems.org
|
|
122
121
|
source_code_uri: https://github.com/bugrabbit/active_rabbit-client
|
|
123
122
|
changelog_uri: https://github.com/bugrabbit/active_rabbit-client/blob/main/CHANGELOG.md
|
|
124
|
-
post_install_message:
|
|
125
123
|
rdoc_options: []
|
|
126
124
|
require_paths:
|
|
127
125
|
- lib
|
|
@@ -136,8 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
136
134
|
- !ruby/object:Gem::Version
|
|
137
135
|
version: '0'
|
|
138
136
|
requirements: []
|
|
139
|
-
rubygems_version:
|
|
140
|
-
signing_key:
|
|
137
|
+
rubygems_version: 4.0.7
|
|
141
138
|
specification_version: 4
|
|
142
139
|
summary: Ruby client for ActiveRabbit.ai application monitoring and error tracking
|
|
143
140
|
test_files: []
|
data/activerabbit-ai-0.4.5.gem
DELETED
|
Binary file
|