u-case 5.4.0 → 5.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/CLAUDE.md +11 -2
- data/README.md +55 -2
- data/README.pt-BR.md +55 -2
- data/lib/micro/case/check.rb +33 -0
- data/lib/micro/case/error.rb +24 -1
- data/lib/micro/case/result/contract.rb +60 -0
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/case.rb +23 -4
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e57208798d170fbccde7b54f5beb6220cecbfd70c3983fedcb701070a73c7bc
|
|
4
|
+
data.tar.gz: e4a0c472d1fd6edacf6ac3495d902ca851a88f5420360b708bdf2f204818cc1a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 154ce36f3efefa81e8e0dcc191cfd25396a7ad0b431f6d2a8190974036ed21f70faa617cc6834569f7fe02855b712abb33dca1da44285913c578ff73cc7cdfcc
|
|
7
|
+
data.tar.gz: 20f5e74caf9b58ae935bb39dc9780fd7bcfe21e68f4adbec162cff797284939067b2e254e790a2eb17ac8de3ce7a30460af5908170ba136730fa94b784f84a87
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
> **Note:** This gem was originally published as `u-service` (versions 0.1.0 – 1.0.0) and renamed to `u-case` starting with `u-case 1.0.0` on 2019-09-15.
|
|
9
9
|
|
|
10
|
+
## [5.5.0] - 2026-05-24
|
|
11
|
+
### Added
|
|
12
|
+
- `Micro::Case.results { |on| ... }` macro to declare a results contract — the allowed `Success`/`Failure` types and the result keys each one requires. `Success(...)` / `Failure(...)` calls that use an undeclared type now raise `Micro::Case::Error::UnexpectedResultType`; calls missing a declared required key raise `Micro::Case::Error::MissingResultKeys`. Use cases without a `results` block keep their previous unrestricted behavior. The check routes through `Micro::Case::Check#results_contract!`, so it is also bypassed when `config.disable_runtime_checks = true` (closes #22). Carve-outs so contracts don't break neighbouring features:
|
|
13
|
+
- Framework-generated `__failure_from_attributes_errors` (the auto-failure produced when `accept:`/`reject:` or ActiveModel validation rejects an input) bypasses the contract — it goes directly to `__set__` rather than through `Failure(...)` — so combining `results` with attribute validation no longer requires declaring `:invalid_attributes`.
|
|
14
|
+
- Rescued exceptions in `Micro::Case::Safe` (which produce `Failure(result: exception)`) bypass the contract.
|
|
15
|
+
- Result hashes with `String` keys are matched against the contract's symbolised required keys — `Success(result: { 'value' => 1 })` satisfies `result: [:value]`, mirroring `Result`'s own tolerance for either key type.
|
|
16
|
+
- Non-`Hash` / non-`Symbol` `result:` arguments fall through to the existing `Micro::Case::Error::InvalidResult` ("must be a Hash") instead of being misreported as missing keys.
|
|
17
|
+
- Non-`Symbol` `type` arguments fall through to `Micro::Case::Error::InvalidResultType` instead of being misreported as undeclared.
|
|
18
|
+
- `Micro::Case.results` raises `ArgumentError` when called on the abstract base class itself, so a stray declaration cannot leak a contract to every subclass in the process.
|
|
19
|
+
|
|
10
20
|
## [5.4.0] - 2026-05-24
|
|
11
21
|
### Added
|
|
12
22
|
- `Micro::Case.config.disable_runtime_checks` config (default `false`) to skip the gem's internal argument/contract checks for better performance in production. All checks are consolidated in `Micro::Case::Check::Enabled` (the default) and `Micro::Case::Check::Disabled` (no-ops with the same signature); the active module is swapped via `Micro::Case.check`. Measured throughput win is JIT-dependent: within noise on stock Ruby (no JIT), ~3–5% on Ruby 3.2 +YJIT, ~4–7% on Ruby 4.0 +PRISM (see `benchmarks/perfomance/runtime_checks/compare.rb`). Closes #45.
|
|
@@ -468,6 +478,7 @@ First release under the `u-case` name (renamed from `u-service`).
|
|
|
468
478
|
- `Micro::Service::Result` with `Success`/`Failure` factories and helper methods for returning typed results from services.
|
|
469
479
|
- Runtime dependency on `u-attributes` for service input declaration.
|
|
470
480
|
|
|
481
|
+
[5.5.0]: https://github.com/serradura/u-case/compare/v5.4.0...v5.5.0
|
|
471
482
|
[5.4.0]: https://github.com/serradura/u-case/compare/v5.3.1...v5.4.0
|
|
472
483
|
[5.3.1]: https://github.com/serradura/u-case/compare/v5.3.0...v5.3.1
|
|
473
484
|
[5.3.0]: https://github.com/serradura/u-case/compare/v5.2.1...v5.3.0
|
data/CLAUDE.md
CHANGED
|
@@ -90,8 +90,17 @@ Both files are user-facing — keep them in sync with the code:
|
|
|
90
90
|
- **`README.md` and `README.pt-BR.md`**: the **Documentation** table and the
|
|
91
91
|
**Compatibility** table at the top reference the latest released version
|
|
92
92
|
and its dependency bounds. Update both files together — they are
|
|
93
|
-
translations of each other and must stay in lockstep.
|
|
94
|
-
|
|
93
|
+
translations of each other and must stay in lockstep. Any user-visible
|
|
94
|
+
API change requires a README update in the same commit:
|
|
95
|
+
- **New public API** (new macro, new module-level method, new public
|
|
96
|
+
instance method, new error class users can rescue, new config option) —
|
|
97
|
+
add or extend the relevant section in both READMEs with an example.
|
|
98
|
+
- **Changed documented API** — update the existing section in both
|
|
99
|
+
READMEs to match the new behavior.
|
|
100
|
+
- **Removed/deprecated API** — remove or mark the section in both
|
|
101
|
+
READMEs.
|
|
102
|
+
- Pure internal refactors, CI tweaks, and test-only changes don't need
|
|
103
|
+
README updates.
|
|
95
104
|
|
|
96
105
|
## Internal argument checks live in `Micro::Case::Check`
|
|
97
106
|
|
data/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The main project goals are:
|
|
|
27
27
|
Version | Documentation
|
|
28
28
|
--------- | -------------
|
|
29
29
|
unreleased| https://github.com/serradura/u-case/blob/main/README.md
|
|
30
|
-
5.
|
|
30
|
+
5.5.0 | https://github.com/serradura/u-case/blob/v5.x/README.md
|
|
31
31
|
4.5.1 | https://github.com/serradura/u-case/blob/v4.x/README.md
|
|
32
32
|
|
|
33
33
|
> **Note:** Você entende português? 🇧🇷 🇵🇹 Verifique o [README traduzido em pt-BR](https://github.com/serradura/u-case/blob/main/README.pt-BR.md).
|
|
@@ -42,6 +42,7 @@ unreleased| https://github.com/serradura/u-case/blob/main/README.md
|
|
|
42
42
|
- [What are the default result types?](#what-are-the-default-result-types)
|
|
43
43
|
- [How to define custom result types?](#how-to-define-custom-result-types)
|
|
44
44
|
- [Is it possible to define a custom type without a result data?](#is-it-possible-to-define-a-custom-type-without-a-result-data)
|
|
45
|
+
- [How to declare a results contract?](#how-to-declare-a-results-contract)
|
|
45
46
|
- [How to use the result hooks?](#how-to-use-the-result-hooks)
|
|
46
47
|
- [Why the hook usage without a defined type exposes the result itself?](#why-the-hook-usage-without-a-defined-type-exposes-the-result-itself)
|
|
47
48
|
- [Using decomposition to access the result data and type](#using-decomposition-to-access-the-result-data-and-type)
|
|
@@ -90,7 +91,7 @@ unreleased| https://github.com/serradura/u-case/blob/main/README.md
|
|
|
90
91
|
| u-case | branch | ruby | activemodel | u-attributes |
|
|
91
92
|
| ---------------- | ------ | -------- | -------------- | -------------- |
|
|
92
93
|
| unreleased | main | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
|
|
93
|
-
| 5.
|
|
94
|
+
| 5.5.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
|
|
94
95
|
| 5.1.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.7, < 4.0 |
|
|
95
96
|
| 4.5.1 | v4.x | >= 2.2.0 | >= 3.2, <= 8.1 | >= 2.7, < 3.0 |
|
|
96
97
|
|
|
@@ -335,6 +336,58 @@ result.use_case.attributes # {"a"=>2, "b"=>"2"}
|
|
|
335
336
|
|
|
336
337
|
[⬆️ Back to Top](#table-of-contents-)
|
|
337
338
|
|
|
339
|
+
#### How to declare a results contract?
|
|
340
|
+
|
|
341
|
+
Answer: Use the `results do |on| ... end` macro to declare which result types your use case can return, and which keys each one requires. When a contract is declared, `Success(...)` / `Failure(...)` calls that use an undeclared type raise `Micro::Case::Error::UnexpectedResultType`, and calls that omit a declared required key raise `Micro::Case::Error::MissingResultKeys`.
|
|
342
|
+
|
|
343
|
+
```ruby
|
|
344
|
+
class Divide < Micro::Case
|
|
345
|
+
attributes :a, :b
|
|
346
|
+
|
|
347
|
+
results do |on|
|
|
348
|
+
on.failure(:attributes_must_be_numbers)
|
|
349
|
+
on.failure(:division_by_zero)
|
|
350
|
+
|
|
351
|
+
on.success(result: [:division])
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def call!
|
|
355
|
+
return Failure(:attributes_must_be_numbers) unless Kind.of?(Numeric, a, b)
|
|
356
|
+
return Failure(:division_by_zero) if b == 0
|
|
357
|
+
|
|
358
|
+
Success result: { division: a / b }
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
Divide.call(a: 10, b: 2).data # => { division: 5 }
|
|
363
|
+
Divide.call(a: 10, b: 0).type # => :division_by_zero
|
|
364
|
+
Divide.call(a: 'x', b: 2).type # => :attributes_must_be_numbers
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
A type passed to `on.success` / `on.failure` without a `result:` argument declares the type with no required keys (any payload — including the implicit `{ type => true }` from `Failure(:my_type)` — is accepted). When `result: [:key1, :key2]` is given, those keys must be present in the result hash; extra keys are allowed.
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
class Wrong < Micro::Case
|
|
371
|
+
results do |on|
|
|
372
|
+
on.success(result: [:value])
|
|
373
|
+
on.failure(:known)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def call!
|
|
377
|
+
Success(:other, result: { value: 1 }) # raises Micro::Case::Error::UnexpectedResultType
|
|
378
|
+
# Success(result: { wrong: 1 }) # raises Micro::Case::Error::MissingResultKeys
|
|
379
|
+
# Failure(:other) # raises Micro::Case::Error::UnexpectedResultType
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Notes:
|
|
385
|
+
- Use cases without a `results` block keep their previous unrestricted behavior — the contract is opt-in.
|
|
386
|
+
- Subclasses inherit the parent's contract.
|
|
387
|
+
- Rescued exceptions in `Micro::Case::Safe` (which produce `Failure(result: exception)` automatically) bypass the contract.
|
|
388
|
+
|
|
389
|
+
[⬆️ Back to Top](#table-of-contents-)
|
|
390
|
+
|
|
338
391
|
#### How to use the result hooks?
|
|
339
392
|
|
|
340
393
|
As [mentioned earlier](#microcaseresult---what-is-a-use-case-result), the `Micro::Case::Result` has two methods to improve the application flow control. They are: `#on_success`, `on_failure`.
|
data/README.pt-BR.md
CHANGED
|
@@ -27,7 +27,7 @@ Principais objetivos deste projeto:
|
|
|
27
27
|
Versão | Documentação
|
|
28
28
|
--------- | -------------
|
|
29
29
|
unreleased| https://github.com/serradura/u-case/blob/main/README.md
|
|
30
|
-
5.
|
|
30
|
+
5.5.0 | https://github.com/serradura/u-case/blob/v5.x/README.md
|
|
31
31
|
4.5.1 | https://github.com/serradura/u-case/blob/v4.x/README.md
|
|
32
32
|
|
|
33
33
|
## Índice <!-- omit in toc -->
|
|
@@ -40,6 +40,7 @@ unreleased| https://github.com/serradura/u-case/blob/main/README.md
|
|
|
40
40
|
- [O que são os tipos de resultados?](#o-que-são-os-tipos-de-resultados)
|
|
41
41
|
- [Como definir tipos customizados de resultados?](#como-definir-tipos-customizados-de-resultados)
|
|
42
42
|
- [É possível definir um tipo sem definir os dados do resultado?](#é-possível-definir-um-tipo-sem-definir-os-dados-do-resultado)
|
|
43
|
+
- [Como declarar um contrato de resultados?](#como-declarar-um-contrato-de-resultados)
|
|
43
44
|
- [Como utilizar os hooks dos resultados?](#como-utilizar-os-hooks-dos-resultados)
|
|
44
45
|
- [Por que o hook sem um tipo definido expõe o próprio resultado?](#por-que-o-hook-sem-um-tipo-definido-expõe-o-próprio-resultado)
|
|
45
46
|
- [Usando decomposição para acessar os dados e tipo do resultado](#usando-decomposição-para-acessar-os-dados-e-tipo-do-resultado)
|
|
@@ -88,7 +89,7 @@ unreleased| https://github.com/serradura/u-case/blob/main/README.md
|
|
|
88
89
|
| u-case | branch | ruby | activemodel | u-attributes |
|
|
89
90
|
| ---------------- | ------ | -------- | -------------- | -------------- |
|
|
90
91
|
| unreleased | main | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
|
|
91
|
-
| 5.
|
|
92
|
+
| 5.5.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
|
|
92
93
|
| 5.1.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.7, < 4.0 |
|
|
93
94
|
| 4.5.1 | v4.x | >= 2.2.0 | >= 3.2, <= 8.1 | >= 2.7, < 3.0 |
|
|
94
95
|
|
|
@@ -333,6 +334,58 @@ result.use_case.attributes # {"a"=>2, "b"=>"2"}
|
|
|
333
334
|
|
|
334
335
|
[⬆️ Voltar para o índice](#índice-)
|
|
335
336
|
|
|
337
|
+
#### Como declarar um contrato de resultados?
|
|
338
|
+
|
|
339
|
+
Resposta: Utilize a macro `results do |on| ... end` para declarar quais tipos de resultado o caso de uso pode retornar e quais chaves cada um exige. Quando há um contrato declarado, chamadas a `Success(...)` / `Failure(...)` que usem um tipo não declarado levantam `Micro::Case::Error::UnexpectedResultType`, e chamadas que omitam uma chave obrigatória declarada levantam `Micro::Case::Error::MissingResultKeys`.
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
class Divide < Micro::Case
|
|
343
|
+
attributes :a, :b
|
|
344
|
+
|
|
345
|
+
results do |on|
|
|
346
|
+
on.failure(:attributes_must_be_numbers)
|
|
347
|
+
on.failure(:division_by_zero)
|
|
348
|
+
|
|
349
|
+
on.success(result: [:division])
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def call!
|
|
353
|
+
return Failure(:attributes_must_be_numbers) unless Kind.of?(Numeric, a, b)
|
|
354
|
+
return Failure(:division_by_zero) if b == 0
|
|
355
|
+
|
|
356
|
+
Success result: { division: a / b }
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
Divide.call(a: 10, b: 2).data # => { division: 5 }
|
|
361
|
+
Divide.call(a: 10, b: 0).type # => :division_by_zero
|
|
362
|
+
Divide.call(a: 'x', b: 2).type # => :attributes_must_be_numbers
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Um tipo declarado em `on.success` / `on.failure` sem o argumento `result:` é aceito sem chaves obrigatórias (qualquer payload — inclusive o implícito `{ tipo => true }` de `Failure(:meu_tipo)` — é aceito). Quando `result: [:chave_1, :chave_2]` é informado, essas chaves precisam estar presentes no hash de resultado; chaves extras são permitidas.
|
|
366
|
+
|
|
367
|
+
```ruby
|
|
368
|
+
class Wrong < Micro::Case
|
|
369
|
+
results do |on|
|
|
370
|
+
on.success(result: [:value])
|
|
371
|
+
on.failure(:known)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def call!
|
|
375
|
+
Success(:other, result: { value: 1 }) # levanta Micro::Case::Error::UnexpectedResultType
|
|
376
|
+
# Success(result: { wrong: 1 }) # levanta Micro::Case::Error::MissingResultKeys
|
|
377
|
+
# Failure(:other) # levanta Micro::Case::Error::UnexpectedResultType
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Notas:
|
|
383
|
+
- Casos de uso sem o bloco `results` mantêm o comportamento anterior sem restrições — o contrato é opt-in.
|
|
384
|
+
- Subclasses herdam o contrato declarado na classe pai.
|
|
385
|
+
- Exceções capturadas em `Micro::Case::Safe` (que geram `Failure(result: exception)` automaticamente) são exemptas do contrato.
|
|
386
|
+
|
|
387
|
+
[⬆️ Voltar para o índice](#índice-)
|
|
388
|
+
|
|
336
389
|
#### Como utilizar os hooks dos resultados?
|
|
337
390
|
|
|
338
391
|
Como [mencionando anteriormente](#microcaseresult---o-que-é-o-resultado-de-um-caso-de-uso), o `Micro::Case::Result` tem dois métodos para melhorar o controle do fluxo da aplicação. São eles:
|
data/lib/micro/case/check.rb
CHANGED
|
@@ -59,6 +59,38 @@ module Micro
|
|
|
59
59
|
def hash!(arg)
|
|
60
60
|
Kind::Hash[arg]
|
|
61
61
|
end
|
|
62
|
+
|
|
63
|
+
def results_contract!(use_case_class, kind, type, value)
|
|
64
|
+
contract = use_case_class.__results_contract__
|
|
65
|
+
return unless contract
|
|
66
|
+
return unless type.is_a?(Symbol)
|
|
67
|
+
return if value.is_a?(Exception)
|
|
68
|
+
|
|
69
|
+
if kind == :success
|
|
70
|
+
declared = contract.success_declared?(type)
|
|
71
|
+
declared_types = contract.successes.keys
|
|
72
|
+
required = contract.success_keys(type) if declared
|
|
73
|
+
else
|
|
74
|
+
declared = contract.failure_declared?(type)
|
|
75
|
+
declared_types = contract.failures.keys
|
|
76
|
+
required = contract.failure_keys(type) if declared
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
raise Error::UnexpectedResultType.new(use_case_class, kind, type, declared_types) unless declared
|
|
80
|
+
return if required.nil? || required.empty?
|
|
81
|
+
|
|
82
|
+
if value.is_a?(Hash)
|
|
83
|
+
data_keys = value.keys.map { |k| k.is_a?(String) ? k.to_sym : k }
|
|
84
|
+
elsif value.is_a?(Symbol)
|
|
85
|
+
data_keys = [type]
|
|
86
|
+
else
|
|
87
|
+
return
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
missing = required - data_keys
|
|
91
|
+
|
|
92
|
+
raise Error::MissingResultKeys.new(use_case_class, kind, type, missing) unless missing.empty?
|
|
93
|
+
end
|
|
62
94
|
end
|
|
63
95
|
|
|
64
96
|
module Disabled
|
|
@@ -76,6 +108,7 @@ module Micro
|
|
|
76
108
|
def flow_use_cases!(_use_cases); end
|
|
77
109
|
def map_args!(_args); end
|
|
78
110
|
def hash!(arg); arg; end
|
|
111
|
+
def results_contract!(_use_case_class, _kind, _type, _value); end
|
|
79
112
|
end
|
|
80
113
|
end
|
|
81
114
|
end
|
data/lib/micro/case/error.rb
CHANGED
|
@@ -60,9 +60,32 @@ module Micro
|
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
class UnexpectedResultType < TypeError
|
|
64
|
+
def initialize(use_case_class, kind, type, declared_types)
|
|
65
|
+
declared_list = declared_types.map { |t| ":#{t}" }.join(', ')
|
|
66
|
+
declared_list = '(none)' if declared_list.empty?
|
|
67
|
+
|
|
68
|
+
super(
|
|
69
|
+
"#{use_case_class.name} declared a results contract — " \
|
|
70
|
+
"#{kind} type :#{type} is not declared. Declared #{kind} types: #{declared_list}."
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class MissingResultKeys < ArgumentError
|
|
76
|
+
def initialize(use_case_class, kind, type, missing_keys)
|
|
77
|
+
missing_list = missing_keys.map { |k| ":#{k}" }.join(', ')
|
|
78
|
+
|
|
79
|
+
super(
|
|
80
|
+
"#{use_case_class.name} declared a results contract — " \
|
|
81
|
+
"#{kind} :#{type} is missing required result keys: #{missing_list}."
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
63
86
|
def self.by_wrong_usage?(exception)
|
|
64
87
|
case exception
|
|
65
|
-
when Kind::Error, ArgumentError, InvalidResult, UnexpectedResult then true
|
|
88
|
+
when Kind::Error, ArgumentError, InvalidResult, UnexpectedResult, UnexpectedResultType then true
|
|
66
89
|
else false
|
|
67
90
|
end
|
|
68
91
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Micro
|
|
4
|
+
class Case
|
|
5
|
+
class Result
|
|
6
|
+
class Contract
|
|
7
|
+
attr_reader :successes, :failures
|
|
8
|
+
|
|
9
|
+
def self.define(&block)
|
|
10
|
+
contract = new
|
|
11
|
+
block.call(Definition.new(contract))
|
|
12
|
+
contract
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@successes = {}
|
|
17
|
+
@failures = {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add_success(type, keys)
|
|
21
|
+
@successes[type] = Array(keys).map(&:to_sym)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def add_failure(type, keys)
|
|
25
|
+
@failures[type] = Array(keys).map(&:to_sym)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def success_declared?(type)
|
|
29
|
+
@successes.key?(type)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def failure_declared?(type)
|
|
33
|
+
@failures.key?(type)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def success_keys(type)
|
|
37
|
+
@successes[type]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def failure_keys(type)
|
|
41
|
+
@failures[type]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Definition
|
|
45
|
+
def initialize(contract)
|
|
46
|
+
@contract = contract
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def success(type = :ok, result: nil)
|
|
50
|
+
@contract.add_success(Kind::Symbol[type], result)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def failure(type = :error, result: nil)
|
|
54
|
+
@contract.add_failure(Kind::Symbol[type], result)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/micro/case/version.rb
CHANGED
data/lib/micro/case.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Micro
|
|
|
11
11
|
require 'micro/case/utils'
|
|
12
12
|
require 'micro/case/error'
|
|
13
13
|
require 'micro/case/result'
|
|
14
|
+
require 'micro/case/result/contract'
|
|
14
15
|
require 'micro/case/check'
|
|
15
16
|
require 'micro/case/config'
|
|
16
17
|
require 'micro/case/safe'
|
|
@@ -66,6 +67,20 @@ module Micro
|
|
|
66
67
|
@__flow_use_cases = Cases::Utils.map_use_cases(args)
|
|
67
68
|
end
|
|
68
69
|
|
|
70
|
+
def self.results(&block)
|
|
71
|
+
raise ArgumentError, 'a block is required'.freeze unless block
|
|
72
|
+
raise ArgumentError, 'must be called on a Micro::Case subclass, not on Micro::Case itself'.freeze if self == ::Micro::Case
|
|
73
|
+
|
|
74
|
+
@__results_contract = Result::Contract.define(&block)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.__results_contract__
|
|
78
|
+
return @__results_contract if defined?(@__results_contract)
|
|
79
|
+
|
|
80
|
+
parent = superclass
|
|
81
|
+
parent.respond_to?(:__results_contract__) ? parent.__results_contract__ : nil
|
|
82
|
+
end
|
|
83
|
+
|
|
69
84
|
class << self
|
|
70
85
|
alias __call__ call
|
|
71
86
|
|
|
@@ -227,9 +242,10 @@ module Micro
|
|
|
227
242
|
end
|
|
228
243
|
|
|
229
244
|
def __failure_from_attributes_errors
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
245
|
+
__get_result(
|
|
246
|
+
false,
|
|
247
|
+
{ errors: attributes_errors },
|
|
248
|
+
Config.instance.activemodel_validation_errors_failure
|
|
233
249
|
)
|
|
234
250
|
end
|
|
235
251
|
|
|
@@ -244,6 +260,8 @@ module Micro
|
|
|
244
260
|
def Success(type = :ok, result: nil)
|
|
245
261
|
value = result || type
|
|
246
262
|
|
|
263
|
+
::Micro::Case.check.results_contract!(self.class, :success, type, value)
|
|
264
|
+
|
|
247
265
|
__get_result(true, value, type)
|
|
248
266
|
end
|
|
249
267
|
|
|
@@ -260,10 +278,11 @@ module Micro
|
|
|
260
278
|
|
|
261
279
|
type = MapFailureType.call(value, type)
|
|
262
280
|
|
|
281
|
+
::Micro::Case.check.results_contract!(self.class, :failure, type, value)
|
|
282
|
+
|
|
263
283
|
__get_result(false, value, type)
|
|
264
284
|
end
|
|
265
285
|
|
|
266
|
-
|
|
267
286
|
def Check(type = nil, result: nil, on: Kind::Empty::HASH)
|
|
268
287
|
result_key = type || :check
|
|
269
288
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: u-case
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rodrigo Serradura
|
|
@@ -122,6 +122,7 @@ files:
|
|
|
122
122
|
- lib/micro/case/config.rb
|
|
123
123
|
- lib/micro/case/error.rb
|
|
124
124
|
- lib/micro/case/result.rb
|
|
125
|
+
- lib/micro/case/result/contract.rb
|
|
125
126
|
- lib/micro/case/result/transitions.rb
|
|
126
127
|
- lib/micro/case/result/wrapper.rb
|
|
127
128
|
- lib/micro/case/safe.rb
|