light_service-validated_context 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c16c17a54c9867f59ccbf92ec6d969760eb396719ecda969e12d2fec2b063623
4
- data.tar.gz: 62103f8748ab7efc04cad3253d5dc5aabd153ae62e4d022ca0427365dc2f657f
3
+ metadata.gz: 859cf8786c692bedc1d764b0656629bc7f9e0ddb741a10874d290c723b708e3d
4
+ data.tar.gz: 18059f28fcbaaa2d54373bdf2cea6c6f8387fc68b07f83f338631e445ba29e5f
5
5
  SHA512:
6
- metadata.gz: 79c63b97672442aabfdc1215f1ca1e5c92293508da7ab3cea9e23e6453a1b9017f240501af69aa7423780f4b9b77b3ae5eecb53aa4b7d80dbaecb0285b94271e
7
- data.tar.gz: 003447c4164440525f526cca2859f3151ebaeb7cc7ba4e32d3ef1f2022b3aa9418ab699400975d4bbe659444678c6df1226716959c00d6c2ccad2a944e547d13
6
+ metadata.gz: 7d7a6568947199bf4e96296d03825a65b7485227083d02b72b450598db273e72bae5547b19dd9d32d5ca9a85e9b5e676cc5af4f257f974ec4d4d9e59a8c3ed7f
7
+ data.tar.gz: eb44c15324105179fb1426fc201d2f34d8bb92e5ccc5e1ed2a265e8c8d85eaf66625b287717c6ec452a8789afa240b69b395fa3050c0a1661ed2026a0fb6e5d0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- light_service-validated_context (0.3.0)
4
+ light_service-validated_context (0.3.1)
5
5
  dry-types (~> 1.7.0)
6
6
  light-service (>= 0.18.0)
7
7
  zeitwerk
data/README.md CHANGED
@@ -23,7 +23,7 @@ AFAIK this is the only way to achieve the goal. Because of this fact I consider
23
23
  ## Goals
24
24
 
25
25
  - implement an advanced and flexible interface to declare,
26
- type-check, coerce and describe action's arguments without reinventing the wheel (the wheel we use under the wood is [`dry-types`](https://dry-rb.org/gems/dry-types))
26
+ type-check, coerce and describe action's arguments without reinventing the wheel (the wheel we use under the wood is [`dry-types`](https://dry-rb.org/gems/dry-types/main/custom-types/))
27
27
  - testing DX and interfaces
28
28
  - study what parts of code are involved into this area of `light-service`'s code base
29
29
 
@@ -101,7 +101,7 @@ ActionOne.execute(age: 37, text: 'Too long too pass the constrain')
101
101
  ```
102
102
 
103
103
  Since all the validation and coercion logic is delegated to `dry-types`, you can
104
- read more about what you can achieve at https://dry-rb.org/gems/dry-types/1.2/
104
+ read more about what you can achieve at https://dry-rb.org/gems/dry-types/main/custom-types/
105
105
 
106
106
  `VK` objects needs to be created with 2 positional arguments:
107
107
 
@@ -116,13 +116,14 @@ You can find more usage example in `spec/support/test_doubles.rb`
116
116
 
117
117
  ### Custom validation error message
118
118
 
119
- You can set a custom validation error message when instantiating a `VK`
119
+ You can set a custom validation error message when instantiating a `VK` object
120
120
 
121
121
  ```ruby
122
- VK.new(:my_integer, Types::Strict::Integer, message: 'Custom validatio message for :my_integer key')
122
+ VK.new(:my_integer, Types::Strict::Integer, message: 'Custom validation message for :my_integer key')
123
123
  ```
124
124
 
125
- Messages translated via `I18n` are supported too, following stardard `light-service`'s configuration
125
+ Messages translated via `I18n` are supported too, following standard `light-service`'s
126
+ [configuration](https://github.com/adomokos/light-service/#localizing-messages)
126
127
 
127
128
  ```ruby
128
129
  VK.new(:my_integer, Types::Strict::Integer, message: :my_integer_error_message)
@@ -130,10 +131,15 @@ VK.new(:my_integer, Types::Strict::Integer, message: :my_integer_error_message)
130
131
 
131
132
  ### Raise vs fail
132
133
 
133
- By default, following original `light-service` implementation, a validation error will raise
134
- and error.
134
+ By default, following original `light-service` implementation, a validation error will raise a
135
+ `LightService::ExpectedKeysNotInContextError` or `LightService::PromisedKeysNotInContextError`.
135
136
 
