light-services 3.1.1 → 3.2.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.
@@ -8,8 +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
11
+ # Type enforcement
12
+ config.require_arg_type = true # Require type option for all arguments
13
+ config.require_output_type = true # Require type option for all outputs
13
14
 
14
15
  # Transaction settings
15
16
  config.use_transactions = true # Wrap each service in a database transaction
@@ -32,7 +33,8 @@ end
32
33
 
33
34
  | Option | Default | Description |
34
35
  |--------|---------|-------------|
35
- | `require_type` | `true` | Raises `Light::Services::MissingTypeError` when defining arguments or outputs without a `type` option |
36
+ | `require_arg_type` | `true` | Raises `Light::Services::MissingTypeError` when defining arguments without a `type` option |
37
+ | `require_output_type` | `true` | Raises `Light::Services::MissingTypeError` when defining outputs without a `type` option |
36
38
  | `use_transactions` | `true` | Wraps service execution in `ActiveRecord::Base.transaction` |
37
39
  | `load_errors` | `true` | Propagates errors to parent service when using `.with(self)` |
38
40
  | `break_on_error` | `true` | Stops executing remaining steps when an error is added |
@@ -159,7 +161,8 @@ To disable type enforcement globally (not recommended):
159
161
 
160
162
  ```ruby
161
163
  Light::Services.configure do |config|
162
- config.require_type = false
164
+ config.require_arg_type = false # Disable for arguments
165
+ config.require_output_type = false # Disable for outputs
163
166
  end
164
167
  ```
165
168
 
@@ -167,10 +170,22 @@ Or disable for specific services:
167
170
 
168
171
  ```ruby
169
172
  class LegacyService < ApplicationService
170
- config require_type: false
173
+ config require_arg_type: false, require_output_type: false
171
174
 
172
- arg :data # Allowed when require_type is disabled
173
- output :result # Allowed when require_type is disabled
175
+ arg :data # Allowed when require_arg_type is disabled
176
+ output :result # Allowed when require_output_type is disabled
177
+ end
178
+ ```
179
+
180
+ You can also control them independently:
181
+
182
+ ```ruby
183
+ class StrictInputService < ApplicationService
184
+ # Require types for arguments but not outputs
185
+ config require_arg_type: true, require_output_type: false
186
+
187
+ arg :data, type: Hash # Type required
188
+ output :result # Type not required
174
189
  end
175
190
  ```
176
191
 
data/docs/errors.md CHANGED
@@ -44,6 +44,24 @@ class ParsePage < ApplicationService
44
44
  end
45
45
  ```
46
46
 
47
+ ## Quick Error with `fail!`
48
+
49
+ The `fail!` method is a shortcut for adding an error to the `:base` key:
50
+
51
+ ```ruby
52
+ class ParsePage < ApplicationService
53
+ def validate
54
+ fail!("URL is required") if url.blank?
55
+ end
56
+ end
57
+ ```
58
+
59
+ This is equivalent to:
60
+
61
+ ```ruby
62
+ errors.add(:base, "URL is required")
63
+ ```
64
+
47
65
  ## Reading Errors
48
66
 
49
67
  To check if a service has errors, you can use the `#failed?` method. You can also use methods like `errors.any?` to inspect errors.
@@ -205,11 +223,13 @@ Light Services defines several exception classes for different error scenarios:
205
223
  | `Light::Services::ReservedNameError` | Raised when using a reserved name for arguments, outputs, or steps |
206
224
  | `Light::Services::InvalidNameError` | Raised when using an invalid name format |
207
225
  | `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 |
226
+ | `Light::Services::MissingTypeError` | Raised when defining an argument or output without a `type` option when `require_arg_type` or `require_output_type` is enabled |
227
+ | `Light::Services::StopExecution` | Control flow exception raised by `stop_immediately!` to halt execution without rollback |
228
+ | `Light::Services::FailExecution` | Control flow exception raised by `fail_immediately!` to halt execution and rollback transactions |
209
229
 
210
230
  ### MissingTypeError
211
231
 
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.
232
+ This exception is raised when you define an argument or output without a `type` option. Since `require_arg_type` and `require_output_type` are enabled by default, all arguments and outputs must have a type.
213
233
 
214
234
  ```ruby
