light-services 3.0.0 → 3.1.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -1
  3. data/CHANGELOG.md +15 -0
  4. data/CLAUDE.md +1 -1
  5. data/Gemfile.lock +1 -1
  6. data/README.md +11 -11
  7. data/docs/arguments.md +23 -0
  8. data/docs/concepts.md +2 -2
  9. data/docs/configuration.md +36 -0
  10. data/docs/errors.md +31 -1
  11. data/docs/outputs.md +23 -0
  12. data/docs/quickstart.md +1 -1
  13. data/docs/readme.md +12 -11
  14. data/docs/rubocop.md +285 -0
  15. data/docs/ruby-lsp.md +133 -0
  16. data/docs/steps.md +62 -8
  17. data/docs/summary.md +2 -0
  18. data/docs/testing.md +1 -1
  19. data/lib/light/services/base.rb +109 -7
  20. data/lib/light/services/base_with_context.rb +23 -1
  21. data/lib/light/services/callbacks.rb +59 -5
  22. data/lib/light/services/collection.rb +50 -2
  23. data/lib/light/services/concerns/execution.rb +3 -0
  24. data/lib/light/services/config.rb +83 -3
  25. data/lib/light/services/constants.rb +3 -0
  26. data/lib/light/services/dsl/arguments_dsl.rb +1 -0
  27. data/lib/light/services/dsl/outputs_dsl.rb +1 -0
  28. data/lib/light/services/dsl/validation.rb +30 -0
  29. data/lib/light/services/exceptions.rb +19 -1
  30. data/lib/light/services/message.rb +28 -3
  31. data/lib/light/services/messages.rb +74 -2
  32. data/lib/light/services/rubocop/cop/light_services/argument_type_required.rb +52 -0
  33. data/lib/light/services/rubocop/cop/light_services/condition_method_exists.rb +173 -0
  34. data/lib/light/services/rubocop/cop/light_services/deprecated_methods.rb +113 -0
  35. data/lib/light/services/rubocop/cop/light_services/dsl_order.rb +176 -0
  36. data/lib/light/services/rubocop/cop/light_services/missing_private_keyword.rb +102 -0
  37. data/lib/light/services/rubocop/cop/light_services/no_direct_instantiation.rb +66 -0
  38. data/lib/light/services/rubocop/cop/light_services/output_type_required.rb +52 -0
  39. data/lib/light/services/rubocop/cop/light_services/step_method_exists.rb +109 -0
  40. data/lib/light/services/rubocop.rb +12 -0
  41. data/lib/light/services/settings/field.rb +33 -5
  42. data/lib/light/services/settings/step.rb +23 -5
  43. data/lib/light/services/version.rb +1 -1
  44. data/lib/ruby_lsp/light_services/addon.rb +36 -0
  45. data/lib/ruby_lsp/light_services/definition.rb +132 -0
  46. data/lib/ruby_lsp/light_services/indexing_enhancement.rb +263 -0
  47. metadata +15 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a45a0a0ba2e870c7188f55865004d697646da536e3cecb05b9b0eb30fda9651d
4
- data.tar.gz: 4ad5df86f1a1ff129ac85f3f2ed1ecda11466d52c8639fbbfda410951e8dc8c5
3
+ metadata.gz: 14b80318915db3dc20bd40365dc9ad00160938bb6836c1cda14bfba5301c7d7a
4
+ data.tar.gz: 80515890786ed055972e5b52bbc6cdee8e79b85d4fa1227b217f4b746c9fb899
5
5
  SHA512:
6
- metadata.gz: 75bbc704971352293401dfcfaf0191aceb3d19ebfd150ce4db1aac0140b4044ee65942ff205484379a7169342298a7164fb76f1fbbf1907d3bb33d99485d1b03
7
- data.tar.gz: 231a847015fcabfa0373693c0f13ac31d2ea2d636aae355b5ec023838efd3a15ae0b4baba0d7604f3c8980272419078268d155cd9d5b4ca7ca9e22f976667c62
6
+ metadata.gz: 454b6f2cd8fdf8615bc29c6b69e16fe704a3b19abd8f7e48fbd1e848505038db013e41396399dd5caa721d7179ae1bc3a6a99989a3adfc0f6868e9fe49a63c85
7
+ data.tar.gz: 50dbf071f72a242c0c36daeb83db94db5826dca4e3e2bbe7a47f9c81006b4d2c2266731715e1948b6528df86633735839f6eee525abde302a76b7a046e484b72
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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.0 (2025-12-13)
4
+
5
+ ### Breaking changes
6
+
7
+ - Enforce arguments and output types by default. Add `config.require_type = false` to your config to disable this behavior.
8
+
9
+ ### Added
10
+
11
+ - `stop!` and `stopped?` methods for early exit (renamed from `done!` and `done?`)
12
+ - `stop_immediately!` method for immediate execution halt within the current step
13
+ - `done!` and `done?` are deprecated, but remain available as aliases for backward compatibility
14
+ - Ruby LSP support with step navigation and indexing
15
+ - Rubocop cops `StepMethodExists`, `ConditionMethodExists`, `DslOrder`, `MissingPrivateKeyword`, `NoDirectInstantiation`, `ArgumentTypeRequired`, `OutputTypeRequired`, `DeprecatedMethods`
16
+ - Comprehensive YARD documentation
17
+
3
18
  ## 3.0.0 (2025-12-12)