136
- May you prefere to fail the action populating outcome's message with error message:
137
+ > NOTE: I know that raised exceptions do not express the concept of "invalid", but I opted
138
+ to preserve the original one in order to make this plugin more droppable-in as possible, thus
139
+ w/o breaking code relying on, for example, rescueing those specific excpetions.
140
+
141
+ May you prefere to fail the action, populating outcome's message with error message, just do
142
+ `extend LightService::Context::FailOnValidationError` into you action:
137
143
 
138
144
  ```ruby
139
145
  class ActionFailInsteadOfRaise
@@ -151,13 +157,45 @@ result = ActionFailInsteadOfRaise.execute(foo: 12)
151
157
  result.message # Here you'll find the validation(s) message(s)
152
158
  ```
153
159
 
154
- ## Why validation matters?
160
+ ### Custom types
161
+
162
+ As documented in [dry-types doc](https://dry-rb.org/gems/dry-types/main/getting-started/#creating-your-first-type),
163
+ you can be more expressive defining custom types; you can define them reopening the already defined `LightService::Types` module
164
+ (or simply `Types` in the global namespace if it does not conflict with your domain's namespace), e.g.:
165
+
166
+ ```ruby
167
+ module LightService::Types
168
+ MyExpressiveThing = Hash.schema(
169
+ name: String,
170
+ age: Coercible::Integer,
171
+ foo: Symbol.constrained(included_in: %i[bar baz])
172
+ )
173
+ end
174
+
175
+ class ActionOne
176
+ extend LightService::Action
177
+ extend LightService::Context::FailOnValidationError
155
178
 
156
- In OO programming there's a rule (strict or "of thumb", IDK) that says to never instantiate an
157
- invalid object - given the object itself has the concept of _validity_.
179
+ expects VK.new(:foo, Types::MyBusinessHash)
180
+
181
+ executed do |context|
182
+ # do something...
183
+ end
184
+ end
185
+
186
+ result = App::ActionOne.execute(foo: {
187
+ name: 'Alessandro',
188
+ age: '37',
189
+ foo: :bar
190
+ })
191
+ ```
192
+
193
+ Custom types will be reusable, more expressive and moreover will clean your action up a bit.
194
+
195
+ ## Why validation matters?
158
196
 
159
- How many times do you find yourself working with an object already initialized and in memory, but
160
- you cannot trust its internal status?
197
+ In OO programming there's a rule that says to never instantiate an
198
+ invalid object.
161
199
 
162
200
  If you cannot trust the state, given the state is internal or delegated to a context object,
163
201
  you'll have to do a bunch of validation-oriented logical branches into your logic. E.g.:
@@ -211,8 +249,11 @@ some features, feel free to drop me a line on Mastodon [@alessandrofazzi@mastodo
211
249
  | type check | ❌ | ✅ | ❌ | ✅ | ✅ |
212
250
  | data structure type check | ❌ | ❌ | ❌ | ❌ | ✅ |
213
251
  | optional | ⚠️ through `default` | ✅ through `allow_nil` (which defaults to `true` 🤔 ❓) | ❌ | ⚠️ through `default` | ✅ |
214
- | 1st party code | ✅ | ✅ | ✅ | ActiveModel::Validation | ❌ Dry::Types |
252
+ | 1st party code | ✅ | ✅ | ✅ | ⚠️ ActiveModel::Validation | ❌ Dry::Types |
215
253
 
254
+ > NOTE: in `active_interaction` the fact that validation code isn't first party isn't an issue, since
255
+ > the gem is a Rails-only gem and validation is delegated to Rails, thus no additional dependencies
256
+ > are required. `light_service-validated_context` depends on additional gems from the dry-rb ecosystem
216
257
 
217
258
  ## Development
218
259
 
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ValidatedContext
4
- module Context
5
- def define_accessor_methods_for_keys(keys)
6
- super keys.map(&:to_sym)
3
+ module LightService
4
+ module ValidatedContext
5
+ module Context
6
+ def define_accessor_methods_for_keys(keys)
7
+ super keys.map(&:to_sym)
8
+ end
7
9
  end
8
10
  end
9
11
  end
@@ -1,29 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ValidatedContext
4
- module ExpectedKeyVerifier
5
- def keys
6
- keys_as_symbols
7
- end
3
+ module LightService
4
+ module ValidatedContext
5
+ module ExpectedKeyVerifier
6
+ def keys
7
+ keys_as_symbols
8
+ end
8
9
 
9
- def keys_as_symbols
10
- action.expected_keys.map do |key|
11
- next key unless key.is_a?(LightService::Context::ValidatedKey)
10
+ def keys_as_symbols
11
+ action.expected_keys.map do |key|
12
+ next key unless key.is_a?(LightService::Context::ValidatedKey)
12
13
 
13
- key.to_sym
14
+ key.to_sym
15
+ end
14
16
  end
15
- end
16
17
 
17
- def raw_keys
18
- action.expected_keys
19
- end
18
+ def raw_keys
19
+ action.expected_keys
20
+ end
20
21
 
21
- def throw_error_predicate(_keys)
22
- type_check_and_coerce_keys!(raw_keys)
22
+ def throw_error_predicate(_keys)
23
+ type_check_and_coerce_keys!(raw_keys)
23
24
 
24
- return false if are_all_keys_valid?
25
+ return false if are_all_keys_valid?
25
26
 
26
- should_throw_on_validation_error?
27
+ should_throw_on_validation_error?
28
+ end
27
29
  end
28
30
  end
29
31
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ValidatedContext
4
- module FailOnValidationError
5
- def fail_on_validation_error? = true
6
- def throw_on_validation_error? = !fail_on_validation_error?
3
+ module LightService
4
+ module ValidatedContext
5
+ module FailOnValidationError
6
+ def fail_on_validation_error? = true
7
+ def throw_on_validation_error? = !fail_on_validation_error?
8
+ end
7
9
  end
8
10
  end
@@ -1,48 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ValidatedContext
4
- module KeyVerifier
5
- def initialize(context, action)
6
- @validation_errors = []
3
+ module LightService
4
+ module ValidatedContext
5
+ module KeyVerifier
6
+ def initialize(context, action)
7
+ @validation_errors = []
7
8
 
8
- super(context, action)
9
- end
9
+ super(context, action)
10
+ end
10
11
 
11
- # rubocop:disable Metrics/AbcSize
12
- # Refactoring this is out of my scope ATM
13
- def type_check_and_coerce_keys!(keys)
14
- errors = []
15
-
16
- keys.each do |key|
17
- next unless key.is_a?(LightService::Context::ValidatedKey)
18
-
19
- begin
20
- context[key.label] = key.type[context[key.label] || Dry::Types::Undefined]
21
- rescue Dry::Types::CoercionError => e
22
- errors << (
23
- LightService::Configuration.localization_adapter.failure(key.message, action) ||
24
- "[#{action}][:#{key.label}] #{e.message}"
25
- )
12
+ # rubocop:disable Metrics/AbcSize
13
+ # Refactoring this is out of my scope ATM
14
+ def type_check_and_coerce_keys!(keys)
15
+ errors = []
16
+
17
+ keys.each do |key|
18
+ next unless key.is_a?(LightService::Context::ValidatedKey)
19
+
20
+ begin
21
+ context[key.label] = key.type[context[key.label] || Dry::Types::Undefined]
22
+ rescue Dry::Types::CoercionError => e
23
+ errors << (
24
+ LightService::Configuration.localization_adapter.failure(key.message, action) ||
25
+ "[#{action}][:#{key.label}] #{e.message}"
26
+ )
27
+ end
26
28
  end
27
- end
28
29
 
29
- @validation_errors = errors
30
- end
31
- # rubocop:enable Metrics/AbcSize
30
+ @validation_errors = errors
31
+ end
32
+ # rubocop:enable Metrics/AbcSize
32
33
 
33
- def are_all_keys_valid?
34
- @validation_errors.none?
35
- end
34
+ def are_all_keys_valid?
35
+ @validation_errors.none?
36
+ end
36
37
 
37
- def error_message
38
- @validation_errors.join(', ')
39
- end
38
+ def error_message
39
+ @validation_errors.join(', ')
40
+ end
40
41
 
41
- def should_throw_on_validation_error?
42
- return true unless action.respond_to?(:fail_on_validation_error?) && action.fail_on_validation_error?
42
+ def should_throw_on_validation_error?
43
+ return true unless action.respond_to?(:fail_on_validation_error?) && action.fail_on_validation_error?
43
44
 
44
- context.fail!(error_message)
45
- false
45
+ context.fail!(error_message)
46
+ false
47
+ end
46
48
  end
47
49
  end
48
50
  end
@@ -1,29 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ValidatedContext
4
- module PromisedKeyVerifier
5
- def keys
6
- keys_as_symbols
7
- end
3
+ module LightService
4
+ module ValidatedContext
5
+ module PromisedKeyVerifier
6
+ def keys
7
+ keys_as_symbols
8
+ end
8
9
 
9
- def keys_as_symbols
10
- raw_keys.map do |key|
11
- next key unless key.is_a?(LightService::Context::ValidatedKey)
10
+ def keys_as_symbols
11
+ raw_keys.map do |key|
12
+ next key unless key.is_a?(LightService::Context::ValidatedKey)
12
13
 
13
- key.to_sym
14
+ key.to_sym
15
+ end
14
16
  end
15
- end
16
17
 
17
- def raw_keys
18
- action.promised_keys
19
- end
18
+ def raw_keys
19
+ action.promised_keys
20
+ end
20
21
 
21
- def throw_error_predicate(_keys)
22
- type_check_and_coerce_keys!(raw_keys)
22
+ def throw_error_predicate(_keys)
23
+ type_check_and_coerce_keys!(raw_keys)
23
24
 
24
- return false if are_all_keys_valid?
25
+ return false if are_all_keys_valid?
25
26
 
26
- should_throw_on_validation_error?
27
+ should_throw_on_validation_error?
28
+ end
27
29
  end
28
30
  end
29
31
  end
@@ -1,21 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ValidatedContext
4
- class ValidatedKey
5
- attr_reader :label, :type, :message
3
+ module LightService
4
+ module ValidatedContext
5
+ class ValidatedKey
6
+ attr_reader :label, :type, :message
6
7
 
7
- def initialize(label, type, message: nil)
8
- @label = label
9
- @type = type
10
- @message = message
11
- end
8
+ def initialize(label, type, message: nil)
9
+ @label = label
10
+ @type = type
11
+ @message = message
12
+ end
12
13
 
13
- def to_sym
14
- label.to_sym
15
- end
14
+ def to_sym
15
+ label.to_sym
16
+ end
16
17
 
17
- def to_s
18
- label.to_s
18
+ def to_s
19
+ label.to_s
20
+ end
19
21
  end
20
22
  end
21
23
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module LightService
4
4
  module ValidatedContext
5
- VERSION = "0.3.0"
5
+ VERSION = "0.3.1"
6
6
  end
7
7
  end
@@ -1,30 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "zeitwerk"
4
- loader = Zeitwerk::Loader.for_gem
4
+ loader = Zeitwerk::Loader.new
5
+ loader.tag = 'LightService::I18n'
6
+ loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
7
+ loader.push_dir(__dir__, :namespace => LightService)
5
8
  loader.setup
6
9
 
7
10
  require 'light-service'
8
11
  require 'dry-types'
9
12
 
10
- module ValidatedContext; end
13
+ module LightService
14
+ module ValidatedContext; end
15
+ end
11
16
 
12
17
  module LightService
13
18
  class Context
14
- ValidatedKey = ::ValidatedContext::ValidatedKey
15
- FailOnValidationError = ::ValidatedContext::FailOnValidationError
16
- prepend ::ValidatedContext::Context
19
+ ValidatedKey = LightService::ValidatedContext::ValidatedKey
20
+ FailOnValidationError = LightService::ValidatedContext::FailOnValidationError
21
+ prepend LightService::ValidatedContext::Context
17
22
 
18
23
  class KeyVerifier
19
- prepend ::ValidatedContext::KeyVerifier
24
+ prepend LightService::ValidatedContext::KeyVerifier
20
25
  end
21
26
 
22
27
  class ExpectedKeyVerifier
23
- prepend ::ValidatedContext::ExpectedKeyVerifier
28
+ prepend LightService::ValidatedContext::ExpectedKeyVerifier
24
29
  end
25
30
 
26
31
  class PromisedKeyVerifier
27
- prepend ::ValidatedContext::PromisedKeyVerifier
32
+ prepend LightService::ValidatedContext::PromisedKeyVerifier
28
33
  end
29
34
  end
30
35
 
@@ -33,7 +38,7 @@ module LightService
33
38
  end
34
39
  end
35
40
 
36
- # Convenience namespace for implementor
41
+ # Convenience alias for implementor
37
42
  Types = LightService::Types unless Module.const_defined?('Types')
38
43
 
39
44
  # Convenience alias for implementor
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light_service-validated_context
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - "'Alessandro Fazzi'"
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-03 00:00:00.000000000 Z
11
+ date: 2022-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-types