exceptify 1.0.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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +16 -0
  3. data/CODE_OF_CONDUCT.md +22 -0
  4. data/CONTRIBUTING.md +33 -0
  5. data/MIT-LICENSE +23 -0
  6. data/README.md +534 -0
  7. data/RELEASING.md +51 -0
  8. data/Rakefile +25 -0
  9. data/docs/notifiers/custom.md +42 -0
  10. data/docs/notifiers/datadog.md +51 -0
  11. data/docs/notifiers/email.md +195 -0
  12. data/docs/notifiers/slack.md +154 -0
  13. data/docs/notifiers/sns.md +37 -0
  14. data/docs/notifiers/teams.md +54 -0
  15. data/docs/notifiers/webhook.md +60 -0
  16. data/exceptify.gemspec +48 -0
  17. data/lib/exceptify/base_notifier.rb +30 -0
  18. data/lib/exceptify/configuration.rb +184 -0
  19. data/lib/exceptify/datadog_notifier.rb +160 -0
  20. data/lib/exceptify/dispatcher.rb +49 -0
  21. data/lib/exceptify/email_notifier.rb +208 -0
  22. data/lib/exceptify/modules/backtrace_cleaner.rb +13 -0
  23. data/lib/exceptify/modules/error_grouping.rb +170 -0
  24. data/lib/exceptify/modules/formatter.rb +119 -0
  25. data/lib/exceptify/notification.rb +71 -0
  26. data/lib/exceptify/notifier.rb +19 -0
  27. data/lib/exceptify/notifier_registry.rb +55 -0
  28. data/lib/exceptify/rack.rb +88 -0
  29. data/lib/exceptify/rails/runner_tie.rb +57 -0
  30. data/lib/exceptify/rails.rb +29 -0
  31. data/lib/exceptify/rake.rb +59 -0
  32. data/lib/exceptify/request_context.rb +35 -0
  33. data/lib/exceptify/resque.rb +25 -0
  34. data/lib/exceptify/sidekiq.rb +15 -0
  35. data/lib/exceptify/slack_notifier.rb +141 -0
  36. data/lib/exceptify/sns_notifier.rb +98 -0
  37. data/lib/exceptify/solid_queue.rb +68 -0
  38. data/lib/exceptify/teams_notifier.rb +209 -0
  39. data/lib/exceptify/version.rb +5 -0
  40. data/lib/exceptify/views/exceptify/_backtrace.html.erb +3 -0
  41. data/lib/exceptify/views/exceptify/_backtrace.text.erb +1 -0
  42. data/lib/exceptify/views/exceptify/_data.html.erb +6 -0
  43. data/lib/exceptify/views/exceptify/_data.text.erb +1 -0
  44. data/lib/exceptify/views/exceptify/_environment.html.erb +10 -0
  45. data/lib/exceptify/views/exceptify/_environment.text.erb +5 -0
  46. data/lib/exceptify/views/exceptify/_request.html.erb +36 -0
  47. data/lib/exceptify/views/exceptify/_request.text.erb +10 -0
  48. data/lib/exceptify/views/exceptify/_session.html.erb +10 -0
  49. data/lib/exceptify/views/exceptify/_session.text.erb +2 -0
  50. data/lib/exceptify/views/exceptify/_title.html.erb +3 -0
  51. data/lib/exceptify/views/exceptify/_title.text.erb +3 -0
  52. data/lib/exceptify/views/exceptify/background_exceptify.html.erb +53 -0
  53. data/lib/exceptify/views/exceptify/background_exceptify.text.erb +14 -0
  54. data/lib/exceptify/views/exceptify/exceptify.html.erb +52 -0
  55. data/lib/exceptify/views/exceptify/exceptify.text.erb +24 -0
  56. data/lib/exceptify/webhook_notifier.rb +63 -0
  57. data/lib/exceptify.rb +177 -0
  58. data/lib/generators/exceptify/install_generator.rb +24 -0
  59. data/lib/generators/exceptify/templates/exceptify.rb.erb +44 -0
  60. metadata +364 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dbf40071354c10947e6fa90ef8c512db08003e84322f1ade20637107db09deb5