215
235
  class MyService < ApplicationService
@@ -230,9 +250,10 @@ If you need to disable type enforcement for legacy services, you can use the `co
230
250
 
231
251
  ```ruby
232
252
  class LegacyService < ApplicationService
233
- config require_type: false
253
+ config require_arg_type: false, require_output_type: false
234
254
 
235
- arg :data # Allowed when require_type is disabled
255
+ arg :data # Allowed when require_arg_type is disabled
256
+ output :result # Allowed when require_output_type is disabled
236
257
  end
237
258
  ```
238
259
 
data/docs/generators.md CHANGED
@@ -216,7 +216,7 @@ RSpec.describe User::Create do
216
216
  describe ".run" do
217
217
  it "creates a user" do
218
218
  service = described_class.run(...)
219
- expect(service).to be_success
219
+ expect(service).to be_successful
220
220
  end
221
221
  end
222
222
  end
data/docs/outputs.md CHANGED
@@ -89,13 +89,13 @@ class MyService < ApplicationService
89
89
  end
90
90
  ```
91
91
 
92
- To disable type enforcement for a specific service:
92
+ To disable type enforcement for outputs in a specific service:
93
93
 
94
94
  ```ruby
95
95
  class LegacyService < ApplicationService
96
- config require_type: false
96
+ config require_output_type: false
97
97
 
98
- output :data # Allowed when require_type is disabled
98
+ output :data # Allowed when require_output_type is disabled
99
99
  end
100
100
  ```
101
101
 
@@ -252,7 +252,7 @@ RSpec.describe Post::Update do
252
252
  attributes: { title: "New Title" }
253
253
  )
254
254
 
255
- expect(service).to be_success
255
+ expect(service).to be_successful
256
256
  expect(post.reload.title).to eq("New Title")
257
257
  end
258
258
  end
data/docs/rubocop.md CHANGED
@@ -232,6 +232,47 @@ LightServices/DeprecatedMethods:
232
232
  ServicePattern: 'Service$' # default: matches classes ending with "Service"
233
233
  ```
234
234
 
235
+ ### LightServices/PreferFailMethod
236
+
237
+ Detects `errors.add(:base, "message")` calls and suggests using the `fail!("message")` helper instead. Includes autocorrection.
238
+
239
+ ```ruby
240
+ # bad
241
+ class MyService < ApplicationService
242
+ step :process
243
+
244
+ private
245
+
246
+ def process
247
+ errors.add(:base, "user is required")
248
+ errors.add(:base, "invalid input", rollback: false)
249
+ end
250
+ end
251
+
252
+ # good
253
+ class MyService < ApplicationService
254
+ step :process
255
+
256
+ private
257
+
258
+ def process
259
+ fail!("user is required")
260
+ fail!("invalid input", rollback: false)
261
+ end
262
+ end
263
+ ```
264
+
265
+ The cop only detects `errors.add(:base, ...)` calls. It does not flag `errors.add(:field_name, ...)` calls for specific fields, as those should not use `fail!`.
266
+
267
+ **Configuration:** Customize the base service classes to check:
268
+
269
+ ```yaml
270
+ LightServices/PreferFailMethod:
271
+ BaseServiceClasses:
272
+ - ApplicationService
273
+ - BaseCreator
274
+ ```
275
+
235
276
  ## Configuration
236
277
 
237
278
  Full configuration example:
@@ -267,6 +308,11 @@ LightServices/NoDirectInstantiation:
267
308
  LightServices/DeprecatedMethods:
268
309
  Enabled: true
269
310
  ServicePattern: 'Service$'
311
+
312
+ LightServices/PreferFailMethod:
313
+ Enabled: true
314
+ BaseServiceClasses:
315
+ - ApplicationService
270
316
  ```
271
317
 
272
318
  To disable a cop for specific files:
data/docs/steps.md CHANGED
@@ -292,6 +292,58 @@ end
292
292
  **Database Transactions:** Calling `stop_immediately!` does NOT rollback database transactions. All database changes made before `stop_immediately!` was called will be committed.
