light-services 3.0.0 → 3.1.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/.rubocop.yml +7 -1
- data/CHANGELOG.md +21 -0
- data/CLAUDE.md +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +11 -11
- data/docs/{readme.md → README.md} +12 -11
- data/docs/{summary.md → SUMMARY.md} +11 -1
- data/docs/arguments.md +23 -0
- data/docs/concepts.md +19 -19
- data/docs/configuration.md +36 -0
- data/docs/errors.md +31 -1
- data/docs/outputs.md +23 -0
- data/docs/quickstart.md +1 -1
- data/docs/rubocop.md +285 -0
- data/docs/ruby-lsp.md +133 -0
- data/docs/steps.md +62 -8
- data/docs/testing.md +1 -1
- data/lib/light/services/base.rb +110 -7
- data/lib/light/services/base_with_context.rb +23 -1
- data/lib/light/services/callbacks.rb +293 -41
- data/lib/light/services/collection.rb +50 -2
- data/lib/light/services/concerns/execution.rb +3 -0
- data/lib/light/services/config.rb +83 -3
- data/lib/light/services/constants.rb +3 -0
- data/lib/light/services/dsl/arguments_dsl.rb +1 -0
- data/lib/light/services/dsl/outputs_dsl.rb +1 -0
- data/lib/light/services/dsl/validation.rb +30 -0
- data/lib/light/services/exceptions.rb +19 -1
- data/lib/light/services/message.rb +28 -3
- data/lib/light/services/messages.rb +74 -2
- data/lib/light/services/rubocop/cop/light_services/argument_type_required.rb +52 -0
- data/lib/light/services/rubocop/cop/light_services/condition_method_exists.rb +173 -0
- data/lib/light/services/rubocop/cop/light_services/deprecated_methods.rb +113 -0
- data/lib/light/services/rubocop/cop/light_services/dsl_order.rb +176 -0
- data/lib/light/services/rubocop/cop/light_services/missing_private_keyword.rb +102 -0
- data/lib/light/services/rubocop/cop/light_services/no_direct_instantiation.rb +66 -0
- data/lib/light/services/rubocop/cop/light_services/output_type_required.rb +52 -0
- data/lib/light/services/rubocop/cop/light_services/step_method_exists.rb +109 -0
- data/lib/light/services/rubocop.rb +12 -0
- data/lib/light/services/settings/field.rb +33 -5
- data/lib/light/services/settings/step.rb +23 -5
- data/lib/light/services/version.rb +1 -1
- data/lib/ruby_lsp/light_services/addon.rb +36 -0
- data/lib/ruby_lsp/light_services/definition.rb +132 -0
- data/lib/ruby_lsp/light_services/indexing_enhancement.rb +263 -0
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a7b40ae2a9930c6ee5831c6d5f64f84aeab383b4f35295a41faf8ae9e0735c82
|
|
4
|
+
data.tar.gz: 8bb7c192556ec28c904bd931abbf502c2533b39137c2d7d989469c11c61cd70d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 671d913a895af97d0c37e45d21e930794317d60fac03bd46df3e43ef3dc6190617b5b5522f445f57143d96434104df636f5d29cf9c6c35d60604f61f54781dfa
|
|
7
|
+
data.tar.gz: eacef0ee6679653963d6b09a6c660c9ee503e8edc3df04411547f36e07bea91210d2683ff02ed1ee6d96924062c6e1620f95b87959e40ac80e78a211c7048f96
|
data/.rubocop.yml
CHANGED
|
@@ -58,6 +58,9 @@ RSpec/MultipleDescribes:
|
|
|
58
58
|
RSpec/MultipleExpectations:
|
|
59
59
|
Max: 10
|
|
60
60
|
|
|
61
|
+
RSpec/MultipleMemoizedHelpers:
|
|
62
|
+
Max: 10
|
|
63
|
+
|
|
61
64
|
RSpec/NestedGroups:
|
|
62
65
|
Max: 5
|
|
63
66
|
|
|
@@ -101,4 +104,7 @@ Style/TrailingCommaInHashLiteral:
|
|
|
101
104
|
EnforcedStyleForMultiline: consistent_comma
|
|
102
105
|
|
|
103
106
|
Style/WordArray:
|
|
104
|
-
EnforcedStyle: brackets
|
|
107
|
+
EnforcedStyle: brackets
|
|
108
|
+
|
|
109
|
+
Style/NumericPredicate:
|
|
110
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.1.1 (2025-12-13)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Better IDE support for callbacks DSL
|
|
8
|
+
|
|
9
|
+
## 3.1.0 (2025-12-13)
|
|
10
|
+
|
|
11
|
+
### Breaking changes
|
|
12
|
+
|
|
13
|
+
- Enforce arguments and output types by default. Add `config.require_type = false` to your config to disable this behavior.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `stop!` and `stopped?` methods for early exit (renamed from `done!` and `done?`)
|
|
18
|
+
- `stop_immediately!` method for immediate execution halt within the current step
|
|
19
|
+
- `done!` and `done?` are deprecated, but remain available as aliases for backward compatibility
|
|
20
|
+
- Ruby LSP support with step navigation and indexing
|
|
21
|
+
- Rubocop cops `StepMethodExists`, `ConditionMethodExists`, `DslOrder`, `MissingPrivateKeyword`, `NoDirectInstantiation`, `ArgumentTypeRequired`, `OutputTypeRequired`, `DeprecatedMethods`
|
|
22
|
+
- Comprehensive YARD documentation
|
|
23
|
+
|
|
3
24
|
## 3.0.0 (2025-12-12)
|
|
4
25
|
|
|
5
26
|
### Breaking changes
|
data/CLAUDE.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -36,15 +36,15 @@ rails generate light_services:install
|
|
|
36
36
|
```ruby
|
|
37
37
|
class GreetService < Light::Services::Base
|
|
38
38
|
# Arguments
|
|
39
|
-
arg :name
|
|
40
|
-
arg :age
|
|
39
|
+
arg :name, type: String
|
|
40
|
+
arg :age, type: Integer
|
|
41
41
|
|
|
42
42
|
# Steps
|
|
43
43
|
step :build_message
|
|
44
44
|
step :send_message
|
|
45
45
|
|
|
46
46
|
# Outputs
|
|
47
|
-
output :message
|
|
47
|
+
output :message, type: String
|
|
48
48
|
|
|
49
49
|
private
|
|
50
50
|
|
|
@@ -58,14 +58,14 @@ class GreetService < Light::Services::Base
|
|
|
58
58
|
end
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
## Advanced Example
|
|
61
|
+
## Advanced Example (with dry-types and conditions)
|
|
62
62
|
|
|
63
63
|
```ruby
|
|
64
64
|
class User::ResetPassword < Light::Services::Base
|
|
65
|
-
# Arguments
|
|
66
|
-
arg :user, type: User, optional: true
|
|
67
|
-
arg :email, type: String, optional: true
|
|
68
|
-
arg :send_email, type:
|
|
65
|
+
# Arguments with dry-types for advanced validation and coercion
|
|
66
|
+
arg :user, type: Types.Instance(User), optional: true
|
|
67
|
+
arg :email, type: Types::Coercible::String, optional: true
|
|
68
|
+
arg :send_email, type: Types::Params::Bool, default: true
|
|
69
69
|
|
|
70
70
|
# Steps
|
|
71
71
|
step :validate
|
|
@@ -74,9 +74,9 @@ class User::ResetPassword < Light::Services::Base
|
|
|
74
74
|
step :save_reset_token
|
|
75
75
|
step :send_reset_email, if: :send_email?
|
|
76
76
|
|
|
77
|
-
# Outputs
|
|
78
|
-
output :user, type: User
|
|
79
|
-
output :reset_token, type: String
|
|
77
|
+
# Outputs with dry-types
|
|
78
|
+
output :user, type: Types.Instance(User)
|
|
79
|
+
output :reset_token, type: Types::Strict::String
|
|
80
80
|
|
|
81
81
|
private
|
|
82
82
|
|
|
@@ -13,6 +13,7 @@ Light Services is a simple yet powerful way to organize business logic in Ruby a
|
|
|
13
13
|
- ⚠️ **Error Handling**: Collect errors from steps and handle them your way
|
|
14
14
|
- 🔗 **Context**: Run multiple services sequentially within the same context
|
|
15
15
|
- 🧪 **RSpec Matchers**: Built-in RSpec matchers for expressive service tests
|
|
16
|
+
- 🔍 **RuboCop Integration**: Custom cops to enforce best practices at lint time
|
|
16
17
|
- 🌐 **Framework Agnostic**: Compatible with Rails, Hanami, or any Ruby framework
|
|
17
18
|
- 🧩 **Modularity**: Isolate and test your services with ease
|
|
18
19
|
- ✅ **100% Test Coverage**: Thoroughly tested and reliable
|
|
@@ -23,15 +24,15 @@ Light Services is a simple yet powerful way to organize business logic in Ruby a
|
|
|
23
24
|
```ruby
|
|
24
25
|
class GreetService < Light::Services::Base
|
|
25
26
|
# Arguments
|
|
26
|
-
arg :name
|
|
27
|
-
arg :age
|
|
27
|
+
arg :name, type: String
|
|
28
|
+
arg :age, type: Integer
|
|
28
29
|
|
|
29
30
|
# Steps
|
|
30
31
|
step :build_message
|
|
31
32
|
step :send_message
|
|
32
33
|
|
|
33
34
|
# Outputs
|
|
34
|
-
output :message
|
|
35
|
+
output :message, type: String
|
|
35
36
|
|
|
36
37
|
private
|
|
37
38
|
|
|
@@ -45,14 +46,14 @@ class GreetService < Light::Services::Base
|
|
|
45
46
|
end
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
## Advanced Example
|
|
49
|
+
## Advanced Example (with dry-types and conditions)
|
|
49
50
|
|
|
50
51
|
```ruby
|
|
51
52
|
class User::ResetPassword < Light::Services::Base
|
|
52
|
-
# Arguments
|
|
53
|
-
arg :user, type: User, optional: true
|
|
54
|
-
arg :email, type: String, optional: true
|
|
55
|
-
arg :send_email, type:
|
|
53
|
+
# Arguments with dry-types for advanced validation and coercion
|
|
54
|
+
arg :user, type: Types.Instance(User), optional: true
|
|
55
|
+
arg :email, type: Types::Coercible::String, optional: true
|
|
56
|
+
arg :send_email, type: Types::Params::Bool, default: true
|
|
56
57
|
|
|
57
58
|
# Steps
|
|
58
59
|
step :validate
|
|
@@ -61,9 +62,9 @@ class User::ResetPassword < Light::Services::Base
|
|
|
61
62
|
step :save_reset_token
|
|
62
63
|
step :send_reset_email, if: :send_email?
|
|
63
64
|
|
|
64
|
-
# Outputs
|
|
65
|
-
output :user, type: User
|
|
66
|
-
output :reset_token, type: String
|
|
65
|
+
# Outputs with dry-types
|
|
66
|
+
output :user, type: Types.Instance(User)
|
|
67
|
+
output :reset_token, type: Types::Strict::String
|
|
67
68
|
|
|
68
69
|
private
|
|
69
70
|
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Summary
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
2
4
|
|
|
3
5
|
* [Light Services](README.md)
|
|
4
6
|
* [Quickstart](quickstart.md)
|
|
5
7
|
* [Concepts](concepts.md)
|
|
8
|
+
|
|
9
|
+
## Deep Dive
|
|
10
|
+
|
|
6
11
|
* [Arguments](arguments.md)
|
|
7
12
|
* [Steps](steps.md)
|
|
8
13
|
* [Outputs](outputs.md)
|
|
@@ -12,6 +17,11 @@
|
|
|
12
17
|
* [Configuration](configuration.md)
|
|
13
18
|
* [Testing](testing.md)
|
|
14
19
|
* [Rails Generators](generators.md)
|
|
20
|
+
* [RuboCop Integration](rubocop.md)
|
|
21
|
+
* [Ruby LSP Integration](ruby-lsp.md)
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
15
25
|
* [Best Practices](best-practices.md)
|
|
16
26
|
* [Recipes](recipes.md)
|
|
17
27
|
* [CRUD](crud.md)
|
data/docs/arguments.md
CHANGED
|
@@ -55,6 +55,29 @@ class HappyBirthdayService < ApplicationService
|
|
|
55
55
|
end
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
### Type Enforcement (Enabled by Default)
|
|
59
|
+
|
|
60
|
+
By default, all arguments must have a `type` option. This helps catch type-related bugs early and makes your services self-documenting.
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
class MyService < ApplicationService
|
|
64
|
+
arg :name, type: String # ✓ Valid
|
|
65
|
+
arg :age # ✗ Raises MissingTypeError
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To disable type enforcement for a specific service:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
class LegacyService < ApplicationService
|
|
73
|
+
config require_type: false
|
|
74
|
+
|
|
75
|
+
arg :name # Allowed when require_type is disabled
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
See the [Configuration documentation](configuration.md) for more details.
|
|
80
|
+
|
|
58
81
|
### dry-types Support
|
|
59
82
|
|
|
60
83
|
Light Services supports [dry-types](https://dry-rb.org/gems/dry-types) for advanced type validation and coercion. When using dry-types, values are automatically coerced to the expected type.
|
data/docs/concepts.md
CHANGED
|
@@ -7,39 +7,39 @@ This section covers the core concepts of Light Services: **Arguments**, **Steps*
|
|
|
7
7
|
When you call `MyService.run(args)`, the following happens:
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
|
|
10
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
11
11
|
│ Service.run(args) │
|
|
12
|
-
|
|
12
|
+
├──────────────────────────────────────────────────────────────┤
|
|
13
13
|
│ 1. Load default values for arguments and outputs │
|
|
14
14
|
│ 2. Validate argument types │
|
|
15
15
|
│ 3. Run before_service_run callbacks │
|
|
16
|
-
|
|
16
|
+
├──────────────────────────────────────────────────────────────┤
|
|
17
17
|
│ 4. Begin around_service_run callback │
|
|
18
18
|
│ 5. Begin database transaction (if use_transactions: true) │
|
|
19
|
-
│ ┌─────────────────────────────────────────────────────┐
|
|
20
|
-
│ │ 6. Execute steps in order │
|
|
21
|
-
│ │ - Run before_step_run / around_step_run │
|
|
22
|
-
│ │ - Execute step method │
|
|
23
|
-
│ │ - Run after_step_run / on_step_success │
|
|
24
|
-
│ │ - Skip if condition (if:/unless:) not met │
|
|
25
|
-
│ │ - Stop if errors.break? is true │
|
|
26
|
-
│ │ - Stop if
|
|
27
|
-
│ ├─────────────────────────────────────────────────────┤
|
|
28
|
-
│ │ 7. On error → Rollback transaction │
|
|
29
|
-
│ │ On success → Commit transaction │
|
|
30
|
-
│ └─────────────────────────────────────────────────────┘
|
|
19
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
20
|
+
│ │ 6. Execute steps in order │ │
|
|
21
|
+
│ │ - Run before_step_run / around_step_run │ │
|
|
22
|
+
│ │ - Execute step method │ │
|
|
23
|
+
│ │ - Run after_step_run / on_step_success │ │
|
|
24
|
+
│ │ - Skip if condition (if:/unless:) not met │ │
|
|
25
|
+
│ │ - Stop if errors.break? is true │ │
|
|
26
|
+
│ │ - Stop if stop! was called │ │
|
|
27
|
+
│ ├─────────────────────────────────────────────────────┤ │
|
|
28
|
+
│ │ 7. On error → Rollback transaction │ │
|
|
29
|
+
│ │ On success → Commit transaction │ │
|
|
30
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
31
31
|
│ 8. End around_service_run callback │
|
|
32
|
-
|
|
33
|
-
│ 9. Run steps marked with always: true (unless
|
|
32
|
+
├──────────────────────────────────────────────────────────────┤
|
|
33
|
+
│ 9. Run steps marked with always: true (unless stop! called) │
|
|
34
34
|
│ 10. Validate output types (if success) │
|
|
35
35
|
│ 11. Copy errors/warnings to parent service (if in context) │
|
|
36
36
|
│ 12. Run after_service_run callback │
|
|
37
37
|
│ 13. Run on_service_success or on_service_failure callback │
|
|
38
|
-
|
|
38
|
+
├──────────────────────────────────────────────────────────────┤
|
|
39
39
|
│ 14. Return service instance │
|
|
40
40
|
│ - service.success? / service.failed? │
|
|
41
41
|
│ - service.outputs / service.errors │
|
|
42
|
-
|
|
42
|
+
└──────────────────────────────────────────────────────────────┘
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Arguments
|
data/docs/configuration.md
CHANGED
|
@@ -8,6 +8,9 @@ Configure Light Services globally using an initializer. For Rails applications,
|
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
Light::Services.configure do |config|
|
|
11
|
+
# Type enforcement
|
|
12
|
+
config.require_type = true # Require type option for all arguments and outputs
|
|
13
|
+
|
|
11
14
|
# Transaction settings
|
|
12
15
|
config.use_transactions = true # Wrap each service in a database transaction
|
|
13
16
|
|
|
@@ -29,6 +32,7 @@ end
|
|
|
29
32
|
|
|
30
33
|
| Option | Default | Description |
|
|
31
34
|
|--------|---------|-------------|
|
|
35
|
+
| `require_type` | `true` | Raises `Light::Services::MissingTypeError` when defining arguments or outputs without a `type` option |
|
|
32
36
|
| `use_transactions` | `true` | Wraps service execution in `ActiveRecord::Base.transaction` |
|
|
33
37
|
| `load_errors` | `true` | Propagates errors to parent service when using `.with(self)` |
|
|
34
38
|
| `break_on_error` | `true` | Stops executing remaining steps when an error is added |
|
|
@@ -138,6 +142,38 @@ class BackgroundTaskService < ApplicationService
|
|
|
138
142
|
end
|
|
139
143
|
```
|
|
140
144
|
|
|
145
|
+
### Type Enforcement (Enabled by Default)
|
|
146
|
+
|
|
147
|
+
By default, all arguments and outputs must have a `type` option. This helps catch type-related bugs early and makes your services self-documenting.
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
class User::Create < ApplicationService
|
|
151
|
+
arg :name, type: String # ✓ Valid
|
|
152
|
+
arg :email # ✗ Raises MissingTypeError
|
|
153
|
+
output :user, type: User # ✓ Valid
|
|
154
|
+
output :token # ✗ Raises MissingTypeError
|
|
155
|
+
end
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
To disable type enforcement globally (not recommended):
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
Light::Services.configure do |config|
|
|
162
|
+
config.require_type = false
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Or disable for specific services:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
class LegacyService < ApplicationService
|
|
170
|
+
config require_type: false
|
|
171
|
+
|
|
172
|
+
arg :data # Allowed when require_type is disabled
|
|
173
|
+
output :result # Allowed when require_type is disabled
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
141
177
|
## Disabling Transactions
|
|
142
178
|
|
|
143
179
|
If you're not using ActiveRecord or want to manage transactions yourself:
|
data/docs/errors.md
CHANGED
|
@@ -201,10 +201,40 @@ Light Services defines several exception classes for different error scenarios:
|
|
|
201
201
|
| Exception | Description |
|
|
202
202
|
|-----------|-------------|
|
|
203
203
|
| `Light::Services::Error` | Base exception class for all Light Services errors |
|
|
204
|
-
| `Light::Services::ArgTypeError` | Raised when an argument type validation fails |
|
|
204
|
+
| `Light::Services::ArgTypeError` | Raised when an argument or output type validation fails |
|
|
205
205
|
| `Light::Services::ReservedNameError` | Raised when using a reserved name for arguments, outputs, or steps |
|
|
206
206
|
| `Light::Services::InvalidNameError` | Raised when using an invalid name format |
|
|
207
207
|
| `Light::Services::NoStepsError` | Raised when a service has no steps defined and no `run` method |
|
|
208
|
+
| `Light::Services::MissingTypeError` | Raised when defining an argument or output without a `type` option when `require_type` is enabled |
|
|
209
|
+
|
|
210
|
+
### MissingTypeError
|
|
211
|
+
|
|
212
|
+
This exception is raised when you define an argument or output without a `type` option. Since `require_type` is enabled by default, all arguments and outputs must have a type.
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
class MyService < ApplicationService
|
|
216
|
+
arg :name # => raises Light::Services::MissingTypeError
|
|
217
|
+
end
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
To fix this, add a `type` option to all arguments and outputs:
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
class MyService < ApplicationService
|
|
224
|
+
arg :name, type: String
|
|
225
|
+
output :result, type: Hash
|
|
226
|
+
end
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
If you need to disable type enforcement for legacy services, you can use the `config` method:
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
class LegacyService < ApplicationService
|
|
233
|
+
config require_type: false
|
|
234
|
+
|
|
235
|
+
arg :data # Allowed when require_type is disabled
|
|
236
|
+
end
|
|
237
|
+
```
|
|
208
238
|
|
|
209
239
|
### NoStepsError
|
|
210
240
|
|
data/docs/outputs.md
CHANGED
|
@@ -78,6 +78,29 @@ class AI::Chat < ApplicationService
|
|
|
78
78
|
end
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
### Type Enforcement (Enabled by Default)
|
|
82
|
+
|
|
83
|
+
By default, all outputs must have a `type` option. This helps catch type-related bugs early and makes your services self-documenting.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
class MyService < ApplicationService
|
|
87
|
+
output :result, type: Hash # ✓ Valid
|
|
88
|
+
output :data # ✗ Raises MissingTypeError
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
To disable type enforcement for a specific service:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
class LegacyService < ApplicationService
|
|
96
|
+
config require_type: false
|
|
97
|
+
|
|
98
|
+
output :data # Allowed when require_type is disabled
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See the [Configuration documentation](configuration.md) for more details.
|
|
103
|
+
|
|
81
104
|
### dry-types Support
|
|
82
105
|
|
|
83
106
|
Outputs also support [dry-types](https://dry-rb.org/gems/dry-types) for advanced type validation and coercion.
|