4
+ data.tar.gz: 28a5b5cfe05a3012a4f5b88d9adda06b1fe7f6257beacd4a294c630b5a360f06
5
+ SHA512:
6
+ metadata.gz: 3f7077257beead584f587c93f989c30908deb06264e5c254f8013bb84841a78dab7ed6eb4964fb04e3e48fab0debb7060851801c3cb55c333961dfbe30c641f1
7
+ data.tar.gz: de6d0cfb74a7be063254a5c7316e7b3ad3678754582648ef93bf05754065f78320d4357a623961cf247065f0f2e7c4fb11936df913efc6a9c74446c00c16a09d
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,16 @@
1
+ == 1.0.0
2
+
3
+ First maintained release line under <tt>lukin-io/exceptify</tt>.
4
+
5
+ * Rename the gem, require paths, namespace, generator, templates, and documentation to <tt>exceptify</tt>.
6
+ * Require Ruby 3.4.4 or newer.
7
+ * Target Rails 8.0.2 or newer for development and verification, below Rails 9.
8
+ * Keep the supported notifier set focused on Email, Slack, Microsoft Teams, Amazon SNS, Datadog, Webhook, and custom notifiers.
9
+ * Remove unsupported notifier integrations and stale documentation.
10
+ * Add PORO configuration, notifier registry, dispatcher, notification, and request-context objects to keep core behavior testable.
11
+ * Add deterministic setup validation for network notifier options.
12
+ * Add dependency injection support for notifier clients/transports.
13
+ * Keep Rack middleware options isolated per middleware instance.
14
+ * Guard Rails runner hook installation from duplicate callbacks.
15
+ * Add Solid Queue support for reporting unhandled Active Job failures.
16
+ * Expand specs for success, failure, and edge/null/boundary behavior across the refactored core and supported notifiers.
@@ -0,0 +1,22 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
6
+
7
+ Examples of unacceptable behavior by participants include:
8
+
9
+ * The use of sexualized language or imagery
10
+ * Personal attacks
11
+ * Trolling or insulting/derogatory comments
12
+ * Public or private harassment
13
+ * Publishing other's private information, such as physical or electronic addresses, without explicit permission
14
+ * Other unethical or unprofessional conduct.
15
+
16
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
17
+
18
+ This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
19
+
20
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
21
+
22
+ This Code of Conduct is adapted from the [Contributor Covenant, version 1.2.0](http://contributor-covenant.org/version/1/2/0/).
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,33 @@
1
+ # How to contribute
2
+
3
+ Pull requests welcome! Please try to make them as complete and clear as possible, with reproduction steps and good tests.
4
+
5
+
6
+ ## Issues
7
+
8
+ Issues are monitored and responded to, but pull requests are much more likely to be merged.
9
+
10
+ Make sure the issue includes version information, reproduction steps, and any other relevant information.
11
+
12
+ You can use the `examples/sample_app.rb` to help reproduce the issue.
13
+
14
+
15
+ ## Pull Requests
16
+
17
+ All PRs with changes will be reviewed and merged if possible. Thank you for taking the time to contribute.
18
+
19
+ Changes must include tests. Please include a description of the problem and why the change is needed. Same with issues, reproduction steps are helpful.
20
+
21
+
22
+ ### Running the tests
23
+
24
+ ```bash
25
+ bundle install
26
+ bundle exec rake test
27
+ ```
28
+
29
+ And running the linting with [standard](https://github.com/standardrb/standard):
30
+
31
+ ```bash
32
+ bundle exec standardrb
33
+ ```
data/MIT-LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2011-2016 Sebastian Martinez
2
+ Copyright (c) 2005-2010 Jamis Buck
3
+
4
+ The MIT License (MIT)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,534 @@
1
+ # Exceptify
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/exceptify.svg)](https://badge.fury.io/rb/exceptify)
4
+ [![Build Status](https://github.com/lukin-io/exceptify/actions/workflows/ci.yml/badge.svg)](https://github.com/lukin-io/exceptify/actions/workflows/ci.yml)
5
+
6
+ Exceptify sends exception reports from Rails and Rack applications to the channels your team already watches: email, chat, webhooks, and monitoring tools.
7
+
8
+ This repository is maintained by [@lukin-io](https://github.com/lukin-io) as `lukin-io/exceptify` with the `exceptify` gem name and `Exceptify` API. Version `1.0.0` starts the maintained Exceptify release line.
9
+
10
+ ## Quick Start
11
+
12
+ Add the gem to your Rails application's `Gemfile`:
13
+
14
+ ```ruby
15
+ gem "exceptify"
16
+ ```
17
+
18
+ Install it and generate the initializer:
19
+
20
+ ```bash
21
+ bundle install
22
+ bundle exec rails generate exceptify:install
23
+ ```
24
+
25
+ Configure at least one notifier:
26
+
27
+ ```ruby
28
+ # config/initializers/exceptify.rb
29
+ require "exceptify/rails"
30
+ require "exceptify/rake"
31
+
32
+ Exceptify.configure do |config|
33
+ config.add_notifier :email, {
34
+ email_prefix: "[#{Rails.env.upcase}] ",
35
+ sender_address: %("Exceptify" <notifier@example.com>),
36
+ exception_recipients: %w[exceptions@example.com]
37
+ }
38
+
39
+ config.ignore_if do |_exception, _options|
40
+ Rails.env.local?
41
+ end
42
+ end
43
+ ```
44
+
45
+ Email delivery uses your application's ActionMailer configuration. See [ActionMailer configuration](docs/notifiers/email.md#actionmailer-configuration) if emails do not send.
46
+
47
+ ## How To Test It
48
+
49
+ After configuring a notifier, trigger a real exception in a non-production environment.
50
+
51
+ ```ruby
52
+ # config/routes.rb
53
+ get "/exceptify_test", to: proc {
54
+ raise "Exceptify test"
55
+ }
56
+ ```
57
+
58
+ Start Rails, visit `/exceptify_test`, and confirm the notification arrives. Remove the route after testing.
59
+
60
+ ## Contents
61
+
62
+ * [Quick Start](#quick-start)
63
+ * [How To Test It](#how-to-test-it)
64
+ * [What It Does](#what-it-does)
65
+ * [Compatibility](#compatibility)
66
+ * [Installation](#installation)
67
+ * [Rails Setup](#rails-setup)
68
+ * [Common Workflows](#common-workflows)
69
+ * [Notifiers](#notifiers)
70
+ * [Noise Control](#noise-control)
71
+ * [Production Checklist](#production-checklist)
72
+ * [Background Jobs](#background-jobs)
73
+ * [Rack and Sinatra](#rack-and-sinatra)
74
+ * [Maintenance](#maintenance)
75
+ * [Development](#development)
76
+ * [License](#license)
77
+
78
+ ## What It Does
79
+
80
+ Exceptify installs a Rack middleware that watches for unhandled exceptions during web requests and sends a notification with request, session, environment, backtrace, and optional application data.
81
+
82
+ Use it when you want a small, self-hosted notification layer for exceptions without adopting a hosted error tracker.
83
+
84
+ It supports:
85
+
86
+ * Rails integration through a generator and initializer.
87
+ * Rack middleware configuration for Rails, Sinatra, and other Rack apps.
88
+ * Built-in notifiers for email, Slack, Teams, Amazon SNS, Datadog, and webhooks.
89
+ * Manual reporting for rescued exceptions, background jobs, scripts, rake tasks, and runners.
90
+ * Ignore rules, crawler filtering, per-notifier filtering, and repeated-error grouping.
91
+
92
+ ## Compatibility
93
+
94
+ * Exceptify 1.0.0 or newer.
95
+ * Ruby 3.4.4 or newer.
96
+ * Rails 8.0.2 or newer, below Rails 9.
97
+ * Rack applications, including Sinatra.
98
+
99
+ The changelog starts at `1.0.0` for the maintained `exceptify` release line.
100
+
101
+ ## Installation
102
+
103
+ Add the gem to your application's `Gemfile`:
104
+
105
+ ```ruby
106
+ gem "exceptify"
107
+ ```
108
+
109
+ Install it:
110
+
111
+ ```bash
112
+ bundle install
113
+ ```
114
+
115
+ To use this maintained repository directly from Git:
116
+
117
+ ```ruby
118
+ gem "exceptify", git: "https://github.com/lukin-io/exceptify.git"
119
+ ```
120
+
121
+ For local gem development, point a test application at your checkout:
122
+
123
+ ```ruby
124
+ gem "exceptify", path: "../exceptify"
125
+ ```
126
+
127
+ If the application should keep a Git source in its `Gemfile` while Bundler uses a local checkout, configure a local override from that application:
128
+
129
+ ```bash
130
+ bundle config set local.exceptify ../exceptify
131
+ bundle install
132
+ ```
133
+
134
+ ## Rails Setup
135
+
136
+ Generate the Rails initializer:
137
+
138
+ ```bash
139
+ bundle exec rails generate exceptify:install
140
+ ```
141
+
142
+ The generated file is written to `config/initializers/exceptify.rb`.
143
+
144
+ Keep the gem available to every environment that loads the initializer. If you want notifications only in production, keep the gem outside a `production`-only bundle group and add an ignore rule for local environments.
145
+
146
+ ### Initializer Notes
147
+
148
+ The generated initializer should load the Rails and Rake integrations, then register one or more notifiers. The email example in [Quick Start](#quick-start) is enough for a first setup; add other notifiers from [Notifiers](#notifiers) as needed.
149
+
150
+ ### Rails Runner Support
151
+
152
+ If you want `rails runner` commands to report exceptions, load the Rails integration from `config/application.rb` below `Bundler.require`:
153
+
154
+ ```ruby
155
+ # config/application.rb
156
+ require "exceptify/rails"
157
+ ```
158
+
159
+ The initializer is too late for runner callbacks. You can still keep notifier configuration in `config/initializers/exceptify.rb`.
160
+ The runner hook is guarded, so loading the integration more than once does not install duplicate `at_exit` callbacks.
161
+
162
+ ## Common Workflows
163
+
164
+ ### Send to Email and Slack
165
+
166
+ Slack notifications require the `slack-notifier` gem:
167
+
168
+ ```ruby
169
+ gem "slack-notifier"
170
+ ```
171
+
172
+ Then register both notifiers:
173
+
174
+ ```ruby
175
+ Exceptify.configure do |config|
176
+ config.add_notifier :email, {
177
+ email_prefix: "[#{Rails.env.upcase}] ",
178
+ sender_address: %("Exceptify" <notifier@example.com>),
179
+ exception_recipients: %w[exceptions@example.com]
180
+ }
181
+
182
+ if (webhook_url = Rails.application.credentials.dig(:slack, :exceptions_webhook_url))
183
+ config.add_notifier :slack, {
184
+ webhook_url: webhook_url,
185
+ channel: "#exceptions",
186
+ additional_parameters: {
187
+ mrkdwn: true
188
+ }
189
+ }
190
+ end
191
+ end
192
+ ```
193
+
194
+ ### Attach Request Context
195
+
196
+ Store application data in the request environment before an exception is raised:
197
+
198
+ ```ruby
199
+ class ApplicationController < ActionController::Base
200
+ before_action :prepare_exceptify_notification
201
+
202
+ private
203
+
204
+ def prepare_exceptify_notification
205
+ request.env["exceptify.exception_data"] = {
206
+ current_user_id: current_user&.id,
207
+ account_id: current_account&.id,
208
+ request_id: request.request_id
209
+ }
210
+ end
211
+ end
212
+ ```
213
+
214
+ That data is included in supported notifier payloads and email sections.
215
+
216
+ ### Report a Handled Controller Error
217
+
218
+ Middleware only sees exceptions that continue up the Rack stack. If a controller rescues an error, notify manually:
219
+
220
+ ```ruby
221
+ class OrdersController < ApplicationController
222
+ rescue_from PaymentGateway::Timeout, with: :payment_gateway_timeout
223
+
224
+ private
225
+
226
+ def payment_gateway_timeout(exception)
227
+ Exceptify.notify_exception(
228
+ exception,
229
+ env: request.env,
230
+ data: {
231
+ order_id: params[:id],
232
+ request_id: request.request_id
233
+ }
234
+ )
235
+
236
+ render json: {error: "payment_gateway_timeout"}, status: :bad_gateway
237
+ end
238
+ end
239
+ ```
240
+
241
+ ### Report from Plain Ruby Code
242
+
243
+ ```ruby
244
+ begin
245
+ ImportCustomers.call(file_path)
246
+ rescue => exception
247
+ Exceptify.notify_exception(
248
+ exception,
249
+ data: {
250
+ job: "ImportCustomers",
251
+ file_path: file_path
252
+ }
253
+ )
254
+
255
+ raise
256
+ end
257
+ ```
258
+
259
+ ### Filter Sensitive Parameters
260
+
261
+ Exception emails can include request parameters. Use Rails parameter filtering for secrets:
262
+
263
+ ```ruby
264
+ # config/application.rb
265
+ config.filter_parameters += [
266
+ :password,
267
+ :password_confirmation,
268
+ :credit_card_number,
269
+ :secret_details
270
+ ]
271
+ ```
272
+
273
+ ## Notifiers
274
+
275
+ Built-in notifier docs and support status:
276
+
277
+ | Notifier | Status | Docs |
278
+ | --- | --- | --- |
279
+ | Email | Supported | [Email](docs/notifiers/email.md) |
280
+ | Slack | Supported | [Slack](docs/notifiers/slack.md) |
281
+ | Teams | Supported | [Teams](docs/notifiers/teams.md) |
282
+ | Amazon SNS | Supported | [Amazon SNS](docs/notifiers/sns.md) |
283
+ | Datadog | Supported | [Datadog](docs/notifiers/datadog.md) |
284
+ | WebHook | Supported | [WebHook](docs/notifiers/webhook.md) |
285
+ | Custom notifiers | Supported extension point | [Custom notifiers](docs/notifiers/custom.md) |
286
+
287
+ ### Notifier Setup Rules
288
+
289
+ Network notifiers validate required options during setup. Missing `webhook_url`, `url`, or AWS credentials raise `ArgumentError` instead of silently disabling notifications.
290
+
291
+ For tests, custom transports, or apps that already wrap provider clients, pass an injected client:
292
+
293
+ ```ruby
294
+ Exceptify.configure do |config|
295
+ config.add_notifier :webhook, {
296
+ url: "https://example.com/exception-webhook",
297
+ http_client: MyHTTPClient
298
+ }
299
+
300
+ config.add_notifier :sns, {
301
+ topic_arn: "arn:aws:sns:us-east-1:123456789012:exceptions",
302
+ client: Aws::SNS::Client.new(region: "us-east-1")
303
+ }
304
+ end
305
+ ```
306
+
307
+ Slack accepts `notifier:` for a prebuilt Slack client. Use `fail_silently: true` only as a temporary compatibility option while migrating old silent configurations.
308
+
309
+ You can also register any object that responds to `#call(exception, options)`:
310
+
311
+ ```ruby
312
+ Exceptify.configure do |config|
313
+ config.add_notifier :logger, lambda { |exception, options|
314
+ Rails.logger.error(
315
+ "[exceptify] #{exception.class}: #{exception.message} #{options[:data].inspect}"
316
+ )
317
+ }
318
+ end
319
+ ```
320
+
321
+ ## Noise Control
322
+
323
+ ### Ignore Known Exceptions
324
+
325
+ ```ruby
326
+ Exceptify.configure do |config|
327
+ config.ignored_exceptions += %w[
328
+ ActionView::TemplateError
329
+ MyApp::ExpectedError
330
+ ]
331
+ end
332
+ ```
333
+
334
+ The default ignored exceptions include common routing, record-not-found, and invalid-parameter errors.
335
+
336
+ ### Ignore Crawlers
337
+
338
+ ```ruby
339
+ Exceptify.configure do |config|
340
+ config.ignore_crawlers %w[Googlebot bingbot]
341
+ end
342
+ ```
343
+
344
+ ### Ignore by Condition
345
+
346
+ ```ruby
347
+ Exceptify.configure do |config|
348
+ config.ignore_if do |exception, options|
349
+ path = options.dig(:env, "PATH_INFO")
350
+
351
+ Rails.env.local? ||
352
+ path == "/health" ||
353
+ exception.message.match?(/Couldn't find Page with ID=/)
354
+ end
355
+ end
356
+ ```
357
+
358
+ ### Ignore Only One Notifier
359
+
360
+ Use per-notifier filtering when email should still send but chat should stay quiet, or the reverse:
361
+
362
+ ```ruby
363
+ Exceptify.configure do |config|
364
+ config.ignore_notifier_if(:slack) do |exception, _options|
365
+ exception.is_a?(ActionController::RoutingError)
366
+ end
367
+ end
368
+ ```
369
+
370
+ ### Group Repeated Errors
371
+
372
+ Error grouping prevents notification floods for the same exception. With the default trigger, notifications are sent at counts 1, 2, 4, 8, 16, and so on.
373
+
374
+ ```ruby
375
+ Exceptify.configure do |config|
376
+ config.error_grouping = true
377
+ config.error_grouping_cache = Rails.cache
378
+ config.error_grouping_period = 5.minutes
379
+
380
+ config.notification_trigger = lambda { |_exception, count|
381
+ count == 1 || (count % 10).zero?
382
+ }
383
+ end
384
+ ```
385
+
386
+ ## Production Checklist
387
+
388
+ Before relying on exception notifications in production:
389
+
390
+ * Configure at least one notifier.
391
+ * Verify delivery with a real test exception in staging.
392
+ * Confirm ActionMailer delivery settings if using email.
393
+ * Filter secrets with `config.filter_parameters`.
394
+ * Ignore local and test environments.
395
+ * Add crawler or health-check ignores if they create noise.
396
+ * Enable error grouping for high-traffic applications.
397
+ * Make sure background jobs are covered separately from web requests.
398
+
399
+ ## Background Jobs
400
+
401
+ The Rack middleware only catches exceptions during web requests. For jobs and command-line work, use one of the integrations below.
402
+
403
+ ### Rake Tasks
404
+
405
+ The generated initializer includes:
406
+
407
+ ```ruby
408
+ require "exceptify/rake"
409
+ ```
410
+
411
+ That reports unhandled exceptions from rake tasks.
412
+
413
+ ### Rails Runner
414
+
415
+ For `rails runner`, require the Rails integration from `config/application.rb` as shown in [Rails Runner Support](#rails-runner-support).
416
+
417
+ ### Sidekiq, Resque, and Solid Queue
418
+
419
+ Generate an initializer with the integration you need:
420
+
421
+ ```bash
422
+ bundle exec rails generate exceptify:install --sidekiq
423
+ ```
424
+
425
+ or:
426
+
427
+ ```bash
428
+ bundle exec rails generate exceptify:install --resque
429
+ ```
430
+
431
+ or:
432
+
433
+ ```bash
434
+ bundle exec rails generate exceptify:install --solid-queue
435
+ ```
436
+
437
+ The Solid Queue integration hooks into Active Job failure notifications and reports unhandled exceptions for jobs using the `solid_queue` adapter. It preserves Active Job failure behavior, so failed jobs still end up in Solid Queue's failed executions table and `retry_on` or `discard_on` handlers still run normally.
438
+
439
+ ### Manual Job Reporting
440
+
441
+ If a job system is not integrated, report exceptions directly:
442
+
443
+ ```ruby
444
+ class RebuildSearchIndexJob
445
+ def perform(account_id)
446
+ RebuildSearchIndex.call(account_id)
447
+ rescue => exception
448
+ Exceptify.notify_exception(
449
+ exception,
450
+ data: {
451
+ job: self.class.name,
452
+ account_id: account_id
453
+ }
454
+ )
455
+
456
+ raise
457
+ end
458
+ end
459
+ ```
460
+
461
+ ## Rack and Sinatra
462
+
463
+ Use the Rack middleware directly when you are not using the Rails generator, or when you need Rack-only options such as `ignore_cascade_pass`.
464
+
465
+ ```ruby
466
+ require "exceptify"
467
+
468
+ use Exceptify::Rack,
469
+ email: {
470
+ email_prefix: "[RACK ERROR] ",
471
+ sender_address: %("Exceptify" <notifier@example.com>),
472
+ exception_recipients: %w[exceptions@example.com]
473
+ },
474
+ ignore_exceptions: ["Sinatra::NotFound"],
475
+ ignore_cascade_pass: true
476
+ ```
477
+
478
+ Options passed directly to `Exceptify::Rack` are local to that middleware instance. To use one application-wide configuration, configure `Exceptify` once and mount the middleware without notifier options:
479
+
480
+ ```ruby
481
+ require "exceptify"
482
+
483
+ Exceptify.configure do |config|
484
+ config.add_notifier :email, {
485
+ sender_address: %("Exceptify" <notifier@example.com>),
486
+ exception_recipients: %w[exceptions@example.com]
487
+ }
488
+ end
489
+
490
+ use Exceptify::Rack
491
+ ```
492
+
493
+ Sinatra users can also review the [example application](examples/sinatra).
494
+
495
+ ## Maintenance
496
+
497
+ This repository is the current home for the `exceptify` gem. Maintenance focuses on modern Ruby and Rails support, clear documentation, and practical bug fixes around the `Exceptify` API.
498
+
499
+ Issues and pull requests are welcome when they include enough context to reproduce the behavior.
500
+ The current refactoring direction is tracked in [REFACTORING.md](REFACTORING.md).
501
+
502
+ ## Development
503
+
504
+ Install dependencies:
505
+
506
+ ```bash
507
+ bundle install
508
+ ```
509
+
510
+ Run tests:
511
+
512
+ ```bash
513
+ bundle exec rake test
514
+ ```
515
+
516
+ Open a console with the gem loaded:
517
+
518
+ ```bash
519
+ bundle exec rake console
520
+ ```
521
+
522
+ Build the gem locally:
523
+
524
+ ```bash
525
+ bundle exec rake build
526
+ ```
527
+
528
+ Pull requests and issues are welcome. Please read the [Contributing Guide](CONTRIBUTING.md) and follow the [Code of Conduct](CODE_OF_CONDUCT.md).
529
+
530
+ ## License
531
+
532
+ Released under the [MIT license](MIT-LICENSE).
533
+
534
+ Maintainer: [@lukin-io](https://github.com/lukin-io)
data/RELEASING.md ADDED
@@ -0,0 +1,51 @@
1
+ # Releasing Exceptify
2
+
3
+ ## First RubyGems.org Publish
4
+
5
+ The `exceptify` gem is built from `exceptify.gemspec`. RubyGems.org receives the
6
+ built `.gem` file, not the Git repository.
7
+
8
+ Run the first publish manually:
9
+
10
+ ```sh
11
+ bundle install
12
+ bundle exec rake test
13
+ bundle exec standardrb
14
+ gem build --strict exceptify.gemspec
15
+ gem signin
16
+ gem push exceptify-1.0.0.gem
17
+ ```
18
+
19
+ RubyGems.org versions are immutable. If a pushed gem needs any code or metadata
20
+ change, bump `Exceptify::VERSION` and publish a new version.
21
+
22
+ ## Trusted Publishing Setup
23
+
24
+ After the first publish, configure RubyGems.org Trusted Publishing for automated
25
+ tag releases:
26
+
27
+ - Gem name: `exceptify`
28
+ - GitHub repository owner: `lukin-io`
29
+ - GitHub repository name: `exceptify`
30
+ - Workflow filename: `gem-push.yml`
31
+ - Environment: `release`
32
+
33
+ The workflow publishes tagged releases to both GitHub Packages and RubyGems.org.
34
+ RubyGems.org publishing uses GitHub OIDC through `rubygems/release-gem@v1`, so no
35
+ long-lived `RUBYGEMS_AUTH_TOKEN` secret is needed.
36
+
37
+ ## Release Flow
38
+
39
+ 1. Update `Exceptify::VERSION` in `lib/exceptify/version.rb`.
40
+ 2. Update `CHANGELOG.rdoc`.
41
+ 3. Commit the changes.
42
+ 4. Create a matching tag:
43
+
44
+ ```sh
45
+ git tag v1.0.0
46
+ git push origin main
47
+ git push origin v1.0.0
48
+ ```
49
+
50
+ The CI workflow verifies that the tag name matches the gem version before
51
+ publishing.