293
293
  {% endhint %}
294
294
 
295
+ ## Immediate Failure with `fail_immediately!`
296
+
297
+ Use `fail_immediately!` when you need to halt execution immediately AND rollback any database transactions. Unlike `stop_immediately!`, this method adds an error and causes transaction rollback.
298
+
299
+ ```ruby
300
+ class Payment::Process < ApplicationService
301
+ arg :amount, type: Integer
302
+ arg :card_token, type: String
303
+
304
+ step :validate_card
305
+ step :charge_card
306
+ step :send_receipt
307
+
308
+ output :transaction_id, type: String
309
+
310
+ private
311
+
312
+ def validate_card
313
+ unless valid_card?(card_token)
314
+ fail_immediately!("Card validation failed")
315
+ end
316
+
317
+ # This code won't run if card is invalid
318
+ log_validation_success
319
+ end
320
+
321
+ def charge_card
322
+ # This step won't run if fail_immediately! was called
323
+ self.transaction_id = PaymentGateway.charge(amount, card_token)
324
+ end
325
+ end
326
+ ```
327
+
328
+ {% hint style="info" %}
329
+ `fail_immediately!` raises an internal exception to halt execution. Steps marked with `always: true` will still run when `fail_immediately!` is called, allowing for cleanup operations.
330
+ {% endhint %}
331
+
332
+ {% hint style="danger" %}
333
+ **Database Transactions:** Calling `fail_immediately!` DOES rollback database transactions. All database changes made before `fail_immediately!` was called will be rolled back.
334
+ {% endhint %}
335
+
336
+ ### Comparison Table
337
+
338
+ | Method | Adds Error | Stops Execution | Transaction Rollback |
339
+ |--------|------------|-----------------|---------------------|
340
+ | `stop!` | No | After current step | No |
341
+ | `stop_immediately!` | No | Immediately | No |
342
+ | `fail!(msg)` | Yes (:base) | After current step* | No |
343
+ | `fail_immediately!(msg)` | Yes (:base) | Immediately | Yes |
344
+
345
+ *By default, adding an error stops subsequent steps from running due to `break_on_add` configuration.
346
+
295
347
  ## Removing Inherited Steps
296
348
 
297
349
  When inheriting from a parent service, you can remove steps using `remove_step`:
data/docs/testing.md CHANGED
@@ -30,7 +30,7 @@ RSpec.describe GreetService do
30
30
  it "returns a greeting message" do
31
31
  service = described_class.run(name: "John")
32
32
 
33
- expect(service).to be_success
33
+ expect(service).to be_successful
34
34
  expect(service.message).to eq("Hello, John!")
35
35
  end
36
36
  end
@@ -48,7 +48,7 @@ RSpec.describe User::Create do
48
48
  it "creates a user" do
49
49
  service = described_class.run(attributes: attributes)
50
50
 
51
- expect(service).to be_success
51
+ expect(service).to be_successful
52
52
  expect(service.user).to be_persisted
53
53
  expect(service.user.email).to eq("test@example.com")
54
54
  end
@@ -86,7 +86,7 @@ RSpec.describe Comment::Create do
86
86
  text: "Great post!"
87
87
  )
88
88
 
89
- expect(service).to be_success
89
+ expect(service).to be_successful
90
90
  expect(service.comment.user).to eq(current_user)
91
91
  end
92
92
  end
@@ -122,7 +122,7 @@ RSpec.describe Order::Create do
122
122
  items: [{ product_id: product.id, quantity: 2 }]
123
123
  )
124
124
 
125
- expect(service).to be_success
125
+ expect(service).to be_successful
126
126
  expect(service.order.order_items.count).to eq(1)
127
127
  expect(service.order.total).to eq(200)
128
128
  end
@@ -252,7 +252,7 @@ RSpec.describe DataImport do
252
252
  it "completes with warnings for skipped records" do
253
253
  service = described_class.run(data: mixed_valid_invalid_data)
254
254
 
255
- expect(service).to be_success # Warnings don't fail the service
255
+ expect(service).to be_successful # Warnings don't fail the service
256
256
  expect(service.warnings?).to be true
