light_service-validated_context 0.1.1 → 0.2.2
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 +4 -4
- data/.rubocop.yml +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +76 -4
- data/lib/light_service/validated_context/{context_overrides.rb → context.rb} +1 -1
- data/lib/light_service/validated_context/expected_key_verifier.rb +15 -1
- data/lib/light_service/validated_context/key_verifier.rb +10 -20
- data/lib/light_service/validated_context/promised_key_verifier.rb +16 -2
- data/lib/light_service/validated_context/validated_key.rb +0 -4
- data/lib/light_service/validated_context/version.rb +1 -1
- data/lib/light_service/validated_context.rb +5 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d272960b5f0002c3b640bba8eeca1684477df3c65a6c7031f3169abcd97eb3f2
|
4
|
+
data.tar.gz: 13f2d1b53652113892ad2d5e93a34aefee19ed38901034e33ac9764590756289
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 483f3c71553b51875b1e10d45c113f42ef3d93102658e7df5499bb912fffb810b9555fade2506eaf6e0c1bc4573b06f8b08b179ee1f2d6a336899f8e519d5aad
|
7
|
+
data.tar.gz: c0b8e60f94ad3dbeda5d3e10377633fb6dc248543176fda3d5e4353034e76c8b48804c855d7f9c0d778fe0a7180fa313ae313624fc8a26c1cc4257529cf67ce9
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# LightService - ValidatedContext
|
2
2
|
|
3
|
+
[](https://github.com/pioneerskies/light_service-validated_context/actions/workflows/main.yml)
|
4
|
+
|
3
5
|
This gem _patches_ `light-service` gem implementing validated keys
|
4
6
|
for `expects` and `promises` action's macros.
|
5
7
|
|
@@ -37,16 +39,22 @@ And then execute:
|
|
37
39
|
|
38
40
|
$ bundle install
|
39
41
|
|
42
|
+
Would you need to manualy require the gem, here's the syntax:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require 'light_service/validated_context'
|
46
|
+
```
|
47
|
+
|
40
48
|
## Usage
|
41
49
|
|
42
50
|
```ruby
|
43
51
|
class ActionOne
|
44
52
|
extend LightService::Action
|
45
53
|
|
46
|
-
expects VK.(:email, Types::Strict::String)
|
47
|
-
expects VK.(:age, Types::Coercible::Integer.constrained(gt: 30))
|
48
|
-
expects VK.(:ary, Types::Array.of(Types::Strict::Symbol).constrained(min_size: 1))
|
49
|
-
promises VK.(:text, Types::Strict::String.constrained(max_size: 10).default('foobar'))
|
54
|
+
expects VK.new(:email, Types::Strict::String)
|
55
|
+
expects VK.new(:age, Types::Coercible::Integer.constrained(gt: 30))
|
56
|
+
expects VK.new(:ary, Types::Array.of(Types::Strict::Symbol).constrained(min_size: 1))
|
57
|
+
promises VK.new(:text, Types::Strict::String.constrained(max_size: 10).default('foobar'))
|
50
58
|
|
51
59
|
executed do |context|
|
52
60
|
# something happens
|
@@ -75,6 +83,70 @@ form to avoid name collisions.
|
|
75
83
|
|
76
84
|
You can find more usage example in `spec/support/test_doubles.rb`
|
77
85
|
|
86
|
+
## Why validation matters?
|
87
|
+
|
88
|
+
In OO programming there's a rule (strict or "of thumb", IDK) that says to "never" instantiate an
|
89
|
+
invalid object whenever the object self has the concept of _validity_ for itself. This rule takes
|
90
|
+
sense to my eyes whenever I'm working with an object already initialized and in memory, but I cannot
|
91
|
+
trust its internal status.
|
92
|
+
|
93
|
+
Taken that `light-service` doesn't work on instances, but it works on classes and class methods
|
94
|
+
having a more functional and stateless approach, side effects of having invalid state in the context
|
95
|
+
(which is The state of an Action/Organizer) are mostly the same.
|
96
|
+
|
97
|
+
Rewording: if I cannot trust the state, given
|
98
|
+
the state is internal or delegated to a context object, I'll have to to a bunch of validation-oriented
|
99
|
+
logical branches into my logic. E.g.:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class HugAFriend
|
103
|
+
extend LightService::Action
|
104
|
+
|
105
|
+
expects :friend
|
106
|
+
|
107
|
+
executed do |context|
|
108
|
+
context.friend.hug if context.friend.respond_to?(:hug)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
The `if` in this uber-trivial example exists just due to lack of trust on the state.
|
114
|
+
|
115
|
+
Let's re-imagine the code given an `executed` block that totally trusts the context:
|
116
|
+
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
class HugAFriend
|
120
|
+
extend LightService::Action
|
121
|
+
|
122
|
+
expects VK.new(:friend, Types.Instance(Friend))
|
123
|
+
# Or a less usual approach could be to trust duck typing
|
124
|
+
# expects VK.new(:friend, Types::Interface(:hug))
|
125
|
+
|
126
|
+
executed do |context|
|
127
|
+
context.friend.hug
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
## Comparison with similar gems
|
133
|
+
|
134
|
+
This is a comparison table I've done using my own limited experience w/ other solutions
|
135
|
+
and/or reading projects' READMEs. Don't take my word for it. And if I was wrong understanding
|
136
|
+
some features, feel free to drop me a line on Mastodon [@alessandrofazzi@mastodon.uno](https://mastodon.uno/@alessandrofazzi)
|
137
|
+
|
138
|
+
| Feature | adomokos/light-service | sunny/actor | collectiveidea/interactor | AaronLasseigne/active_interaction | pioneerskies/light_service-validated_context/ |
|
139
|
+
| ------------------------- | ---------------------- | ---------------------------------------------------- | ------------------------- | --------------------------------- | --------------------------------------------- |
|
140
|
+
| presence | ✅ | ✅ | ❌ | ⚠️ Only input, not output | ✅ |
|
141
|
+
| static default | ✅ | ✅ | ❌ | ✅ | ✅ |
|
142
|
+
| dynamic default | ✅ | ✅ | ❌ | ✅ | ✅ |
|
143
|
+
| raise or fail control | ❌ | ✅ | ❌ | ❓ | ❌ |
|
144
|
+
| type check | ❌ | ✅ | ❌ | ✅ | ✅ |
|
145
|
+
| data structure type check | ❌ | ❌ | ❌ | ❌ | ✅ |
|
146
|
+
| optional | ⚠️ through `default` | ✅ through `allow_nil` (which defaults to `true` 🤔 ❓) | ❌ | ⚠️ through `default` | ✅ |
|
147
|
+
| built-in | ✅ | ✅ | ❌ | ❌ ActiveModel::Validation | ❌ Dry::Types |
|
148
|
+
|
149
|
+
|
78
150
|
## Development
|
79
151
|
|
80
152
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -3,15 +3,29 @@
|
|
3
3
|
module ValidatedContext
|
4
4
|
module ExpectedKeyVerifier
|
5
5
|
def keys
|
6
|
+
keys_as_symbols
|
7
|
+
end
|
8
|
+
|
9
|
+
def keys_as_symbols
|
6
10
|
action.expected_keys.map do |key|
|
7
11
|
next key unless key.is_a?(LightService::Context::ValidatedKey)
|
8
12
|
|
9
|
-
key.
|
13
|
+
key.to_sym
|
10
14
|
end
|
11
15
|
end
|
12
16
|
|
13
17
|
def raw_keys
|
14
18
|
action.expected_keys
|
15
19
|
end
|
20
|
+
|
21
|
+
def throw_error_predicate(keys)
|
22
|
+
type_check_and_coerce_keys!(raw_keys)
|
23
|
+
|
24
|
+
keys_are_all_present = are_all_keys_in_context?(keys)
|
25
|
+
|
26
|
+
return false if are_all_keys_valid? && keys_are_all_present
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
16
30
|
end
|
17
31
|
end
|
@@ -2,38 +2,28 @@
|
|
2
2
|
|
3
3
|
module ValidatedContext
|
4
4
|
module KeyVerifier
|
5
|
-
def
|
5
|
+
def type_check_and_coerce_keys!(keys)
|
6
6
|
errors = []
|
7
7
|
|
8
|
-
|
8
|
+
keys.each do |key|
|
9
9
|
next unless key.is_a?(LightService::Context::ValidatedKey)
|
10
10
|
|
11
11
|
begin
|
12
12
|
context[key.label] = key.type[context[key.label] || Dry::Types::Undefined]
|
13
13
|
rescue Dry::Types::CoercionError => e
|
14
|
-
errors << e.message
|
14
|
+
errors << "[#{action}][:#{key.label}] #{e.message}"
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
18
|
-
|
17
|
+
# debugger
|
18
|
+
@validation_errors = errors
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
not_found_keys = keys_not_found(keys)
|
25
|
-
keys_are_all_present = not_found_keys.none?
|
26
|
-
|
27
|
-
return true if keys_are_all_valid && keys_are_all_present
|
28
|
-
|
29
|
-
msg = if !keys_are_all_present
|
30
|
-
error_message
|
31
|
-
elsif !keys_are_all_valid
|
32
|
-
validation_errors.join(', ')
|
33
|
-
end
|
21
|
+
def are_all_keys_valid?
|
22
|
+
@validation_errors.none?
|
23
|
+
end
|
34
24
|
|
35
|
-
|
36
|
-
|
25
|
+
def error_message
|
26
|
+
@validation_errors.join(', ')
|
37
27
|
end
|
38
28
|
end
|
39
29
|
end
|
@@ -3,15 +3,29 @@
|
|
3
3
|
module ValidatedContext
|
4
4
|
module PromisedKeyVerifier
|
5
5
|
def keys
|
6
|
-
|
6
|
+
keys_as_symbols
|
7
|
+
end
|
8
|
+
|
9
|
+
def keys_as_symbols
|
10
|
+
raw_keys.map do |key|
|
7
11
|
next key unless key.is_a?(LightService::Context::ValidatedKey)
|
8
12
|
|
9
|
-
key.
|
13
|
+
key.to_sym
|
10
14
|
end
|
11
15
|
end
|
12
16
|
|
13
17
|
def raw_keys
|
14
18
|
action.promised_keys
|
15
19
|
end
|
20
|
+
|
21
|
+
def throw_error_predicate(keys)
|
22
|
+
type_check_and_coerce_keys!(raw_keys)
|
23
|
+
|
24
|
+
keys_are_all_present = are_all_keys_in_context?(keys)
|
25
|
+
|
26
|
+
return false if are_all_keys_valid? && keys_are_all_present
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
16
30
|
end
|
17
31
|
end
|
@@ -12,15 +12,17 @@ module ValidatedContext; end
|
|
12
12
|
module LightService
|
13
13
|
class Context
|
14
14
|
ValidatedKey = ::ValidatedContext::ValidatedKey
|
15
|
-
prepend ::ValidatedContext::
|
15
|
+
prepend ::ValidatedContext::Context
|
16
16
|
|
17
|
-
class
|
17
|
+
class KeyVerifier
|
18
18
|
prepend ::ValidatedContext::KeyVerifier
|
19
|
+
end
|
20
|
+
|
21
|
+
class ExpectedKeyVerifier
|
19
22
|
prepend ::ValidatedContext::ExpectedKeyVerifier
|
20
23
|
end
|
21
24
|
|
22
25
|
class PromisedKeyVerifier
|
23
|
-
prepend ::ValidatedContext::KeyVerifier
|
24
26
|
prepend ::ValidatedContext::PromisedKeyVerifier
|
25
27
|
end
|
26
28
|
end
|
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.
|
4
|
+
version: 0.2.2
|
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-11-
|
11
|
+
date: 2022-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-types
|
@@ -67,7 +67,7 @@ files:
|
|
67
67
|
- README.md
|
68
68
|
- Rakefile
|
69
69
|
- lib/light_service/validated_context.rb
|
70
|
-
- lib/light_service/validated_context/
|
70
|
+
- lib/light_service/validated_context/context.rb
|
71
71
|
- lib/light_service/validated_context/expected_key_verifier.rb
|
72
72
|
- lib/light_service/validated_context/key_verifier.rb
|
73
73
|
- lib/light_service/validated_context/promised_key_verifier.rb
|