4
19
 
5
20
  ### Breaking changes
data/CLAUDE.md CHANGED
@@ -93,7 +93,7 @@ class ExampleService < Light::Services::Base
93
93
  end
94
94
 
95
95
  def cleanup
96
- # Runs regardless of errors/warnings, unless done! was called
96
+ # Runs regardless of errors/warnings, unless stop! was called
97
97
  end
98
98
 
99
99
  def should_process?
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- light-services (3.0.0)
4
+ light-services (3.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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: [TrueClass, FalseClass], default: true
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
 
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
@@ -23,14 +23,14 @@ When you call `MyService.run(args)`, the following happens:
23
23
  │ │ - Run after_step_run / on_step_success │ │
24
24
  │ │ - Skip if condition (if:/unless:) not met │ │
25
25
  │ │ - Stop if errors.break? is true │ │
26
- │ │ - Stop if done! was called │ │
26
+ │ │ - Stop if stop! was called │ │
27
27
  │ ├─────────────────────────────────────────────────────┤ │
28
28
  │ │ 7. On error → Rollback transaction │ │
29
29
  │ │ On success → Commit transaction │ │
30
30
  │ └─────────────────────────────────────────────────────┘ │
31
31
  │ 8. End around_service_run callback │
32
32
  ├─────────────────────────────────────────────────────────────┤
33
- │ 9. Run steps marked with always: true (unless done! called) │
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 │
@@ -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.
data/docs/quickstart.md CHANGED
@@ -74,7 +74,7 @@ class GreetService < ApplicationService
74
74
  step :greet
75
75
 
76
76
  # Outputs
77
- output :greeted, default: false
77
+ output :greeted, type: [TrueClass, FalseClass], default: false
78
78
 
79
79
  private
80
80
 
data/docs/readme.md CHANGED
@@ -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: [TrueClass, FalseClass], default: true
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
 
data/docs/rubocop.md ADDED
@@ -0,0 +1,285 @@
1
+ # RuboCop Integration
2
+
3
+ Light Services provides custom RuboCop cops to help enforce best practices in your service definitions.
4
+
5
+ ## Setup
6
+
7
+ Add this to your `.rubocop.yml`:
8
+
9
+ ```yaml
10
+ require:
11
+ - light/services/rubocop
12
+ ```
13
+
14
+ ## Available Cops
15
+
16
+ ### LightServices/ArgumentTypeRequired
17
+
18
+ Ensures all `arg` declarations include a `type:` option.
19
+
20
+ ```ruby
21
+ # bad
22
+ arg :user_id
23
+ arg :params, default: {}
24
+
25
+ # good
26
+ arg :user_id, type: Integer
27
+ arg :params, type: Hash, default: {}
28
+ ```
29
+
30
+ ### LightServices/OutputTypeRequired
31
+
32
+ Ensures all `output` declarations include a `type:` option.
33
+
34
+ ```ruby
35
+ # bad
36
+ output :result
37
+ output :data, optional: true
38
+
39
+ # good
40
+ output :result, type: Hash
41
+ output :data, type: Hash, optional: true
42
+ ```
43
+
44
+ ### LightServices/StepMethodExists
45
+
46
+ Ensures all `step` declarations have a corresponding method defined.
47
+
48
+ ```ruby
49
+ # bad
50
+ class MyService < ApplicationService
51
+ step :validate
52
+ step :process # missing method
53
+
54
+ private
55
+
56
+ def validate; end
57
+ end
58
+
59
+ # good
60
+ class MyService < ApplicationService
61
+ step :validate
62
+ step :process
63
+
64
+ private
65
+
66
+ def validate; end
67
+ def process; end
68
+ end
69
+ ```
70
+
71
+ **Configuration:** Use `ExcludedSteps` for inherited steps:
72
+
73
+ ```yaml
74
+ LightServices/StepMethodExists:
75
+ ExcludedSteps:
76
+ - initialize_entity
77
+ - assign_attributes
78
+ ```
79
+
80
+ ### LightServices/ConditionMethodExists
81
+
82
+ Ensures symbol conditions (`:if`, `:unless`) have corresponding methods defined.
83
+
84
+ This cop automatically recognizes predicate methods generated by `arg` and `output` declarations (e.g., `arg :user` creates `user?`).
85
+
86
+ ```ruby
87
+ # bad
88
+ class MyService < ApplicationService
89
+ step :notify, if: :should_notify? # missing method
90
+
91
+ private
92
+
93
+ def notify; end
94
+ end
95
+
96
+ # good - explicit method
97
+ class MyService < ApplicationService
98
+ step :notify, if: :should_notify?
99
+
100
+ private
101
+
102
+ def notify; end
103
+ def should_notify?; true; end
104
+ end
105
+
106
+ # good - predicate from arg/output
107
+ class MyService < ApplicationService
108
+ arg :user, type: User, optional: true
109
+
110
+ step :greet, if: :user? # user? is auto-generated
111
+
112
+ private
113
+
114
+ def greet; end
115
+ end
116
+ ```
117
+
118
+ **Configuration:** Use `ExcludedMethods` for inherited condition methods:
119
+
120
+ ```yaml
121
+ LightServices/ConditionMethodExists:
122
+ ExcludedMethods:
123
+ - admin?
124
+ - guest?
125
+ ```
126
+
127
+ ### LightServices/DslOrder
128
+
129
+ Enforces consistent ordering of DSL declarations: `config` → `arg` → `step` → `output`
130
+
131
+ ```ruby
132
+ # bad
133
+ class MyService < ApplicationService
134
+ step :process
135
+ arg :name, type: String
136
+ config raise_on_error: true
137
+ end
138
+
139
+ # good
140
+ class MyService < ApplicationService
141
+ config raise_on_error: true
142
+
143
+ arg :name, type: String
144
+
145
+ step :process
146
+
147
+ output :result, type: Hash
148
+ end
149
+ ```
150
+
151
+ ### LightServices/MissingPrivateKeyword
152
+
153
+ Ensures step methods are defined as private.
154
+
155
+ ```ruby
156
+ # bad
157
+ class MyService < ApplicationService
158
+ step :process
159
+
160
+ def process # should be private
161
+ # implementation
162
+ end
163
+ end
164
+
165
+ # good
166
+ class MyService < ApplicationService
167
+ step :process
168
+
169
+ private
170
+
171
+ def process
172
+ # implementation
173
+ end
174
+ end
175
+ ```
176
+
177
+ ### LightServices/NoDirectInstantiation
178
+
179
+ Prevents direct instantiation of service classes with `.new`.
180
+
181
+ ```ruby
182
+ # bad
183
+ UserService.new(name: "John")
184
+
185
+ # good
186
+ UserService.run(name: "John")
187
+ UserService.run!(name: "John")
188
+ UserService.call(name: "John")
189
+ ```
190
+
191
+ **Configuration:** Customize the pattern for service class detection:
192
+
193
+ ```yaml
194
+ LightServices/NoDirectInstantiation:
195
+ ServicePattern: 'Service$' # default: matches classes ending with "Service"
196
+ ```
197
+
198
+ ### LightServices/DeprecatedMethods
199
+
200
+ Detects deprecated `done!` and `done?` method calls and suggests using `stop!` and `stopped?` instead. Includes autocorrection.
201
+
202
+ ```ruby
203
+ # bad
204
+ class MyService < ApplicationService
205
+ step :process
206
+
207
+ private
208
+
209
+ def process
210
+ done! if condition_met?
211
+ return if done?
212
+ end
213
+ end
214
+
215
+ # good
216
+ class MyService < ApplicationService
217
+ step :process
218
+
219
+ private
220
+
221
+ def process
222
+ stop! if condition_met?
223
+ return if stopped?
224
+ end
225
+ end
226
+ ```
227
+
228
+ **Configuration:** Customize the pattern for service class detection:
229
+
230
+ ```yaml
231
+ LightServices/DeprecatedMethods:
232
+ ServicePattern: 'Service$' # default: matches classes ending with "Service"
233
+ ```
234
+
235
+ ## Configuration
236
+
237
+ Full configuration example:
238
+
239
+ ```yaml
240
+ require:
241
+ - light/services/rubocop
242
+
243
+ LightServices/ArgumentTypeRequired:
244
+ Enabled: true
245
+
246
+ LightServices/OutputTypeRequired:
247
+ Enabled: true
248
+
249
+ LightServices/StepMethodExists:
250
+ Enabled: true
251
+ ExcludedSteps: []
252
+
253
+ LightServices/ConditionMethodExists:
254
+ Enabled: true
255
+ ExcludedMethods: []
256
+
257
+ LightServices/DslOrder:
258
+ Enabled: true
259
+
260
+ LightServices/MissingPrivateKeyword:
261
+ Enabled: true
262
+
263
+ LightServices/NoDirectInstantiation:
264
+ Enabled: true
265
+ ServicePattern: 'Service$'
266
+
267
+ LightServices/DeprecatedMethods:
268
+ Enabled: true
269
+ ServicePattern: 'Service$'
270
+ ```
271
+
272
+ To disable a cop for specific files:
273
+
274
+ ```yaml
275
+ LightServices/ArgumentTypeRequired:
276
+ Exclude:
277
+ - 'spec/**/*'
278
+ - 'test/**/*'
279
+ ```
280
+
281
+ ## What's Next?
282
+
283
+ Learn more about testing your services:
284
+
285
+ [Next: Testing](testing.md)