257
257
  expect(service.warnings[:skipped]).to include("Row 3: invalid format")
258
258
  end
@@ -273,7 +273,7 @@ RSpec.describe Payment::Charge do
273
273
  it "processes payment successfully" do
274
274
  service = described_class.run(amount: 1000, card_token: "tok_visa")
275
275
 
276
- expect(service).to be_success
276
+ expect(service).to be_successful
277
277
  expect(service.payment_intent_id).to eq("pi_123")
278
278
  end
279
279
 
@@ -312,7 +312,7 @@ RSpec.describe MyService do
312
312
 
313
313
  it "accepts optional arguments as nil" do
314
314
  service = described_class.run(name: "John", nickname: nil)
315
- expect(service).to be_success
315
+ expect(service).to be_successful
316
316
  end
317
317
  end
318
318
  end
@@ -353,7 +353,7 @@ Create a helper module for common service testing patterns:
353
353
  # spec/support/service_helpers.rb
354
354
  module ServiceHelpers
355
355
  def expect_service_success(service)
356
- expect(service).to be_success, -> { "Expected success but got errors: #{service.errors.to_h}" }
356
+ expect(service).to be_successful, -> { "Expected success but got errors: #{service.errors.to_h}" }
357
357
  end
358
358
 
359
359
  def expect_service_failure(service, key = nil)
@@ -34,7 +34,7 @@ RSpec.describe <%= class_name %>, type: :service do
34
34
  let(:args) { {} }
35
35
 
36
36
  it "succeeds" do
37
- expect(service).to be_success
37
+ expect(service).to be_successful
38
38
  end
39
39
  end
40
40
  end
@@ -89,6 +89,7 @@ module Light
89
89
  def success?
90
90
  !errors?
91
91
  end
92
+ alias successful? success?
92
93
 
93
94
  # Check if the service completed with errors.
94
95
  #
@@ -136,6 +137,25 @@ module Light
136
137
  raise Light::Services::StopExecution
137
138
  end
138
139
 
140
+ # Add an error to the :base key.
141
+ #
142
+ # @param message [String] the error message
143
+ # @return [void]
144
+ def fail!(message)
145
+ errors.add(:base, message)
146
+ end
147
+
148
+ # Add an error and stop execution immediately, causing transaction rollback.
149
+ # Steps marked with `always: true` will still run after this method is called.
150
+ #
151
+ # @param message [String] the error message
152
+ # @raise [FailExecution] always raises to halt execution and rollback
153
+ # @return [void]
154
+ def fail_immediately!(message)
155
+ errors.add(:base, message, rollback: false)
156
+ raise Light::Services::FailExecution
157
+ end
158
+
139
159
  # Execute the service steps.
140
160
  #
141
161
  # @return [void]
@@ -44,6 +44,9 @@ module Light
44
44
  # Gracefully handle stop_immediately! inside transaction to prevent rollback
45
45
  @stopped = true
46
46
  end
47
+ rescue Light::Services::FailExecution
48
+ # FailExecution bubbles out of transaction (causing rollback) but is caught here
49
+ nil
47
50
  end
48
51
 
49
52
  # Run steps with parameter `always` if they weren't launched because of errors/warnings
@@ -10,7 +10,8 @@ module Light
10
10
  #
11
11
  # @example
12
12
  # Light::Services.configure do |config|
13
- # config.require_type = true
13
+ # config.require_arg_type = true
14
+ # config.require_output_type = true
14
15
  # config.use_transactions = false
15
16
  # end
16
17
  def configure
@@ -28,13 +29,16 @@ module Light
28
29
  # Configuration class for Light::Services global settings.
29
30
  #
30
31
  # @example Accessing configuration
31
- # Light::Services.config.require_type # => true
32
+ # Light::Services.config.require_arg_type # => true
32
33
  #
33
34
  # @example Modifying configuration
34
35
  # Light::Services.config.use_transactions = false
35
36
  class Config
36
- # @return [Boolean] whether arguments and outputs must have a type specified
37
- attr_reader :require_type
37
+ # @return [Boolean] whether arguments must have a type specified
38
+ attr_reader :require_arg_type
39
+
40
+ # @return [Boolean] whether outputs must have a type specified
41
+ attr_reader :require_output_type
38
42
 
