axn 0.1.0.pre.alpha.2.1 → 0.1.0.pre.alpha.2.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 +10 -0
- data/docs/recipes/testing.md +30 -1
- data/docs/reference/class.md +1 -0
- data/lib/action/core/context_facade.rb +14 -4
- data/lib/action/core/contract.rb +6 -5
- data/lib/action/core/contract_validator.rb +2 -0
- data/lib/action/core/hoist_errors.rb +3 -1
- data/lib/axn/factory.rb +14 -1
- data/lib/axn/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac3f240b093cdf14fef16b5eb6993dbb8f6d49ba58c837245128a0efb3558f11
|
4
|
+
data.tar.gz: 40b631f49349b51811ebb0bbc60cbc7962a3f7d633bab1134b7b234b4f681123
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8ac8be69e70d72d04b0c5be89e4ec198223c4a1771a15d254b82b0a204543c627a80321b3de95d69fa97a244fc56c9c39ca59262600c6b991c363603e0a7beb
|
7
|
+
data.tar.gz: 4eba5152626d0417ff3be725da9512034276d4b034898f8ad331890499196c1851a1d3dcd1cde880955c42f425090ab6c5fe30cac1496835013c7bd6cda52cdb
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,16 @@
|
|
3
3
|
## UNRELEASED
|
4
4
|
* N/A
|
5
5
|
|
6
|
+
## 0.1.0-alpha.2.3
|
7
|
+
* `expects` / `exposes`: Add `type: :uuid` special case validation
|
8
|
+
* [BUGFIX] Allow `hoist_errors` to pass the result through on success (allow access to subactions' exposures)
|
9
|
+
* [`Axn::Factory`] Support error_from + rescues
|
10
|
+
* `Action::Result.error` spec helper -- creation should NOT trigger global exception handler
|
11
|
+
* [CHANGE] `expects` / `exposes`: The `default` key, if a callable, should be evaluated in the _instance_'s context
|
12
|
+
|
13
|
+
## 0.1.0-alpha.2.2
|
14
|
+
* Expands `Action::Result.ok` and `Action::Result.error` to better support mocking in specs
|
15
|
+
|
6
16
|
## 0.1.0-alpha.2.1
|
7
17
|
* Expects/Exposes: Add `allow_nil` option
|
8
18
|
* Expects/Exposes: Replace `boolean: true` with `type: :boolean`
|
data/docs/recipes/testing.md
CHANGED
@@ -4,7 +4,36 @@
|
|
4
4
|
* TODO: document testing patterns
|
5
5
|
:::
|
6
6
|
|
7
|
-
|
7
|
+
## Mocking Axn calls
|
8
|
+
|
9
|
+
Say you're writing unit specs for PrimaryAction that calls Subaction, and you want to mock out the Subaction call.
|
10
|
+
|
11
|
+
To generate a successful Action::Result:
|
12
|
+
|
13
|
+
* Base case: `Action::Result.ok`
|
14
|
+
* [Optional] Custom message: `Action::Result.ok("It went awesome")`
|
15
|
+
* [Optional] Custom exposures: `Action::Result.ok("It went awesome", some_var: 123)`
|
16
|
+
|
17
|
+
To generate a failed Action::Result:
|
18
|
+
|
19
|
+
* Base case: `Action::Result.error`
|
20
|
+
* [Optional] Custom message: `Action::Result.error("It went poorly")`
|
21
|
+
* [Optional] Custom exposures: `Action::Result.error("It went poorly", some_var: 123)`
|
22
|
+
* [Optional] Custom exception: `Action::Result.error(some_var: 123) { raise FooBarException.new("bad thing") }`
|
23
|
+
|
24
|
+
Either way, using those to mock an actual call would look something like this in your rspec:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
let(:subaction_response) { Action::Result.ok("custom message", foo: 1) }
|
28
|
+
|
29
|
+
before do
|
30
|
+
expect(Subaction).to receive(:call).and_return(subaction_response)
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
## RSpec configuration
|
35
|
+
|
36
|
+
Configuring rspec to treat files in spec/actions as service specs (very optional):
|
8
37
|
|
9
38
|
```ruby
|
10
39
|
RSpec.configure do |config|
|
data/docs/reference/class.md
CHANGED
@@ -25,6 +25,7 @@ While we _support_ complex interface validations, in practice you usually just w
|
|
25
25
|
In addition to the [standard ActiveModel validations](https://guides.rubyonrails.org/active_record_validations.html), we also support two additional custom validators:
|
26
26
|
* `type: Foo` - fails unless the provided value `.is_a?(Foo)`
|
27
27
|
* Edge case: use `type: :boolean` to handle a boolean field (since ruby doesn't have a Boolean class to pass in directly)
|
28
|
+
* Edge case: use `type: :uuid` to handle a confirming given string is a UUID (with or without `-` chars)
|
28
29
|
* `validate: [callable]` - Support custom validations (fails if any string is returned OR if it raises an exception)
|
29
30
|
* Example:
|
30
31
|
```ruby
|
@@ -94,11 +94,21 @@ module Action
|
|
94
94
|
class Result < ContextFacade
|
95
95
|
# For ease of mocking return results in tests
|
96
96
|
class << self
|
97
|
-
def ok =
|
97
|
+
def ok(msg = nil, **exposures)
|
98
|
+
Axn::Factory.build(exposes: exposures.keys, messages: { success: msg }) do
|
99
|
+
exposures.each do |key, value|
|
100
|
+
expose(key, value)
|
101
|
+
end
|
102
|
+
end.call
|
103
|
+
end
|
98
104
|
|
99
|
-
def error(msg =
|
100
|
-
|
101
|
-
|
105
|
+
def error(msg = nil, **exposures, &block)
|
106
|
+
Axn::Factory.build(exposes: exposures.keys, rescues: [-> { true }, msg]) do
|
107
|
+
exposures.each do |key, value|
|
108
|
+
expose(key, value)
|
109
|
+
end
|
110
|
+
block.call if block_given?
|
111
|
+
fail!
|
102
112
|
end.call
|
103
113
|
end
|
104
114
|
end
|
data/lib/action/core/contract.rb
CHANGED
@@ -163,11 +163,12 @@ module Action
|
|
163
163
|
hash[config.field] = config.default
|
164
164
|
end.compact
|
165
165
|
|
166
|
-
defaults_mapping.each do |field,
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
166
|
+
defaults_mapping.each do |field, default_value_getter|
|
167
|
+
next if @context.public_send(field).present?
|
168
|
+
|
169
|
+
default_value = default_value_getter.respond_to?(:call) ? instance_exec(&default_value_getter) : default_value_getter
|
170
|
+
|
171
|
+
@context.public_send("#{field}=", default_value)
|
171
172
|
end
|
172
173
|
end
|
173
174
|
|
@@ -54,6 +54,8 @@ module Action
|
|
54
54
|
record.errors.add attribute, (options[:message] || msg) unless types.any? do |type|
|
55
55
|
if type == :boolean
|
56
56
|
[true, false].include?(value)
|
57
|
+
elsif type == :uuid
|
58
|
+
value.is_a?(String) && value.match?(/\A[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}\z/i)
|
57
59
|
else
|
58
60
|
value.is_a?(type)
|
59
61
|
end
|
@@ -36,7 +36,9 @@ module Action
|
|
36
36
|
"#hoist_errors is expected to wrap an Action call, but it returned a #{result.class.name} instead"
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
return result if result.ok?
|
40
|
+
|
41
|
+
_handle_hoisted_errors(result, prefix:)
|
40
42
|
end
|
41
43
|
|
42
44
|
# Separate method to allow overriding in subclasses
|
data/lib/axn/factory.rb
CHANGED
@@ -14,6 +14,10 @@ module Axn
|
|
14
14
|
exposes: [],
|
15
15
|
expects: [],
|
16
16
|
messages: {},
|
17
|
+
error_from: {},
|
18
|
+
rescues: {},
|
19
|
+
|
20
|
+
# Hooks
|
17
21
|
before: nil,
|
18
22
|
after: nil,
|
19
23
|
around: nil,
|
@@ -71,7 +75,10 @@ module Axn
|
|
71
75
|
axn.exposes(field, **opts)
|
72
76
|
end
|
73
77
|
|
74
|
-
axn.messages(**messages) if messages.present?
|
78
|
+
axn.messages(**messages) if messages.present? && messages.values.any?(&:present?)
|
79
|
+
|
80
|
+
axn.error_from(**_array_to_hash(error_from)) if error_from.present?
|
81
|
+
axn.rescues(**_array_to_hash(rescues)) if rescues.present?
|
75
82
|
|
76
83
|
# Hooks
|
77
84
|
axn.before(before) if before.present?
|
@@ -98,6 +105,12 @@ module Axn
|
|
98
105
|
|
99
106
|
def _hash_with_default_array = Hash.new { |h, k| h[k] = [] }
|
100
107
|
|
108
|
+
def _array_to_hash(given)
|
109
|
+
return given if given.is_a?(Hash)
|
110
|
+
|
111
|
+
[given].to_h
|
112
|
+
end
|
113
|
+
|
101
114
|
def _hydrate_hash(given)
|
102
115
|
return given if given.is_a?(Hash)
|
103
116
|
|
data/lib/axn/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: axn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.pre.alpha.2.
|
4
|
+
version: 0.1.0.pre.alpha.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kali Donovan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|