axn 0.1.0.pre.alpha.2.8.1 → 0.1.0.pre.alpha.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 +4 -4
- data/.cursor/commands/pr.md +36 -0
- data/.cursor/rules/axn-framework-patterns.mdc +43 -0
- data/.cursor/rules/general-coding-standards.mdc +27 -0
- data/.cursor/rules/spec/testing-patterns.mdc +40 -0
- data/CHANGELOG.md +57 -0
- data/Rakefile +114 -4
- data/docs/.vitepress/config.mjs +19 -10
- data/docs/advanced/conventions.md +3 -3
- data/docs/advanced/mountable.md +476 -0
- data/docs/advanced/profiling.md +351 -0
- data/docs/advanced/rough.md +27 -8
- data/docs/index.md +5 -3
- data/docs/intro/about.md +1 -1
- data/docs/intro/overview.md +6 -6
- data/docs/recipes/formatting-context-for-error-tracking.md +186 -0
- data/docs/recipes/memoization.md +103 -18
- data/docs/recipes/rubocop-integration.md +38 -284
- data/docs/recipes/testing.md +14 -14
- data/docs/recipes/validating-user-input.md +1 -1
- data/docs/reference/async.md +429 -0
- data/docs/reference/axn-result.md +107 -0
- data/docs/reference/class.md +225 -64
- data/docs/reference/configuration.md +366 -34
- data/docs/reference/form-object.md +252 -0
- data/docs/reference/instance.md +14 -29
- data/docs/strategies/client.md +212 -0
- data/docs/strategies/form.md +235 -0
- data/docs/strategies/index.md +21 -21
- data/docs/strategies/transaction.md +1 -1
- data/docs/usage/setup.md +16 -2
- data/docs/usage/steps.md +7 -7
- data/docs/usage/using.md +23 -12
- data/docs/usage/writing.md +191 -12
- data/lib/axn/async/adapters/active_job.rb +74 -0
- data/lib/axn/async/adapters/disabled.rb +41 -0
- data/lib/axn/async/adapters/sidekiq.rb +67 -0
- data/lib/axn/async/adapters.rb +26 -0
- data/lib/axn/async/batch_enqueue/config.rb +38 -0
- data/lib/axn/async/batch_enqueue.rb +99 -0
- data/lib/axn/async/enqueue_all_orchestrator.rb +363 -0
- data/lib/axn/async.rb +178 -0
- data/lib/axn/configuration.rb +113 -0
- data/lib/{action → axn}/context.rb +22 -4
- data/lib/axn/core/automatic_logging.rb +89 -0
- data/lib/axn/core/context/facade.rb +69 -0
- data/lib/{action → axn}/core/context/facade_inspector.rb +32 -5
- data/lib/{action → axn}/core/context/internal.rb +5 -5
- data/lib/{action → axn}/core/contract.rb +111 -73
- data/lib/{action → axn}/core/contract_for_subfields.rb +30 -35
- data/lib/{action → axn}/core/contract_validation.rb +27 -12
- data/lib/axn/core/contract_validation_for_subfields.rb +165 -0
- data/lib/axn/core/default_call.rb +63 -0
- data/lib/axn/core/field_resolvers/extract.rb +32 -0
- data/lib/axn/core/field_resolvers/model.rb +63 -0
- data/lib/axn/core/field_resolvers.rb +24 -0
- data/lib/{action → axn}/core/flow/callbacks.rb +7 -7
- data/lib/{action → axn}/core/flow/exception_execution.rb +9 -13
- data/lib/{action → axn}/core/flow/handlers/base_descriptor.rb +3 -2
- data/lib/{action → axn}/core/flow/handlers/descriptors/callback_descriptor.rb +2 -2
- data/lib/{action → axn}/core/flow/handlers/descriptors/message_descriptor.rb +23 -11
- data/lib/axn/core/flow/handlers/invoker.rb +47 -0
- data/lib/{action → axn}/core/flow/handlers/matcher.rb +9 -19
- data/lib/{action → axn}/core/flow/handlers/registry.rb +3 -1
- data/lib/{action → axn}/core/flow/handlers/resolvers/base_resolver.rb +1 -1
- data/lib/{action → axn}/core/flow/handlers/resolvers/callback_resolver.rb +2 -2
- data/lib/{action → axn}/core/flow/handlers/resolvers/message_resolver.rb +12 -3
- data/lib/axn/core/flow/handlers.rb +20 -0
- data/lib/{action → axn}/core/flow/messages.rb +8 -8
- data/lib/{action → axn}/core/flow.rb +4 -4
- data/lib/{action → axn}/core/hooks.rb +17 -5
- data/lib/axn/core/logging.rb +48 -0
- data/lib/axn/core/memoization.rb +53 -0
- data/lib/{action → axn}/core/nesting_tracking.rb +1 -1
- data/lib/{action → axn}/core/timing.rb +1 -1
- data/lib/axn/core/tracing.rb +90 -0
- data/lib/axn/core/use_strategy.rb +29 -0
- data/lib/{action → axn}/core/validation/fields.rb +26 -2
- data/lib/{action → axn}/core/validation/subfields.rb +14 -12
- data/lib/axn/core/validation/validators/model_validator.rb +36 -0
- data/lib/axn/core/validation/validators/type_validator.rb +80 -0
- data/lib/{action → axn}/core/validation/validators/validate_validator.rb +12 -2
- data/lib/{action → axn}/core.rb +55 -55
- data/lib/{action → axn}/exceptions.rb +12 -2
- data/lib/axn/extras/strategies/client.rb +150 -0
- data/lib/axn/extras/strategies/vernier.rb +121 -0
- data/lib/axn/extras.rb +4 -0
- data/lib/axn/factory.rb +122 -34
- data/lib/axn/form_object.rb +90 -0
- data/lib/axn/internal/logging.rb +30 -0
- data/lib/axn/internal/registry.rb +87 -0
- data/lib/axn/mountable/descriptor.rb +76 -0
- data/lib/axn/mountable/helpers/class_builder.rb +193 -0
- data/lib/axn/mountable/helpers/mounter.rb +33 -0
- data/lib/axn/mountable/helpers/namespace_manager.rb +38 -0
- data/lib/axn/mountable/helpers/validator.rb +112 -0
- data/lib/axn/mountable/inherit_profiles.rb +72 -0
- data/lib/axn/mountable/mounting_strategies/_base.rb +87 -0
- data/lib/axn/mountable/mounting_strategies/axn.rb +48 -0
- data/lib/axn/mountable/mounting_strategies/method.rb +95 -0
- data/lib/axn/mountable/mounting_strategies/step.rb +69 -0
- data/lib/axn/mountable/mounting_strategies.rb +32 -0
- data/lib/axn/mountable.rb +119 -0
- data/lib/axn/rails/engine.rb +51 -0
- data/lib/axn/rails/generators/axn_generator.rb +86 -0
- data/lib/axn/rails/generators/templates/action.rb.erb +17 -0
- data/lib/axn/rails/generators/templates/action_spec.rb.erb +25 -0
- data/lib/{action → axn}/result.rb +32 -13
- data/lib/axn/strategies/form.rb +98 -0
- data/lib/axn/strategies/transaction.rb +26 -0
- data/lib/axn/strategies.rb +20 -0
- data/lib/axn/testing/spec_helpers.rb +6 -8
- data/lib/axn/util/callable.rb +120 -0
- data/lib/axn/util/contract_error_handling.rb +32 -0
- data/lib/axn/util/execution_context.rb +34 -0
- data/lib/axn/util/global_id_serialization.rb +52 -0
- data/lib/axn/util/logging.rb +87 -0
- data/lib/axn/util/memoization.rb +20 -0
- data/lib/axn/version.rb +1 -1
- data/lib/axn.rb +26 -16
- data/lib/rubocop/cop/axn/README.md +23 -23
- data/lib/rubocop/cop/axn/unchecked_result.rb +138 -17
- metadata +106 -64
- data/.rspec +0 -3
- data/.rubocop.yml +0 -76
- data/.tool-versions +0 -1
- data/docs/reference/action-result.md +0 -37
- data/lib/action/attachable/base.rb +0 -43
- data/lib/action/attachable/steps.rb +0 -63
- data/lib/action/attachable/subactions.rb +0 -70
- data/lib/action/attachable.rb +0 -17
- data/lib/action/configuration.rb +0 -55
- data/lib/action/core/automatic_logging.rb +0 -93
- data/lib/action/core/context/facade.rb +0 -48
- data/lib/action/core/flow/handlers/invoker.rb +0 -73
- data/lib/action/core/flow/handlers.rb +0 -20
- data/lib/action/core/logging.rb +0 -37
- data/lib/action/core/tracing.rb +0 -17
- data/lib/action/core/use_strategy.rb +0 -30
- data/lib/action/core/validation/validators/model_validator.rb +0 -34
- data/lib/action/core/validation/validators/type_validator.rb +0 -30
- data/lib/action/enqueueable/via_sidekiq.rb +0 -76
- data/lib/action/enqueueable.rb +0 -13
- data/lib/action/strategies/transaction.rb +0 -19
- data/lib/action/strategies.rb +0 -48
- data/lib/axn/util.rb +0 -24
- data/package.json +0 -10
- data/yarn.lock +0 -1166
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Configuration
|
|
2
2
|
|
|
3
|
-
Somewhere at boot (e.g. `config/initializers/actions.rb` in Rails), you can call `
|
|
3
|
+
Somewhere at boot (e.g. `config/initializers/actions.rb` in Rails), you can call `Axn.configure` to adjust a few global settings.
|
|
4
4
|
|
|
5
5
|
```ruby
|
|
6
|
-
|
|
6
|
+
Axn.configure do |c|
|
|
7
7
|
c.log_level = :info
|
|
8
8
|
c.logger = ...
|
|
9
9
|
c.on_exception = proc do |e, action:, context:|
|
|
@@ -22,7 +22,7 @@ By default any swallowed errors are noted in the logs, but it's _highly recommen
|
|
|
22
22
|
For example, if you're using Honeybadger this could look something like:
|
|
23
23
|
|
|
24
24
|
```ruby
|
|
25
|
-
|
|
25
|
+
Axn.configure do |c|
|
|
26
26
|
c.on_exception = proc do |e, action:, context:|
|
|
27
27
|
message = "[#{action.class.name}] Failing due to #{e.class.name}: #{e.message}"
|
|
28
28
|
|
|
@@ -53,54 +53,228 @@ A couple notes:
|
|
|
53
53
|
* `context` will contain the arguments passed to the `action`, _but_ any marked as sensitive (e.g. `expects :foo, sensitive: true`) will be filtered out in the logs.
|
|
54
54
|
* If your handler raises, the failure will _also_ be swallowed and logged
|
|
55
55
|
* This handler is global across _all_ Axns. You can also specify per-Action handlers via [the class-level declaration](/reference/class#on-exception).
|
|
56
|
+
* The `context` hash may contain complex objects (like ActiveRecord models, `ActionController::Parameters`, or `Axn::FormObject` instances) that aren't easily serialized by error tracking systems. See [Formatting Context for Error Tracking Systems](/recipes/formatting-context-for-error-tracking) for a recipe to convert these to readable formats.
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
### Adding Additional Context to Exception Logging
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
When processing records in a loop or performing batch operations, you may want to include additional context (like which record is being processed) in exception logs. You can do this in two ways:
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
**Option 1: Explicit setter** - Call `set_logging_context` during execution:
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
```ruby
|
|
65
|
+
class ProcessPendingRecords
|
|
66
|
+
include Axn
|
|
67
|
+
|
|
68
|
+
def call
|
|
69
|
+
pending_records.each do |record|
|
|
70
|
+
set_logging_context(current_record_id: record.id, batch_index: @index)
|
|
71
|
+
# ... process record ...
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
```
|
|
65
76
|
|
|
66
|
-
|
|
77
|
+
**Option 2: Hook method** - Define a private `additional_logging_context` method that returns a hash:
|
|
67
78
|
|
|
68
79
|
```ruby
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
class ProcessPendingRecords
|
|
81
|
+
include Axn
|
|
82
|
+
|
|
83
|
+
def call
|
|
84
|
+
pending_records.each do |record|
|
|
85
|
+
@current_record = record
|
|
86
|
+
# ... process record ...
|
|
74
87
|
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def additional_logging_context
|
|
93
|
+
return {} unless @current_record
|
|
94
|
+
|
|
95
|
+
{
|
|
96
|
+
current_record_id: @current_record.id,
|
|
97
|
+
record_type: @current_record.class.name
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Both approaches can be used together - they will be merged. The additional context is **only** included in exception logging (not in normal pre/post execution logs), and is evaluated lazily (the hook method is only called when an exception occurs).
|
|
104
|
+
|
|
105
|
+
Action-specific `on_exception` handlers can also access this context by calling `context_for_logging` directly:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
class ProcessPendingRecords
|
|
109
|
+
include Axn
|
|
110
|
+
|
|
111
|
+
on_exception do |exception:|
|
|
112
|
+
log "Failed with this extra context: #{context_for_logging}"
|
|
113
|
+
# ... handle exception with context ...
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## `raise_piping_errors_in_dev`
|
|
119
|
+
|
|
120
|
+
By default, errors that occur in framework code (e.g., in logging hooks, exception handlers, validators, or other user-provided callbacks) are swallowed and logged to prevent them from interfering with the main action execution. In development, you can opt-in to have these errors raised instead of logged:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
Axn.configure do |c|
|
|
124
|
+
c.raise_piping_errors_in_dev = true
|
|
125
|
+
end
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Important notes:**
|
|
129
|
+
- This setting only applies in the development environment—errors are always swallowed in test and production
|
|
130
|
+
- Test and production environments behave identically (errors swallowed), ensuring tests verify actual production behavior
|
|
131
|
+
- When enabled in development, errors in framework code (like logging hooks, exception handlers, validators) will be raised instead of logged, putting issues front and center during manual testing
|
|
132
|
+
|
|
133
|
+
## OpenTelemetry Tracing
|
|
134
|
+
|
|
135
|
+
Axn automatically creates OpenTelemetry spans for all action executions when OpenTelemetry is available. The framework creates a span named `"axn.call"` with the following attributes:
|
|
136
|
+
|
|
137
|
+
- `axn.resource`: The action class name (e.g., `"UserManagement::CreateUser"`)
|
|
138
|
+
- `axn.outcome`: The execution outcome (`"success"`, `"failure"`, or `"exception"`)
|
|
139
|
+
|
|
140
|
+
When an action fails or raises an exception, the span is marked as an error with the exception details recorded.
|
|
141
|
+
|
|
142
|
+
### Basic Setup
|
|
143
|
+
|
|
144
|
+
If you just want OpenTelemetry spans (without sending to an APM provider), install the API gem:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
# Gemfile
|
|
148
|
+
gem "opentelemetry-api"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Then configure a tracer provider:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
# config/initializers/opentelemetry.rb
|
|
155
|
+
require "opentelemetry-sdk"
|
|
156
|
+
|
|
157
|
+
OpenTelemetry::SDK.configure do |c|
|
|
158
|
+
c.service_name = "my-app"
|
|
159
|
+
end
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Datadog Integration
|
|
163
|
+
|
|
164
|
+
To send OpenTelemetry spans to Datadog APM, you need both the OpenTelemetry SDK and the Datadog bridge. The bridge intercepts `OpenTelemetry::SDK.configure` and routes spans to Datadog's tracer.
|
|
165
|
+
|
|
166
|
+
**1. Add the required gems:**
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
# Gemfile
|
|
170
|
+
gem "datadog" # Datadog APM
|
|
171
|
+
gem "opentelemetry-api" # OpenTelemetry API
|
|
172
|
+
gem "opentelemetry-sdk" # OpenTelemetry SDK (required for Datadog bridge)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**2. Configure Datadog first, then OpenTelemetry:**
|
|
176
|
+
|
|
177
|
+
The order matters — Datadog must be configured before loading the OpenTelemetry bridge, and `OpenTelemetry::SDK.configure` must be called after the bridge is loaded.
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
# config/initializers/datadog.rb (use a filename that loads early, e.g., 00_datadog.rb)
|
|
181
|
+
|
|
182
|
+
# 1. Configure Datadog first
|
|
183
|
+
Datadog.configure do |c|
|
|
184
|
+
c.env = Rails.env
|
|
185
|
+
c.service = "my-app"
|
|
186
|
+
c.tracing.enabled = Rails.env.production? || Rails.env.staging?
|
|
187
|
+
c.tracing.instrument :rails
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# 2. Load the OpenTelemetry SDK and Datadog bridge
|
|
191
|
+
require "opentelemetry-api"
|
|
192
|
+
require "opentelemetry-sdk"
|
|
193
|
+
require "datadog/opentelemetry"
|
|
194
|
+
|
|
195
|
+
# 3. Configure OpenTelemetry SDK (Datadog intercepts this)
|
|
196
|
+
OpenTelemetry::SDK.configure do |c|
|
|
197
|
+
c.service_name = "my-app"
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
::: warning Important
|
|
202
|
+
The `opentelemetry-sdk` gem is required — not just `opentelemetry-api`. The Datadog bridge only activates when `OpenTelemetry::SDK` is defined and `OpenTelemetry::SDK.configure` is called.
|
|
203
|
+
:::
|
|
75
204
|
|
|
76
|
-
|
|
205
|
+
With this setup, all Axn actions will automatically create spans that appear in Datadog APM as children of your Rails request traces.
|
|
206
|
+
|
|
207
|
+
## `emit_metrics`
|
|
208
|
+
|
|
209
|
+
If you're using a metrics provider, you can emit custom metrics after each action completes using the `emit_metrics` hook. This is a post-execution hook that receives the action result—do NOT call any blocks.
|
|
210
|
+
|
|
211
|
+
The hook only receives the keyword arguments it explicitly expects (e.g., if you only define `resource:`, you won't receive `result:`).
|
|
212
|
+
|
|
213
|
+
For example, to wire up Datadog metrics:
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
Axn.configure do |c|
|
|
217
|
+
c.emit_metrics = proc do |resource:, result:|
|
|
77
218
|
TS::Metrics.increment("action.#{resource.underscore}", tags: { outcome: result.outcome.to_s, resource: })
|
|
78
219
|
TS::Metrics.histogram("action.duration", result.elapsed_time, tags: { resource: })
|
|
79
220
|
end
|
|
80
221
|
end
|
|
81
222
|
```
|
|
82
223
|
|
|
224
|
+
You can also define `emit_metrics` to only receive the arguments you need:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# Only receive resource (if you don't need the result)
|
|
228
|
+
c.emit_metrics = proc do |resource:|
|
|
229
|
+
TS::Metrics.increment("action.#{resource.underscore}")
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Only receive result (if you don't need the resource)
|
|
233
|
+
c.emit_metrics = proc do |result:|
|
|
234
|
+
TS::Metrics.increment("action.call", tags: { outcome: result.outcome.to_s })
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Accept any keyword arguments (receives both)
|
|
238
|
+
c.emit_metrics = proc do |**kwargs|
|
|
239
|
+
# kwargs will contain both :resource and :result
|
|
240
|
+
end
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Important:** When using `result:` in your `emit_metrics` hook, be careful about cardinality. Avoid creating metrics with unbounded tag values from the result (e.g., user IDs, email addresses, or other high-cardinality data). Instead, use bounded values like `result.outcome.to_s` or aggregate data. High-cardinality metrics can cause performance issues and increased costs with metrics providers.
|
|
244
|
+
|
|
83
245
|
A couple notes:
|
|
84
246
|
|
|
85
|
-
* `Datadog::Tracing` is provided by [the datadog gem](https://rubygems.org/gems/datadog)
|
|
86
247
|
* `TS::Metrics` is a custom implementation to set a Datadog count metric, but the relevant part to note is that the result object provides access to the outcome (`result.outcome.success?`, `result.outcome.failure?`, `result.outcome.exception?`) and elapsed time of the action.
|
|
87
|
-
* The `wrap_with_trace` hook is an around hook - you must call the provided block to execute the action
|
|
88
248
|
* The `emit_metrics` hook is called after execution with the result - do not call any blocks
|
|
89
249
|
|
|
90
250
|
## `logger`
|
|
91
251
|
|
|
92
252
|
Defaults to `Rails.logger`, if present, otherwise falls back to `Logger.new($stdout)`. But can be set to a custom logger as necessary.
|
|
93
253
|
|
|
254
|
+
### Background Job Logging
|
|
255
|
+
|
|
256
|
+
When using background jobs, you may want different loggers for web requests vs. background job execution. Here's a recommended pattern:
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
Axn.configure do |c|
|
|
260
|
+
# Use Sidekiq's logger when running in Sidekiq workers, otherwise use Rails logger
|
|
261
|
+
c.logger = (defined?(Sidekiq) && Sidekiq.server?) ? Sidekiq.logger : Rails.logger
|
|
262
|
+
end
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
This ensures that:
|
|
266
|
+
- Web requests log to `Rails.logger` (typically `log/production.log`)
|
|
267
|
+
- Background jobs log to `Sidekiq.logger` (typically STDOUT or a separate log file)
|
|
94
268
|
|
|
95
269
|
|
|
96
270
|
## `additional_includes`
|
|
97
271
|
|
|
98
|
-
This is much less critical than the preceding options, but on the off chance you want to add additional customization to _all_ your actions you can set additional modules to be included alongside `include
|
|
272
|
+
This is much less critical than the preceding options, but on the off chance you want to add additional customization to _all_ your actions you can set additional modules to be included alongside `include Axn`.
|
|
99
273
|
|
|
100
274
|
For example:
|
|
101
275
|
|
|
102
276
|
```ruby
|
|
103
|
-
|
|
277
|
+
Axn.configure do |c|
|
|
104
278
|
c.additional_includes = [SomeFancyCustomModule]
|
|
105
279
|
end
|
|
106
280
|
```
|
|
@@ -113,7 +287,134 @@ Sets the log level used when you call `log "Some message"` in your Action. Note
|
|
|
113
287
|
|
|
114
288
|
## `env`
|
|
115
289
|
|
|
116
|
-
Automatically detects the environment from `RACK_ENV` or `RAILS_ENV`, defaulting to `"development"`.
|
|
290
|
+
Automatically detects the environment from `RACK_ENV` or `RAILS_ENV`, defaulting to `"development"`. Returns an `ActiveSupport::StringInquirer`, allowing you to use predicate methods like `env.production?` or `env.development?`.
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
Axn.config.env.production? # => true/false
|
|
294
|
+
Axn.config.env.development? # => true/false
|
|
295
|
+
Axn.config.env.test? # => true/false
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Environment-Dependent Behavior
|
|
299
|
+
|
|
300
|
+
Several Axn behaviors change based on the detected environment:
|
|
301
|
+
|
|
302
|
+
| Behavior | Production | Test | Development |
|
|
303
|
+
| -------- | ---------- | ---- | ----------- |
|
|
304
|
+
| Log separators in async calls | Hidden | Visible (`------`) | Visible (`------`) |
|
|
305
|
+
| `raise_piping_errors_in_dev` | Always swallowed | Always swallowed | Configurable |
|
|
306
|
+
| Error message verbosity | Minimal | More detailed | More detailed |
|
|
307
|
+
|
|
308
|
+
### Overriding the Environment
|
|
309
|
+
|
|
310
|
+
You can explicitly set the environment if auto-detection doesn't work for your setup:
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
Axn.configure do |c|
|
|
314
|
+
c.env = "staging"
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
Axn.config.env.staging? # => true
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## `set_default_async`
|
|
321
|
+
|
|
322
|
+
Configures the default async adapter and settings for all actions that don't explicitly specify their own async configuration.
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
Axn.configure do |c|
|
|
326
|
+
# Set default async adapter with configuration
|
|
327
|
+
c.set_default_async(:sidekiq, queue: "default", retry: 3) do
|
|
328
|
+
sidekiq_options priority: 5
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Set default async adapter with just configuration
|
|
332
|
+
c.set_default_async(:active_job) do
|
|
333
|
+
queue_as "default"
|
|
334
|
+
self.priority = 5
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Disable async by default
|
|
338
|
+
c.set_default_async(false)
|
|
339
|
+
end
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Async Configuration
|
|
343
|
+
|
|
344
|
+
Axn supports asynchronous execution through background job processing libraries. You can configure async behavior globally or per-action.
|
|
345
|
+
|
|
346
|
+
**Available adapters:**
|
|
347
|
+
- `:sidekiq` - Sidekiq background job processing
|
|
348
|
+
- `:active_job` - Rails ActiveJob framework
|
|
349
|
+
- `false` - Disable async execution
|
|
350
|
+
|
|
351
|
+
**Basic usage:**
|
|
352
|
+
```ruby
|
|
353
|
+
# Configure per-action
|
|
354
|
+
async :sidekiq, queue: "high_priority"
|
|
355
|
+
|
|
356
|
+
# Configure globally
|
|
357
|
+
Axn.configure do |c|
|
|
358
|
+
c.set_default_async(:sidekiq, queue: "default")
|
|
359
|
+
end
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
For detailed information about async execution, including delayed execution, adapter configuration options, and best practices, see the [Async Execution documentation](/reference/async).
|
|
363
|
+
|
|
364
|
+
#### Disabled
|
|
365
|
+
|
|
366
|
+
Disables async execution entirely. The action will raise a `NotImplementedError` when `call_async` is called.
|
|
367
|
+
|
|
368
|
+
```ruby
|
|
369
|
+
# In your action class
|
|
370
|
+
async false
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Default Configuration
|
|
374
|
+
|
|
375
|
+
By default, async execution is disabled (`false`). You can set a default configuration that will be applied to all actions that don't explicitly configure their own async behavior:
|
|
376
|
+
|
|
377
|
+
```ruby
|
|
378
|
+
Axn.configure do |c|
|
|
379
|
+
# Set a default async configuration
|
|
380
|
+
c.set_default_async(:sidekiq, queue: "default") do
|
|
381
|
+
sidekiq_options retry: 3
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Now all actions will use Sidekiq by default
|
|
386
|
+
class MyAction
|
|
387
|
+
include Axn
|
|
388
|
+
# No async configuration needed - uses default
|
|
389
|
+
end
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Rails-specific Configuration
|
|
393
|
+
|
|
394
|
+
When using Axn in a Rails application, additional configuration options are available under `Axn.config.rails`:
|
|
395
|
+
|
|
396
|
+
### `app_actions_autoload_namespace`
|
|
397
|
+
|
|
398
|
+
Controls the namespace for actions in `app/actions`. Defaults to `nil` (no namespace).
|
|
399
|
+
|
|
400
|
+
```ruby
|
|
401
|
+
Axn.configure do |c|
|
|
402
|
+
# No namespace (default behavior)
|
|
403
|
+
c.rails.app_actions_autoload_namespace = nil
|
|
404
|
+
|
|
405
|
+
# Use Actions namespace
|
|
406
|
+
c.rails.app_actions_autoload_namespace = :Actions
|
|
407
|
+
|
|
408
|
+
# Use any other namespace
|
|
409
|
+
c.rails.app_actions_autoload_namespace = :MyApp
|
|
410
|
+
end
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
When `nil` (default), actions in `app/actions/user_management/create_user.rb` will be available as `UserManagement::CreateUser`.
|
|
414
|
+
|
|
415
|
+
When set to `:Actions`, the same action will be available as `Actions::UserManagement::CreateUser`.
|
|
416
|
+
|
|
417
|
+
When set to any other symbol (e.g., `:MyApp`), the action will be available as `MyApp::UserManagement::CreateUser`.
|
|
117
418
|
|
|
118
419
|
## Automatic Logging
|
|
119
420
|
|
|
@@ -124,37 +425,63 @@ By default, every `action.call` will emit log lines when it is called and after
|
|
|
124
425
|
[YourCustomAction] Execution completed (with outcome: success) in 0.957 milliseconds
|
|
125
426
|
```
|
|
126
427
|
|
|
127
|
-
Automatic logging will log at `
|
|
428
|
+
Automatic logging will log at `Axn.config.log_level` by default, but can be overridden or disabled using the declarative `log_calls` method:
|
|
128
429
|
|
|
129
430
|
```ruby
|
|
130
431
|
# Set default for all actions (affects both explicit logging and automatic logging)
|
|
131
|
-
|
|
432
|
+
Axn.configure do |c|
|
|
132
433
|
c.log_level = :debug
|
|
133
434
|
end
|
|
134
435
|
|
|
135
436
|
# Override for specific actions
|
|
136
437
|
class MyAction
|
|
137
|
-
|
|
438
|
+
log_calls :warn # Use warn level for this action
|
|
138
439
|
end
|
|
139
440
|
|
|
140
441
|
class SilentAction
|
|
141
|
-
|
|
442
|
+
log_calls false # Disable automatic logging for this action
|
|
142
443
|
end
|
|
143
444
|
|
|
144
|
-
# Use default level (no
|
|
445
|
+
# Use default level (no log_calls call needed)
|
|
145
446
|
class DefaultAction
|
|
146
|
-
# Uses
|
|
447
|
+
# Uses Axn.config.log_level
|
|
448
|
+
end
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
The `log_calls` method supports inheritance, so subclasses will inherit the setting from their parent class unless explicitly overridden.
|
|
452
|
+
|
|
453
|
+
### Error-Only Logging
|
|
454
|
+
|
|
455
|
+
For actions where you only want to log when something goes wrong, use `log_errors` instead of `log_calls`. This will:
|
|
456
|
+
- **Not** log before execution
|
|
457
|
+
- **Only** log after execution if `result.ok?` is false (i.e., on failures or exceptions)
|
|
458
|
+
|
|
459
|
+
```ruby
|
|
460
|
+
class MyAction
|
|
461
|
+
log_calls false # Disable full logging
|
|
462
|
+
log_errors :warn # Only log failures/exceptions at warn level
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
class SilentOnErrorsAction
|
|
466
|
+
log_calls false
|
|
467
|
+
log_errors false # Disable error logging for this action
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Use default level
|
|
471
|
+
class DefaultErrorLoggingAction
|
|
472
|
+
log_calls false
|
|
473
|
+
log_errors Axn.config.log_level # Uses default log level
|
|
147
474
|
end
|
|
148
475
|
```
|
|
149
476
|
|
|
150
|
-
The `
|
|
477
|
+
The `log_errors` method supports inheritance, just like `log_calls`. If both `log_calls` and `log_errors` are set, `log_calls` takes precedence (it will log before and after for all outcomes). To use `log_errors` exclusively, you must first disable `log_calls` with `log_calls false`.
|
|
151
478
|
|
|
152
479
|
## Complete Configuration Example
|
|
153
480
|
|
|
154
481
|
Here's a complete example showing all available configuration options:
|
|
155
482
|
|
|
156
483
|
```ruby
|
|
157
|
-
|
|
484
|
+
Axn.configure do |c|
|
|
158
485
|
# Logging
|
|
159
486
|
c.log_level = :info
|
|
160
487
|
c.logger = Rails.logger
|
|
@@ -167,18 +494,23 @@ Action.configure do |c|
|
|
|
167
494
|
end
|
|
168
495
|
|
|
169
496
|
# Observability
|
|
170
|
-
|
|
171
|
-
Datadog::Tracing.trace("Action", resource:) do
|
|
172
|
-
action.call
|
|
173
|
-
end
|
|
174
|
-
end
|
|
497
|
+
# OpenTelemetry tracing is automatic when OpenTelemetry is available
|
|
175
498
|
|
|
176
|
-
c.emit_metrics = proc do |resource
|
|
499
|
+
c.emit_metrics = proc do |resource:, result:|
|
|
177
500
|
Datadog::Metrics.increment("action.#{resource.underscore}", tags: { outcome: result.outcome.to_s })
|
|
178
501
|
Datadog::Metrics.histogram("action.duration", result.elapsed_time, tags: { resource: })
|
|
179
502
|
end
|
|
180
503
|
|
|
504
|
+
|
|
505
|
+
# Async configuration
|
|
506
|
+
c.set_default_async(:sidekiq, queue: "default") do
|
|
507
|
+
sidekiq_options retry: 3, priority: 5
|
|
508
|
+
end
|
|
509
|
+
|
|
181
510
|
# Global includes
|
|
182
511
|
c.additional_includes = [MyCustomModule]
|
|
512
|
+
|
|
513
|
+
# Rails-specific configuration
|
|
514
|
+
c.rails.app_actions_autoload_namespace = :Actions
|
|
183
515
|
end
|
|
184
516
|
```
|