light_service-validated_context 0.3.0 → 0.3.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: 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