39
43
  # @return [Boolean] whether to wrap service execution in a database transaction
40
44
  attr_reader :use_transactions
@@ -69,7 +73,8 @@ module Light
69
73
  attr_reader :ruby_lsp_type_mappings
70
74
 
71
75
  DEFAULTS = {
72
- require_type: true,
76
+ require_arg_type: true,
77
+ require_output_type: true,
73
78
  use_transactions: true,
74
79
 
75
80
  load_errors: true,
@@ -92,6 +97,16 @@ module Light
92
97
  end
93
98
  end
94
99
 
100
+ # Convenience setter for backward compatibility.
101
+ # Sets both require_arg_type and require_output_type.
102
+ #
103
+ # @param value [Boolean] whether to require types for arguments and outputs
104
+ # @return [void]
105
+ def require_type=(value)
106
+ self.require_arg_type = value
107
+ self.require_output_type = value
108
+ end
109
+
95
110
  # Initialize configuration with default values.
96
111
  def initialize
97
112
  reset_to_defaults!
@@ -135,26 +135,37 @@ module Light
135
135
  # @param opts [Hash] the options hash to check for type
136
136
  def self.validate_type_required!(name, field_type, service_class, opts)
137
137
  return if opts.key?(:type)
138
- return unless require_type_enabled?(service_class)
138
+ return unless require_type_enabled_for?(field_type, service_class)
139
139
 
140
+ config_name = field_type == :argument ? "require_arg_type" : "require_output_type"
140
141
  raise Light::Services::MissingTypeError,
141
142
  "#{field_type.to_s.capitalize} `#{name}` in #{service_class} must have a type specified " \
142
- "(require_type is enabled)"
143
+ "(#{config_name} is enabled)"
143
144
  end
144
145
 
145
- # Check if require_type is enabled for the service class
146
- def self.require_type_enabled?(service_class)
146
+ # Check if require_type is enabled for the given field type and service class
147
+ #
148
+ # @param field_type [Symbol] the type of field (:argument, :output)
149
+ # @param service_class [Class] the service class to check
150
+ # @return [Boolean] whether type is required for the field type
151
+ def self.require_type_enabled_for?(field_type, service_class)
152
+ config_key = field_type == :argument ? :require_arg_type : :require_output_type
153
+
147
154
  # Check class-level config in the inheritance chain, then fall back to global config
148
155
  klass = service_class
149
156
  while klass.respond_to?(:class_config)
150
157
  class_config = klass.class_config
151
158
 
159
+ # Check specific config first (require_arg_type or require_output_type)
160
+ return class_config[config_key] if class_config&.key?(config_key)
161
+
162
+ # Check convenience config (require_type) for backward compatibility
152
163
  return class_config[:require_type] if class_config&.key?(:require_type)
153
164
 
154
165
  klass = klass.superclass
155
166
  end
156
167
 
157
- Light::Services.config.require_type
168
+ Light::Services.config.public_send(config_key)
158
169
  end
159
170
  end
160
171
  end
@@ -24,6 +24,10 @@ module Light
24
24
  # Not an error - used to halt execution gracefully.
25
25
  class StopExecution < StandardError; end
26
26
 
27
+ # Control flow exception for fail_immediately!
28
+ # Unlike StopExecution, this exception causes transaction rollback.
29
+ class FailExecution < StandardError; end
30
+
27
31
  # @deprecated Use {Error} instead
28
32
  NoStepError = Error
29
33
 
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module LightServices
6
+ # Detects `errors.add(:base, "message")` and suggests using `fail!("message")` instead.
7
+ #
8
+ # This cop checks calls inside service classes that inherit from
9
+ # Light::Services::Base or any configured base service classes.
10
+ #
11
+ # @safety
12
+ # This cop's autocorrection is safe as `fail!` is a wrapper method
13
+ # that calls `errors.add(:base, message)` internally.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # class User::Create < ApplicationService
18
+ # step :process
19
+ #
20
+ # private
21
+ #
22
+ # def process
23
+ # errors.add(:base, "user is required")
24
+ # end
25
+ # end
26
+ #
27
+ # # good
28
+ # class User::Create < ApplicationService
29
+ # step :process
30
+ #
31
+ # private
32
+ #
33
+ # def process
34
+ # fail!("user is required")
35
+ # end
36
+ # end
37
+ #
38
+ class PreferFailMethod < Base
39
+ extend AutoCorrector
40
+
41
+ MSG = "Use `fail!(...)` instead of `errors.add(:base, ...)`."
42
+
43
+ def_node_matcher :errors_add_base?, <<~PATTERN
44
+ (send
45
+ (send nil? :errors) :add
46
+ (sym :base)
47
+ ...)
48
+ PATTERN
49
+
50
+ DEFAULT_BASE_CLASSES = ["ApplicationService"].freeze
51
+
52
+ def on_class(node)
53
+ @in_service_class = service_class?(node)
54
+ end
55
+
56
+ def after_class(_node)
57
+ @in_service_class = false
58
+ end
59
+
60
+ def on_send(node)
61
+ return unless @in_service_class
62
+ return unless node.method_name == :add
63
+ return unless node.receiver&.method_name == :errors
64
+
65
+ return unless errors_add_base?(node)
66
+
67
+ # Only flag if there's a message argument after :base
68
+ # errors.add(:base) without a message is invalid Light Services syntax
69
+ message_args = node.arguments[1..]
70
+ return if message_args.empty?
71
+
72
+ add_offense(node, message: MSG) do |corrector|
73
+ autocorrect(corrector, node, message_args)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def autocorrect(corrector, node, message_args)
80
+ # Get the source code for all arguments after the :base symbol
81
+ args_source = message_args.map(&:source).join(", ")
82
+ replacement = "fail!(#{args_source})"
83
+
84
+ corrector.replace(node, replacement)
85
+ end
86
+
87
+ def service_class?(node)
88
+ return false unless node.parent_class
89
+
90
+ parent_class_name = extract_class_name(node.parent_class)
91
+ return false unless parent_class_name
92
+
93
+ # Check for direct Light::Services::Base inheritance
94
+ return true if parent_class_name == "Light::Services::Base"
95
+
96
+ # Check against configured base service classes
97
+ base_classes = cop_config.fetch("BaseServiceClasses", DEFAULT_BASE_CLASSES)
98
+ base_classes.include?(parent_class_name)
99
+ end
100
+
101
+ def extract_class_name(node)
102
+ case node.type
103
+ when :const
104
+ node.const_name
105
+ when :send
106
+ # For namespaced constants like Light::Services::Base
107
+ node.source
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -9,4 +9,5 @@ require_relative "rubocop/cop/light_services/dsl_order"
9
9
  require_relative "rubocop/cop/light_services/missing_private_keyword"
10
10
  require_relative "rubocop/cop/light_services/no_direct_instantiation"
11
11
  require_relative "rubocop/cop/light_services/output_type_required"
12
+ require_relative "rubocop/cop/light_services/prefer_fail_method"
12
13
  require_relative "rubocop/cop/light_services/step_method_exists"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Light
4
4
  module Services
5
- VERSION = "3.1.1"
5
+ VERSION = "3.2.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light-services
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kodkod
@@ -17,6 +17,8 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".cursor/rules/services-rspec/RULE.md"
21
+ - ".cursor/rules/services/RULE.md"
20
22
  - ".github/config/rubocop_linter_action.yml"
21
23
  - ".github/dependabot.yml"
22
24
  - ".github/workflows/ci.yml"
@@ -96,6 +98,7 @@ files:
96
98
  - lib/light/services/rubocop/cop/light_services/missing_private_keyword.rb
97
99
  - lib/light/services/rubocop/cop/light_services/no_direct_instantiation.rb
98
100
  - lib/light/services/rubocop/cop/light_services/output_type_required.rb
101
+ - lib/light/services/rubocop/cop/light_services/prefer_fail_method.rb
99
102
  - lib/light/services/rubocop/cop/light_services/step_method_exists.rb
100
103
  - lib/light/services/settings/field.rb
101
104
  - lib/light/services/settings/step.rb