interactor-validation 0.4.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34fec19e843f97e1e192485a44c70b70f52533dafe4431f7bd4e0fe9ccd22dce
4
- data.tar.gz: be4ffec4a5ec8d3b9f65d9f5be3303a5bb9e3b1bb7f13bff790575152d1d3a81
3
+ metadata.gz: a84c031fc1b57751f8632449ffdc9014c2dfa7caff24bafd5bf418882adf1550
4
+ data.tar.gz: a3315d47373378b4152095b7e358305330c4aa67af41f3f97e943092b0f5ed23
5
5
  SHA512:
6
- metadata.gz: 107fc2505862ab2c62be405fd38f8a115c9b775a32d15052bd27a3c47eede633d36a3cf1fa2b0c383057d9bbc9b960900670e254dab41cdaa0abf42dceaf7a19
7
- data.tar.gz: 2b60b8af76fa09de78733ecd26a57bf112a94d5e5d0e7605edf3a4234f366e9b85aafd0720130b39a8acdc195e6c865fb08c181da53dd4e65285964b217fa73b
6
+ metadata.gz: 39c3fc71fc57f9b56c342417fba7b362f095516e9e624a810f6474b6f4006173de76c6336be8dbaaf067b6d30bfac916c5628312b8f74bbbe5536e01837c4b28
7
+ data.tar.gz: 68e581fd48735e33046850215381a91687d040b6d792d03e4d0972772023fa099978441908b6f460367cc909d5175bb60992753c635cc001199072a5468cb8a3
data/README.md CHANGED
@@ -7,7 +7,7 @@ Structured, lightweight parameter validation designed specifically for [Interact
7
7
  - **Built for Interactor** - Seamless integration with service objects
8
8
  - **Comprehensive validators** - Presence, format, length, inclusion, numericality, boolean
9
9
  - **Nested validation** - Validate complex hashes and arrays
10
- - **Custom validations** - Override `validate!` for business logic
10
+ - **Custom validations** - `validate!` for other business logic
11
11
  - **Flexible error formats** - Human-readable messages or machine-readable codes
12
12
  - **Zero dependencies** - Just Interactor and Ruby stdlib
13
13
  - **Configurable** - Control validation behavior and error handling
@@ -30,6 +30,9 @@ Structured, lightweight parameter validation designed specifically for [Interact
30
30
  - [Parameter Delegation](#parameter-delegation)
31
31
  - [Requirements](#requirements)
32
32
  - [Design Philosophy](#design-philosophy)
33
+ - [Development](#development)
34
+ - [Contributing](#contributing)
35
+ - [License](#license)
33
36
 
34
37
  ## Installation
35
38
 
@@ -239,7 +242,7 @@ result.errors
239
242
 
240
243
  ## Custom Validations
241
244
 
242
- Override `validate!` for custom business logic:
245
+ Override `validate!` for custom business logic that requires external dependencies (database queries, API calls, etc.):
243
246
 
244
247
  ```ruby
245
248
  class CreateOrder
@@ -253,7 +256,8 @@ class CreateOrder
253
256
  validates :user_id, presence: true
254
257
 
255
258
  def validate!
256
- super # Run parameter validations first
259
+ # Parameter validations have already run at this point
260
+ # No need to call super - there is no parent validate! method
257
261
 
258
262
  product = Product.find_by(id: product_id)
259
263
  if product.nil?
@@ -269,9 +273,41 @@ class CreateOrder
269
273
  end
270
274
  ```
271
275
 
276
+ **Important:** Parameter validations (defined via `validates`) run automatically before `validate!`. You should never call `super` in your `validate!` method as there is no parent implementation.
277
+
272
278
  ## Configuration
273
279
 
274
- Configure global validation behavior in an initializer or before your interactors are loaded:
280
+ Configuration can be set at three levels (in order of precedence):
281
+
282
+ ### 1. Per-Interactor Configuration
283
+
284
+ Configure individual interactors using either a `configure` block or dedicated methods:
285
+
286
+ ```ruby
287
+ class CreateUser
288
+ include Interactor
289
+ include Interactor::Validation
290
+
291
+ # Option 1: Using configure block
292
+ configure do |config|
293
+ config.halt = true
294
+ config.mode = :code
295
+ end
296
+
297
+ # Option 2: Using dedicated methods
298
+ validation_halt true
299
+ validation_mode :code
300
+ validation_skip_validate false
301
+
302
+ # ... validations and call method
303
+ end
304
+ ```
305
+
306
+ Configuration is inherited from parent classes and can be overridden in child classes.
307
+
308
+ ### 2. Global Configuration
309
+
310
+ Configure global defaults in an initializer or before your interactors are loaded:
275
311
 
276
312
  ```ruby
277
313
  Interactor::Validation.configure do |config|
@@ -427,10 +463,46 @@ While ActiveModel::Validations is powerful, it's designed for ActiveRecord model
427
463
 
428
464
  ## Development
429
465
 
466
+ ### Setup
467
+
430
468
  ```bash
431
469
  bundle install
432
- bundle exec rspec # Run tests
433
- bundle exec rubocop # Lint code
470
+ ```
471
+
472
+ ### Running Tests
473
+
474
+ ```bash
475
+ bundle exec rspec # Run all tests
476
+ bundle exec rspec spec/interactor/validation_spec.rb # Run specific test file
477
+ bundle exec rspec spec/interactor/validation_spec.rb:42 # Run specific test at line 42
478
+ ```
479
+
480
+ ### Linting
481
+
482
+ ```bash
483
+ bundle exec rubocop # Check code style
484
+ bundle exec rubocop -a # Auto-fix safe issues
485
+ bundle exec rubocop -A # Auto-fix all issues (use with caution)
486
+ ```
487
+
488
+ ### Combined (Default Rake Task)
489
+
490
+ ```bash
491
+ bundle exec rake # Runs both rspec and rubocop
492
+ ```
493
+
494
+ ### Interactive Console
495
+
496
+ ```bash
497
+ bundle exec irb -r ./lib/interactor/validation # Load gem in IRB
498
+ ```
499
+
500
+ ### Gem Management
501
+
502
+ ```bash
503
+ bundle exec rake build # Build gem file
504
+ bundle exec rake install # Install gem locally
505
+ bundle exec rake release # Release gem (requires permissions)
434
506
  ```
435
507
 
436
508
  ## Contributing
@@ -11,6 +11,9 @@ require_relative "validators/array"
11
11
 
12
12
  module Interactor
13
13
  module Validation
14
+ # Exception raised when validation should halt on first error
15
+ class HaltValidation < StandardError; end
16
+
14
17
  module Validates
15
18
  def self.included(base)
16
19
  base.extend(ClassMethods)
@@ -21,18 +24,73 @@ module Interactor
21
24
  base.prepend(InstanceMethods)
22
25
  end
23
26
 
27
+ class ConfigurationProxy
28
+ def initialize(config_hash)
29
+ @config = config_hash
30
+ end
31
+
32
+ def mode=(value)
33
+ @config[:mode] = value
34
+ end
35
+
36
+ def halt=(value)
37
+ @config[:halt] = value
38
+ end
39
+
40
+ def skip_validate=(value)
41
+ @config[:skip_validate] = value
42
+ end
43
+ end
44
+
24
45
  module ClassMethods
46
+ def inherited(subclass)
47
+ super
48
+ # Ensure child class gets its own copy of config, merging with parent's config
49
+ subclass._validation_config = _validation_config.dup
50
+ # Ensure child class gets its own copy of validations
51
+ subclass._validations = _validations.dup
52
+ end
53
+
25
54
  def validates(param_name, **rules, &)
55
+ # Ensure we have our own copy of validations when first modifying
56
+ begin
57
+ self._validations = _validations.dup if _validations.equal?(superclass._validations)
58
+ rescue StandardError
59
+ false
60
+ end
26
61
  _validations[param_name] ||= {}
27
62
  _validations[param_name].merge!(rules)
28
63
  _validations[param_name][:_nested] = build_nested_rules(&) if block_given?
29
64
  end
30
65
 
66
+ def configure
67
+ # Ensure we have our own copy of config before modifying
68
+ begin
69
+ self._validation_config = _validation_config.dup if _validation_config.equal?(superclass._validation_config)
70
+ rescue StandardError
71
+ false
72
+ end
73
+ config = ConfigurationProxy.new(_validation_config)
74
+ yield(config)
75
+ end
76
+
31
77
  def validation_halt(value)
78
+ # Ensure we have our own copy of config before modifying
79
+ begin
80
+ self._validation_config = _validation_config.dup if _validation_config.equal?(superclass._validation_config)
81
+ rescue StandardError
82
+ false
83
+ end
32
84
  _validation_config[:halt] = value
33
85
  end
34
86
 
35
87
  def validation_mode(value)
88
+ # Ensure we have our own copy of config before modifying
89
+ begin
90
+ self._validation_config = _validation_config.dup if _validation_config.equal?(superclass._validation_config)
91
+ rescue StandardError
92
+ false
93
+ end
36
94
  _validation_config[:mode] = value
37
95
  end
38
96
 
@@ -57,19 +115,8 @@ module Interactor
57
115
  end
58
116
  end
59
117
 
60
- # Base module with default validate! that does nothing
61
- module BaseValidation
62
- def validate!
63
- # Default implementation - does nothing
64
- # Subclasses can override and call super
65
- end
66
- end
67
-
68
118
  module InstanceMethods
69
119
  def self.prepended(base)
70
- # Include BaseValidation so super works in user's validate!
71
- base.include(BaseValidation) unless base.ancestors.include?(BaseValidation)
72
-
73
120
  # Include all validator modules
74
121
  base.include(Validators::Presence)
75
122
  base.include(Validators::Numeric)
@@ -82,31 +129,28 @@ module Interactor
82
129
  end
83
130
 
84
131
  def errors
85
- @errors ||= Errors.new
132
+ @errors ||= Errors.new(halt_checker: -> { validation_config(:halt) })
86
133
  end
87
134
 
88
- def validate!
89
- errors.clear
135
+ def run_validations!
90
136
  param_errors = false
91
137
 
92
- # Run parameter validations
93
- if self.class._validations
94
- self.class._validations.each do |param, rules|
95
- value = context.respond_to?(param) ? context.public_send(param) : nil
96
- validate_param(param, value, rules)
97
-
98
- # Halt on first error if configured
99
- if validation_config(:halt) && errors.any?
100
- context.fail!(errors: format_errors)
101
- break
138
+ begin
139
+ # Run parameter validations
140
+ if self.class._validations
141
+ self.class._validations.each do |param, rules|
142
+ value = context.respond_to?(param) ? context.public_send(param) : nil
143
+ validate_param(param, value, rules)
102
144
  end
145
+ param_errors = errors.any?
103
146
  end
104
- param_errors = errors.any?
105
- end
106
147
 
107
- # Call super to allow user-defined validate! to run
108
- # Skip if param validations failed and skip_validate is true
109
- super unless param_errors && validation_config(:skip_validate)
148
+ # Run custom validations if defined
149
+ # Skip if param validations failed and skip_validate is true
150
+ validate! if respond_to?(:validate!, true) && !(param_errors && validation_config(:skip_validate))
151
+ rescue HaltValidation
152
+ # Validation halted on first error - fall through to fail context
153
+ end
110
154
 
111
155
  # Fail context if any errors exist
112
156
  context.fail!(errors: format_errors) if errors.any?
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Interactor
4
4
  module Validation
5
- VERSION = "0.4.0"
5
+ VERSION = "0.4.1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: interactor-validation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wilson Anciro