activerabbit-ai 0.1.0

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.
@@ -0,0 +1,437 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example Rails Application Testing with ActiveRabbit
4
+ # This file shows how to test ActiveRabbit integration in your Rails app
5
+
6
+ # spec/support/active_rabbit_helpers.rb
7
+ module ActiveRabbitHelpers
8
+ def setup_active_rabbit_test
9
+ ActiveRabbit::Client.configure do |config|
10
+ config.api_key = "test-api-key"
11
+ config.project_id = "test-project"
12
+ config.api_url = "https://api.activerabbit.com"
13
+ config.environment = "test"
14
+ end
15
+
16
+ # Stub all API calls
17
+ stub_active_rabbit_api
18
+ end
19
+
20
+ def stub_active_rabbit_api
21
+ stub_request(:post, "https://api.activerabbit.com/api/v1/exceptions")
22
+ .to_return(status: 200, body: '{"status":"ok"}')
23
+
24
+ stub_request(:post, "https://api.activerabbit.com/api/v1/events")
25
+ .to_return(status: 200, body: '{"status":"ok"}')
26
+
27
+ stub_request(:post, "https://api.activerabbit.com/api/v1/performance")
28
+ .to_return(status: 200, body: '{"status":"ok"}')
29
+
30
+ stub_request(:post, "https://api.activerabbit.com/api/v1/batch")
31
+ .to_return(status: 200, body: '{"status":"ok"}')
32
+ end
33
+
34
+ def expect_exception_tracked(exception_type: nil, message: nil, context: nil)
35
+ expect(WebMock).to have_requested(:post, "https://api.activerabbit.com/api/v1/exceptions")
36
+ .with { |request|
37
+ body = JSON.parse(request.body)
38
+
39
+ result = true
40
+ result &&= body["type"] == exception_type if exception_type
41
+ result &&= body["message"].include?(message) if message
42
+
43
+ if context
44
+ context.each do |key, value|
45
+ result &&= body.dig("context", key.to_s) == value
46
+ end
47
+ end
48
+
49
+ result
50
+ }
51
+ end
52
+
53
+ def expect_event_tracked(event_name, properties: nil)
54
+ expect(WebMock).to have_requested(:post, "https://api.activerabbit.com/api/v1/events")
55
+ .with { |request|
56
+ body = JSON.parse(request.body)
57
+
58
+ result = body["name"] == event_name
59
+
60
+ if properties
61
+ properties.each do |key, value|
62
+ result &&= body.dig("properties", key.to_s) == value
63
+ end
64
+ end
65
+
66
+ result
67
+ }
68
+ end
69
+
70
+ def expect_performance_tracked(operation_name, min_duration: nil)
71
+ expect(WebMock).to have_requested(:post, "https://api.activerabbit.com/api/v1/performance")
72
+ .with { |request|
73
+ body = JSON.parse(request.body)
74
+
75
+ result = body["name"] == operation_name
76
+ result &&= body["duration_ms"] >= min_duration if min_duration
77
+
78
+ result
79
+ }
80
+ end
81
+ end
82
+
83
+ # Include in RSpec configuration
84
+ # spec/rails_helper.rb
85
+ RSpec.configure do |config|
86
+ config.include ActiveRabbitHelpers
87
+
88
+ config.before(:each) do
89
+ setup_active_rabbit_test
90
+ end
91
+
92
+ config.after(:each) do
93
+ ActiveRabbit::Client.configuration = nil
94
+ Thread.current[:active_rabbit_request_context] = nil
95
+ end
96
+ end
97
+
98
+ # Example controller test
99
+ # spec/controllers/users_controller_spec.rb
100
+ RSpec.describe UsersController, type: :controller do
101
+ describe "GET #show" do
102
+ context "when user exists" do
103
+ let(:user) { create(:user) }
104
+
105
+ it "returns the user successfully" do
106
+ get :show, params: { id: user.id }
107
+
108
+ expect(response).to have_http_status(:success)
109
+
110
+ # Should track performance but no exceptions
111
+ expect_performance_tracked("controller.action")
112
+ expect(WebMock).not_to have_requested(:post, /exceptions/)
113
+ end
114
+ end
115
+
116
+ context "when user does not exist" do
117
+ it "tracks the RecordNotFound exception" do
118
+ expect {
119
+ get :show, params: { id: 999999 }
120
+ }.to raise_error(ActiveRecord::RecordNotFound)
121
+
122
+ expect_exception_tracked(
123
+ exception_type: "ActiveRecord::RecordNotFound",
124
+ message: "Couldn't find User",
125
+ context: {
126
+ "request" => hash_including(
127
+ "method" => "GET",
128
+ "path" => "/users/999999"
129
+ )
130
+ }
131
+ )
132
+ end
133
+ end
134
+ end
135
+
136
+ describe "POST #create" do
137
+ context "with valid parameters" do
138
+ let(:valid_params) { { user: { name: "John Doe", email: "john@example.com" } } }
139
+
140
+ it "creates user and tracks success event" do
141
+ expect {
142
+ post :create, params: valid_params
143
+ }.to change(User, :count).by(1)
144
+
145
+ expect_event_tracked(
146
+ "user_created",
147
+ properties: {
148
+ "source" => "web"
149
+ }
150
+ )
151
+ end
152
+ end
153
+
154
+ context "with invalid parameters" do
155
+ let(:invalid_params) { { user: { name: "" } } }
156
+
157
+ it "tracks validation failure" do
158
+ post :create, params: invalid_params
159
+
160
+ expect_event_tracked(
161
+ "user_creation_failed",
162
+ properties: {
163
+ "errors" => array_including("Name can't be blank")
164
+ }
165
+ )
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ # Example model test
172
+ # spec/models/user_spec.rb
173
+ RSpec.describe User, type: :model do
174
+ describe "callbacks" do
175
+ it "tracks user creation event" do
176
+ user = create(:user)
177
+
178
+ expect_event_tracked(
179
+ "model_user_created",
180
+ properties: {
181
+ "id" => user.id
182
+ }
183
+ )
184
+ end
185
+
186
+ it "tracks user status changes" do
187
+ user = create(:user, status: "pending")
188
+ user.update(status: "active")
189
+
190
+ expect_event_tracked(
191
+ "user_status_changed",
192
+ properties: {
193
+ "from_status" => "pending",
194
+ "to_status" => "active"
195
+ }
196
+ )
197
+ end
198
+ end
199
+
200
+ describe "error handling" do
201
+ it "tracks validation errors" do
202
+ user = build(:user, email: "invalid-email")
203
+
204
+ expect(user.valid?).to be false
205
+
206
+ # If you've added custom tracking for validation errors
207
+ expect_event_tracked(
208
+ "validation_failed",
209
+ properties: {
210
+ "model" => "User",
211
+ "errors" => ["Email is invalid"]
212
+ }
213
+ )
214
+ end
215
+ end
216
+ end
217
+
218
+ # Example job test
219
+ # spec/jobs/user_notification_job_spec.rb
220
+ RSpec.describe UserNotificationJob, type: :job do
221
+ let(:user) { create(:user) }
222
+
223
+ describe "#perform" do
224
+ context "when job succeeds" do
225
+ it "tracks job completion" do
226
+ perform_enqueued_jobs do
227
+ UserNotificationJob.perform_later(user.id)
228
+ end
229
+
230
+ expect_event_tracked("sidekiq_job_completed")
231
+ expect_performance_tracked("sidekiq.job")
232
+ end
233
+ end
234
+
235
+ context "when job fails" do
236
+ before do
237
+ allow_any_instance_of(UserNotificationJob).to receive(:perform)
238
+ .and_raise(StandardError, "Email service unavailable")
239
+ end
240
+
241
+ it "tracks job failure and exception" do
242
+ expect {
243
+ perform_enqueued_jobs do
244
+ UserNotificationJob.perform_later(user.id)
245
+ end
246
+ }.to raise_error(StandardError, "Email service unavailable")
247
+
248
+ expect_exception_tracked(
249
+ exception_type: "StandardError",
250
+ message: "Email service unavailable"
251
+ )
252
+
253
+ expect_event_tracked("sidekiq_job_failed")
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ # Example feature test
260
+ # spec/features/user_registration_spec.rb
261
+ RSpec.describe "User Registration", type: :feature do
262
+ scenario "successful user registration" do
263
+ visit new_user_registration_path
264
+
265
+ fill_in "Name", with: "John Doe"
266
+ fill_in "Email", with: "john@example.com"
267
+ fill_in "Password", with: "password123"
268
+
269
+ click_button "Sign Up"
270
+
271
+ expect(page).to have_content("Welcome, John!")
272
+
273
+ # Verify tracking
274
+ expect_event_tracked(
275
+ "user_signup",
276
+ properties: {
277
+ "source" => "website"
278
+ }
279
+ )
280
+ end
281
+
282
+ scenario "user registration with invalid data" do
283
+ visit new_user_registration_path
284
+
285
+ fill_in "Email", with: "invalid-email"
286
+ click_button "Sign Up"
287
+
288
+ expect(page).to have_content("Email is invalid")
289
+
290
+ # Should track the validation failure
291
+ expect_event_tracked("user_signup_failed")
292
+ end
293
+
294
+ scenario "handles server errors gracefully" do
295
+ # Simulate a server error during registration
296
+ allow_any_instance_of(UsersController).to receive(:create)
297
+ .and_raise(StandardError, "Database connection failed")
298
+
299
+ visit new_user_registration_path
300
+
301
+ fill_in "Name", with: "John Doe"
302
+ fill_in "Email", with: "john@example.com"
303
+
304
+ expect {
305
+ click_button "Sign Up"
306
+ }.to raise_error(StandardError)
307
+
308
+ # Verify exception was tracked
309
+ expect_exception_tracked(
310
+ exception_type: "StandardError",
311
+ message: "Database connection failed"
312
+ )
313
+ end
314
+ end
315
+
316
+ # Example request test for API endpoints
317
+ # spec/requests/api/users_spec.rb
318
+ RSpec.describe "API::Users", type: :request do
319
+ describe "GET /api/users/:id" do
320
+ let(:user) { create(:user) }
321
+
322
+ context "with valid request" do
323
+ it "returns user data and tracks API usage" do
324
+ get "/api/users/#{user.id}", headers: {
325
+ "Accept" => "application/json",
326
+ "User-Agent" => "MyApp/1.0"
327
+ }
328
+
329
+ expect(response).to have_http_status(:success)
330
+
331
+ expect_event_tracked(
332
+ "api_endpoint_accessed",
333
+ properties: {
334
+ "endpoint" => "/api/users/:id",
335
+ "method" => "GET"
336
+ }
337
+ )
338
+ end
339
+ end
340
+
341
+ context "with invalid user ID" do
342
+ it "returns 404 and tracks the error" do
343
+ get "/api/users/999999", headers: { "Accept" => "application/json" }
344
+
345
+ expect(response).to have_http_status(:not_found)
346
+
347
+ expect_exception_tracked(
348
+ exception_type: "ActiveRecord::RecordNotFound"
349
+ )
350
+ end
351
+ end
352
+ end
353
+
354
+ describe "POST /api/users" do
355
+ context "with rate limiting" do
356
+ it "tracks rate limit violations" do
357
+ # Simulate rate limiting
358
+ allow_any_instance_of(ApplicationController).to receive(:check_rate_limit)
359
+ .and_raise(RateLimitExceeded, "Too many requests")
360
+
361
+ expect {
362
+ post "/api/users", params: { user: { name: "Test" } }
363
+ }.to raise_error(RateLimitExceeded)
364
+
365
+ expect_exception_tracked(
366
+ exception_type: "RateLimitExceeded",
367
+ message: "Too many requests"
368
+ )
369
+ end
370
+ end
371
+ end
372
+ end
373
+
374
+ # Example system test
375
+ # spec/system/error_handling_spec.rb
376
+ RSpec.describe "Error Handling", type: :system do
377
+ before do
378
+ driven_by(:selenium_chrome_headless)
379
+ end
380
+
381
+ scenario "handles JavaScript errors" do
382
+ # If you have JavaScript error tracking
383
+ visit some_page_with_js_error
384
+
385
+ # Trigger JS error
386
+ click_button "Trigger Error"
387
+
388
+ # Verify JS error was tracked (if implemented)
389
+ expect_event_tracked("javascript_error")
390
+ end
391
+
392
+ scenario "handles network timeouts" do
393
+ # Simulate slow external API
394
+ stub_request(:get, "https://external-api.com/data")
395
+ .to_timeout
396
+
397
+ visit page_that_calls_external_api
398
+
399
+ # Should handle gracefully and track the timeout
400
+ expect_exception_tracked(
401
+ exception_type: "Net::TimeoutError"
402
+ )
403
+ end
404
+ end
405
+
406
+ # Performance test example
407
+ # spec/performance/activerabbit_impact_spec.rb
408
+ RSpec.describe "ActiveRabbit Performance Impact" do
409
+ let(:iterations) { 100 }
410
+
411
+ it "has minimal impact on controller actions" do
412
+ # Baseline without ActiveRabbit
413
+ ActiveRabbit::Client.configuration = nil
414
+
415
+ baseline_time = Benchmark.measure do
416
+ iterations.times do
417
+ get "/users/1"
418
+ end
419
+ end
420
+
421
+ # With ActiveRabbit enabled
422
+ setup_active_rabbit_test
423
+
424
+ activerabbit_time = Benchmark.measure do
425
+ iterations.times do
426
+ get "/users/1"
427
+ end
428
+ end
429
+
430
+ # Calculate overhead
431
+ overhead_percent = ((activerabbit_time.real - baseline_time.real) / baseline_time.real) * 100
432
+
433
+ expect(overhead_percent).to be < 5,
434
+ "ActiveRabbit overhead (#{overhead_percent.round(2)}%) exceeds 5% threshold"
435
+ end
436
+ end
437
+
@@ -0,0 +1,243 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example Rails integration for ActiveRabbit::Client
4
+
5
+ # config/initializers/active_rabbit.rb
6
+ ActiveRabbit::Client.configure do |config|
7
+ # Required configuration
8
+ config.api_key = ENV['active_rabbit_API_KEY']
9
+ config.project_id = ENV['active_rabbit_PROJECT_ID']
10
+ config.environment = Rails.env
11
+
12
+ # Optional configuration
13
+ config.api_url = ENV.fetch('active_rabbit_API_URL', 'https://api.activerabbit.com')
14
+ config.release = ENV['HEROKU_SLUG_COMMIT'] || `git rev-parse HEAD`.chomp
15
+
16
+ # Performance settings
17
+ config.enable_performance_monitoring = Rails.env.production?
18
+ config.enable_n_plus_one_detection = true
19
+ config.batch_size = 50
20
+ config.flush_interval = 15
21
+
22
+ # PII protection
23
+ config.enable_pii_scrubbing = true
24
+ config.pii_fields += %w[
25
+ customer_id
26
+ internal_notes
27
+ admin_comments
28
+ ]
29
+
30
+ # Exception filtering
31
+ config.ignored_exceptions += %w[
32
+ MyApp::BusinessLogicError
33
+ MyApp::ExpectedError
34
+ ]
35
+
36
+ # User agent filtering (ignore bots in production)
37
+ if Rails.env.production?
38
+ config.ignored_user_agents += [
39
+ /HeadlessChrome/i,
40
+ /PhantomJS/i,
41
+ /SiteAudit/i
42
+ ]
43
+ end
44
+
45
+ # Callbacks for additional processing
46
+ config.before_send_exception = proc do |exception_data|
47
+ # Add deployment information
48
+ exception_data[:deployment] = {
49
+ version: ENV['APP_VERSION'],
50
+ build_number: ENV['BUILD_NUMBER']
51
+ }
52
+
53
+ # Don't send exceptions in test environment
54
+ return nil if Rails.env.test?
55
+
56
+ exception_data
57
+ end
58
+
59
+ config.before_send_event = proc do |event_data|
60
+ # Add user context if available
61
+ if current_user = Thread.current[:current_user]
62
+ event_data[:user_context] = {
63
+ id: current_user.id,
64
+ plan: current_user.plan,
65
+ created_at: current_user.created_at
66
+ }
67
+ end
68
+
69
+ event_data
70
+ end
71
+ end
72
+
73
+ # app/controllers/application_controller.rb
74
+ class ApplicationController < ActionController::Base
75
+ before_action :set_active_rabbit_context
76
+
77
+ private
78
+
79
+ def set_active_rabbit_context
80
+ # Set current user for ActiveRabbit context
81
+ Thread.current[:current_user] = current_user
82
+
83
+ # Add additional request context
84
+ if Thread.current[:active_rabbit_request_context]
85
+ Thread.current[:active_rabbit_request_context].merge!(
86
+ user_id: current_user&.id,
87
+ user_plan: current_user&.plan,
88
+ tenant_id: current_tenant&.id
89
+ )
90
+ end
91
+ end
92
+ end
93
+
94
+ # app/controllers/orders_controller.rb
95
+ class OrdersController < ApplicationController
96
+ def create
97
+ # Manual exception tracking with context
98
+ begin
99
+ @order = Order.create!(order_params)
100
+
101
+ # Track successful order creation
102
+ ActiveRabbit::Client.track_event(
103
+ 'order_created',
104
+ {
105
+ order_id: @order.id,
106
+ amount: @order.total_amount,
107
+ items_count: @order.items.count,
108
+ payment_method: @order.payment_method
109
+ },
110
+ user_id: current_user.id
111
+ )
112
+
113
+ redirect_to @order, notice: 'Order was successfully created.'
114
+ rescue PaymentProcessor::Error => e
115
+ # Track payment errors with additional context
116
+ ActiveRabbit::Client.track_exception(
117
+ e,
118
+ context: {
119
+ order_params: order_params.to_h,
120
+ payment_method: params[:payment_method],
121
+ user_id: current_user.id
122
+ },
123
+ tags: {
124
+ component: 'payment_processor',
125
+ severity: 'high'
126
+ }
127
+ )
128
+
129
+ redirect_to new_order_path, alert: 'Payment failed. Please try again.'
130
+ end
131
+ end
132
+
133
+ def show
134
+ # Performance monitoring for complex operations
135
+ @order = ActiveRabbit::Client.performance_monitor.measure('order_loading') do
136
+ Order.includes(:items, :customer, :shipping_address).find(params[:id])
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def order_params
143
+ params.require(:order).permit(:customer_id, items_attributes: [:product_id, :quantity])
144
+ end
145
+ end
146
+
147
+ # app/jobs/order_processing_job.rb
148
+ class OrderProcessingJob < ApplicationJob
149
+ def perform(order_id)
150
+ order = Order.find(order_id)
151
+
152
+ # Sidekiq integration will automatically track this job
153
+ # But you can add custom events for important milestones
154
+
155
+ ActiveRabbit::Client.track_event(
156
+ 'order_processing_started',
157
+ { order_id: order.id },
158
+ user_id: order.customer_id
159
+ )
160
+
161
+ # Process the order
162
+ process_inventory(order)
163
+ charge_payment(order)
164
+ send_confirmation_email(order)
165
+
166
+ ActiveRabbit::Client.track_event(
167
+ 'order_processing_completed',
168
+ {
169
+ order_id: order.id,
170
+ processing_time: Time.current - order.created_at
171
+ },
172
+ user_id: order.customer_id
173
+ )
174
+ end
175
+
176
+ private
177
+
178
+ def process_inventory(order)
179
+ # Custom performance tracking
180
+ ActiveRabbit::Client.track_performance(
181
+ 'inventory_processing',
182
+ measure_time { update_inventory_levels(order) },
183
+ metadata: {
184
+ order_id: order.id,
185
+ items_count: order.items.count
186
+ }
187
+ )
188
+ end
189
+
190
+ def measure_time
191
+ start_time = Time.current
192
+ yield
193
+ ((Time.current - start_time) * 1000).round(2)
194
+ end
195
+ end
196
+
197
+ # app/models/order.rb
198
+ class Order < ApplicationRecord
199
+ has_many :items
200
+ belongs_to :customer
201
+
202
+ after_create :track_creation
203
+ after_update :track_status_changes
204
+
205
+ private
206
+
207
+ def track_creation
208
+ ActiveRabbit::Client.track_event(
209
+ 'model_order_created',
210
+ {
211
+ id: id,
212
+ customer_id: customer_id,
213
+ total_amount: total_amount
214
+ }
215
+ )
216
+ end
217
+
218
+ def track_status_changes
219
+ if saved_change_to_status?
220
+ ActiveRabbit::Client.track_event(
221
+ 'order_status_changed',
222
+ {
223
+ order_id: id,
224
+ from_status: status_before_last_save,
225
+ to_status: status,
226
+ customer_id: customer_id
227
+ }
228
+ )
229
+ end
230
+ end
231
+ end
232
+
233
+ # config/environments/production.rb
234
+ Rails.application.configure do
235
+ # ... other configuration ...
236
+
237
+ # Ensure ActiveRabbit client shuts down gracefully
238
+ config.after_initialize do
239
+ at_exit do
240
+ ActiveRabbit::Client.shutdown if ActiveRabbit::Client.configured?
241
+ end
242
+ end
243
+ end