light_service-validated_context 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +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 +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42b7fa071364c73605b8bd5fd1d374ea6942e575d386a714c477d67061ba8aeb
|
4
|
+
data.tar.gz: 7a9ee001a427055c2c30f90f982c98eadb0d8e3ee4209afd2acfa0e59c9da415
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78965b6a9605abf0e44e755be975c3277fdbc823df4d8296ab3850d38a2f62af053316dfdc86038b54540ce1348e68602541867fe6a268c500597be055b3d3a6
|
7
|
+
data.tar.gz: 43b9c95eb665b7667043ca18a36ad852cddfe3f5b6164176ac30c31f1f8697e390cdd242af069f6e82778e9eecfcb770c436ce650b9a4c2a0d42b518e5613ccf
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# LightService - ValidatedContext
|
2
2
|
|
3
|
+
[![Ruby](https://github.com/pioneerskies/light_service-validated_context/actions/workflows/main.yml/badge.svg?branch=main)](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.0
|
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
|