axn 0.1.0.pre.alpha.2.7 → 0.1.0.pre.alpha.2.7.1
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 +4 -1
- data/docs/reference/action-result.md +1 -1
- data/docs/reference/class.md +12 -4
- data/docs/reference/configuration.md +39 -7
- data/docs/reference/instance.md +1 -1
- data/docs/usage/writing.md +3 -3
- data/lib/action/core/flow/callbacks.rb +8 -7
- data/lib/axn/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57d67670bb1cf60960aa2bb8234a5894a611801b69a89957057bfffd07ace8cd
|
4
|
+
data.tar.gz: 9e0025cbe4bc367e1351ea867d630b9fdb3d706547d676b9cd2162e8fda0ef38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70cc7c2851d95956278d43c746190dd5173ddda62a9a931ae9538374cf75b72523cc7f9b3524a6df6b8b8d57c969fb5510c07fad56b36312130ae86c59e48a15
|
7
|
+
data.tar.gz: f3f961781373a05284ddc54072baaf13998d235528110bf482c221e5f27d6142ab261516d8237bd0689d1161887eef6b567e8ecb4688d400a20ec32d1ae33837
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.1.0-alpha.2.7.1
|
4
|
+
* [FEAT] Implemented symbol method handler support for callbacks
|
5
|
+
|
3
6
|
## 0.1.0-alpha.2.7
|
4
7
|
* [BREAKING] Replaced `messages` declaration with separate `success` and `error` calls
|
5
8
|
* [BREAKING] Removed `rescues` method (use `error_from` for custom error messages; all exceptions now report to `on_exception` handlers)
|
@@ -7,7 +10,7 @@
|
|
7
10
|
* [FEAT] Implemented conditional success message filtering as well
|
8
11
|
* [FEAT] Added block support for `error` and `success`
|
9
12
|
* [FEAT] `if:` now supports symbol predicates referencing instance methods (arity 0, 1, or keyword `exception:`). If the method accepts `exception:` it is passed as a keyword; else if it accepts one positional arg, it is passed positionally; otherwise it is called with no args. If the method is missing, the symbol falls back to constant lookup (e.g., `:ArgumentError`).
|
10
|
-
* [FEAT] `success`/`error`
|
13
|
+
* [FEAT] `success`/`error` now accept symbol method names (e.g., `success :local_method`). Handlers can receive the exception via keyword (`exception:`) or single positional argument; otherwise they are called with no args.
|
11
14
|
* [BREAKING] Updated callback methods (`on_success`, `on_error`, `on_failure`, `on_exception`) to use consistent `if:` interface (matching messages)
|
12
15
|
* [FEAT] Added `unless:` support to both `success`/`error` messages and callbacks (`on_success`, `on_error`, `on_failure`, `on_exception`)
|
13
16
|
|
@@ -13,7 +13,7 @@ Every `call` invocation on an Action will return an `Action::Result` instance, w
|
|
13
13
|
| `elapsed_time` | Execution time in milliseconds (Float)
|
14
14
|
| any `expose`d values | guaranteed to be set if `ok?` (since they have outgoing presence validations by default; any missing would have failed the action)
|
15
15
|
|
16
|
-
NOTE: `success` and `error` (and so implicitly `message`) can be configured per-action via [the `
|
16
|
+
NOTE: `success` and `error` (and so implicitly `message`) can be configured per-action via [the `success` and `error` declarations](/reference/class#success-and-error).
|
17
17
|
|
18
18
|
### Clarification of exposed values
|
19
19
|
|
data/docs/reference/class.md
CHANGED
@@ -235,6 +235,16 @@ In addition to the [global exception handler](/reference/configuration#on-except
|
|
235
235
|
* *Callbacks* (defined below) are executed _after_ the `call` -- exceptions or `fail!`s here will _not_ change `result.ok?`
|
236
236
|
:::
|
237
237
|
|
238
|
+
|
239
|
+
**Note:** Symbol method handlers for all callback types follow the same argument pattern as [message handlers](#conditional-messages):
|
240
|
+
- If the method accepts `exception:` as a keyword, the exception is passed as a keyword
|
241
|
+
- If the method accepts one positional argument, the exception is passed positionally
|
242
|
+
- Otherwise, the method is called with no arguments
|
243
|
+
|
244
|
+
::: warning
|
245
|
+
You cannot use both `if:` and `unless:` for the same callback - this will raise an `ArgumentError`.
|
246
|
+
:::
|
247
|
+
|
238
248
|
### `on_success`
|
239
249
|
|
240
250
|
This is triggered after the Axn completes, if it was successful. Difference from `after`: if the given block raises an error, this WILL be reported to the global exception handler, but will NOT change `ok?` to false.
|
@@ -280,16 +290,14 @@ def transient_error?
|
|
280
290
|
name == "temporary"
|
281
291
|
end
|
282
292
|
|
283
|
-
::: warning
|
284
|
-
You cannot use both `if:` and `unless:` for the same callback - this will raise an `ArgumentError`.
|
285
|
-
:::
|
286
|
-
|
287
293
|
on_exception(if: ->(e) { e.is_a?(ZeroDivisionError) }) do # [!code focus]
|
288
294
|
# e.g. trigger a slack error
|
289
295
|
end
|
290
296
|
end
|
291
297
|
```
|
292
298
|
|
299
|
+
|
293
300
|
If multiple `on_exception` handlers are provided, ALL that match the raised exception will be triggered in the order provided.
|
294
301
|
|
295
302
|
The _global_ handler will be triggered _after_ all class-specific handlers.
|
303
|
+
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
Somewhere at boot (e.g. `config/initializers/actions.rb` in Rails), you can call `Action.configure` to adjust a few global settings.
|
4
4
|
|
5
|
-
|
6
5
|
```ruby
|
7
6
|
Action.configure do |c|
|
8
7
|
c.log_level = :info
|
@@ -22,7 +21,6 @@ By default any swallowed errors are noted in the logs, but it's _highly recommen
|
|
22
21
|
|
23
22
|
For example, if you're using Honeybadger this could look something like:
|
24
23
|
|
25
|
-
|
26
24
|
```ruby
|
27
25
|
Action.configure do |c|
|
28
26
|
c.on_exception = proc do |e, action:, context:|
|
@@ -56,13 +54,10 @@ A couple notes:
|
|
56
54
|
* If your handler raises, the failure will _also_ be swallowed and logged
|
57
55
|
* This handler is global across _all_ Axns. You can also specify per-Action handlers via [the class-level declaration](/reference/class#on-exception).
|
58
56
|
|
59
|
-
|
60
|
-
## `top_level_around_hook`
|
57
|
+
## `wrap_with_trace` and `emit_metrics`
|
61
58
|
|
62
59
|
If you're using an APM provider, observability can be greatly enhanced by adding automatic _tracing_ of Action calls and/or emitting count metrics after each call completes.
|
63
60
|
|
64
|
-
### Tracing and Metrics
|
65
|
-
|
66
61
|
The framework provides two distinct hooks for observability:
|
67
62
|
|
68
63
|
- **`wrap_with_trace`**: An around hook that wraps the entire action execution. You MUST call the provided block to execute the action.
|
@@ -92,7 +87,6 @@ A couple notes:
|
|
92
87
|
* The `wrap_with_trace` hook is an around hook - you must call the provided block to execute the action
|
93
88
|
* The `emit_metrics` hook is called after execution with the result - do not call any blocks
|
94
89
|
|
95
|
-
|
96
90
|
## `logger`
|
97
91
|
|
98
92
|
Defaults to `Rails.logger`, if present, otherwise falls back to `Logger.new($stdout)`. But can be set to a custom logger as necessary.
|
@@ -115,6 +109,10 @@ For a practical example of this in practice, see [our 'memoization' recipe](/rec
|
|
115
109
|
|
116
110
|
Sets the log level used when you call `log "Some message"` in your Action. Note this is read via a `log_level` class method, so you can easily use inheritance to support different log levels for different sets of actions.
|
117
111
|
|
112
|
+
## `env`
|
113
|
+
|
114
|
+
Automatically detects the environment from `RACK_ENV` or `RAILS_ENV`, defaulting to `"development"`. This is used internally for conditional behavior (e.g., more verbose logging in non-production environments).
|
115
|
+
|
118
116
|
## Automatic Logging
|
119
117
|
|
120
118
|
By default, every `action.call` will emit log lines when it is called and after it completes:
|
@@ -148,3 +146,37 @@ end
|
|
148
146
|
```
|
149
147
|
|
150
148
|
The `auto_log` method supports inheritance, so subclasses will inherit the setting from their parent class unless explicitly overridden.
|
149
|
+
|
150
|
+
## Complete Configuration Example
|
151
|
+
|
152
|
+
Here's a complete example showing all available configuration options:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
Action.configure do |c|
|
156
|
+
# Logging
|
157
|
+
c.log_level = :info
|
158
|
+
c.logger = Rails.logger
|
159
|
+
|
160
|
+
# Exception handling
|
161
|
+
c.on_exception = proc do |e, action:, context:|
|
162
|
+
message = "[#{action.class.name}] Failing due to #{e.class.name}: #{e.message}"
|
163
|
+
Rails.logger.warn(message)
|
164
|
+
Honeybadger.notify(message, context: { axn_context: context })
|
165
|
+
end
|
166
|
+
|
167
|
+
# Observability
|
168
|
+
c.wrap_with_trace = proc do |resource, &action|
|
169
|
+
Datadog::Tracing.trace("Action", resource:) do
|
170
|
+
action.call
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
c.emit_metrics = proc do |resource, result|
|
175
|
+
Datadog::Metrics.increment("action.#{resource.underscore}", tags: { outcome: result.outcome })
|
176
|
+
Datadog::Metrics.histogram("action.duration", result.elapsed_time, tags: { resource: })
|
177
|
+
end
|
178
|
+
|
179
|
+
# Global includes
|
180
|
+
c.additional_includes = [MyCustomModule]
|
181
|
+
end
|
182
|
+
```
|
data/docs/reference/instance.md
CHANGED
@@ -63,7 +63,7 @@ NOTE: expects a single action call in the block -- if there are multiple calls,
|
|
63
63
|
|
64
64
|
::: tip Versus `call!`
|
65
65
|
* If you just want to make sure your action fails if the subaction fails: call subaction via `call!` (any failures will raise, which will fail the parent).
|
66
|
-
* Note this passes _child_ exception into _parent_ `
|
66
|
+
* Note this passes _child_ exception into _parent_ `error` message parsing.
|
67
67
|
* If you want _the child's_ `result.error` to become the _parent's_ `result.error` on failure, use `hoist_errors` + `call`
|
68
68
|
:::
|
69
69
|
|
data/docs/usage/writing.md
CHANGED
@@ -68,7 +68,7 @@ See [the reference doc](/reference/instance) for a few more handy helper methods
|
|
68
68
|
|
69
69
|
The default `error` and `success` message strings ("Something went wrong" / "Action completed successfully", respectively) _are_ technically safe to show users, but you'll often want to set them to something more useful.
|
70
70
|
|
71
|
-
There
|
71
|
+
There are `success` and `error` declarations for that -- you can set strings (most common) or a callable (note for the error case, if you give it a callable that expects a single argument, the exception that was raised will be passed in).
|
72
72
|
|
73
73
|
For instance, configuring the action like this:
|
74
74
|
|
@@ -79,8 +79,8 @@ class Foo
|
|
79
79
|
expects :name, type: String
|
80
80
|
exposes :meaning_of_life
|
81
81
|
|
82
|
-
|
83
|
-
|
82
|
+
success { "Revealed to #{name}: #{result.meaning_of_life}" } # [!code focus:2]
|
83
|
+
error { |e| "No secret of life for you: #{e.message}" }
|
84
84
|
|
85
85
|
def call
|
86
86
|
fail! "Douglas already knows the meaning" if name == "Doug"
|
@@ -23,28 +23,29 @@ module Action
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# ONLY raised exceptions (i.e. NOT fail!).
|
26
|
-
def on_exception(**, &block) = _add_callback(:exception, **, block:)
|
26
|
+
def on_exception(handler = nil, **, &block) = _add_callback(:exception, handler:, **, block:)
|
27
27
|
|
28
28
|
# ONLY raised on fail! (i.e. NOT unhandled exceptions).
|
29
|
-
def on_failure(**, &block) = _add_callback(:failure, **, block:)
|
29
|
+
def on_failure(handler = nil, **, &block) = _add_callback(:failure, handler:, **, block:)
|
30
30
|
|
31
31
|
# Handles both fail! and unhandled exceptions
|
32
|
-
def on_error(**, &block) = _add_callback(:error, **, block:)
|
32
|
+
def on_error(handler = nil, **, &block) = _add_callback(:error, handler:, **, block:)
|
33
33
|
|
34
34
|
# Executes when the action completes successfully (after all after hooks complete successfully)
|
35
35
|
# Runs in child-first order (child handlers before parent handlers)
|
36
|
-
def on_success(**, &block) = _add_callback(:success, **, block:)
|
36
|
+
def on_success(handler = nil, **, &block) = _add_callback(:success, handler:, **, block:)
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
def _add_callback(event_type, block
|
40
|
+
def _add_callback(event_type, handler: nil, block: nil, **kwargs)
|
41
41
|
raise ArgumentError, "on_#{event_type} cannot be called with both :if and :unless" if kwargs.key?(:if) && kwargs.key?(:unless)
|
42
42
|
|
43
43
|
condition = kwargs.key?(:if) ? kwargs[:if] : kwargs[:unless]
|
44
|
-
raise ArgumentError, "on_#{event_type} must be called with a block" unless block
|
44
|
+
raise ArgumentError, "on_#{event_type} must be called with a block or symbol" unless block || handler
|
45
45
|
|
46
|
+
callback_handler = block || handler
|
46
47
|
matcher = condition.nil? ? nil : Action::Core::Flow::Handlers::Matcher.new(condition, invert: kwargs.key?(:unless))
|
47
|
-
entry = Action::Core::Flow::Handlers::CallbackHandler.new(matcher:, handler:
|
48
|
+
entry = Action::Core::Flow::Handlers::CallbackHandler.new(matcher:, handler: callback_handler)
|
48
49
|
self._callbacks_registry = _callbacks_registry.register(event_type:, entry:)
|
49
50
|
end
|
50
51
|
end
|
data/lib/axn/version.rb
CHANGED