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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 22770c4c6eeee16f8c3705709e653810d114a94f798c9408f1e5d6cb31d69af2
4
- data.tar.gz: 683201c0c625ebfa39b4cfeccbdb24d47fe4e246ea781fe32dd2ffc1983941b3
3
+ metadata.gz: d272960b5f0002c3b640bba8eeca1684477df3c65a6c7031f3169abcd97eb3f2
4
+ data.tar.gz: 13f2d1b53652113892ad2d5e93a34aefee19ed38901034e33ac9764590756289
5
5
  SHA512:
6
- metadata.gz: 8f33f7bd519557a9c3171b15312d4e902931c6ba8451cea28c6104b2ee490b0d44270d1e3678b06effd9e7dd9a75afd7738f853a11ef36df1ca4cd8692b23c33
7
- data.tar.gz: 537103640214323d3b77a835e74f59a626486b152d185586a534e9300d290e95ce28fbb49dfa9a3d82f76144feebe2cfd45a396997e863aa8ca78ab8918208b1
6
+ metadata.gz: 483f3c71553b51875b1e10d45c113f42ef3d93102658e7df5499bb912fffb810b9555fade2506eaf6e0c1bc4573b06f8b08b179ee1f2d6a336899f8e519d5aad
7
+ data.tar.gz: c0b8e60f94ad3dbeda5d3e10377633fb6dc248543176fda3d5e4353034e76c8b48804c855d7f9c0d778fe0a7180fa313ae313624fc8a26c1cc4257529cf67ce9
data/.rubocop.yml CHANGED
@@ -5,6 +5,7 @@ AllCops:
5
5
  TargetRubyVersion: 3.1
6
6
  Exclude:
7
7
  - "bin/*"
8
+ - 'vendor/bundle/**/*'
8
9
 
9
10
  Style/LambdaCall:
10
11
  Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- light_service-validated_context (0.1.1)
4
+ light_service-validated_context (0.2.2)
5
5
  dry-types (~> 1.7.0)
6
6
  light-service (>= 0.18.0)
7
7
  zeitwerk
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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ValidatedContext
4
- module ContextOverrides
4
+ module Context
5
5
  def define_accessor_methods_for_keys(keys)
6
6
  super keys.map(&:to_sym)
7
7
  end
@@ -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.label
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 are_all_keys_valid?(validated_keys)
5
+ def type_check_and_coerce_keys!(keys)
6
6
  errors = []
7
7
 
8
- validated_keys.each do |key|
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
- [errors.none?, errors]
17
+ # debugger
18
+ @validation_errors = errors
19
19
  end
20
20
 
21
- def are_all_keys_in_context?(keys)
22
- keys_are_all_valid, validation_errors = are_all_keys_valid?(raw_keys)
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
- LightService::Configuration.logger.error msg
36
- raise error_to_throw, msg
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
- action.promised_keys.map do |key|
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.label
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
@@ -2,10 +2,6 @@
2
2
 
3
3
  module ValidatedContext
4
4
  ValidatedKey = Struct.new(:label, :type) do
5
- def self.call(*args)
6
- new(*args)
7
- end
8
-
9
5
  def to_sym
10
6
  label.to_sym
11
7
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module LightService
4
4
  module ValidatedContext
5
- VERSION = "0.1.1"
5
+ VERSION = "0.2.2"
6
6
  end
7
7
  end
@@ -12,15 +12,17 @@ module ValidatedContext; end
12
12
  module LightService
13
13
  class Context
14
14
  ValidatedKey = ::ValidatedContext::ValidatedKey
15
- prepend ::ValidatedContext::ContextOverrides
15
+ prepend ::ValidatedContext::Context
16
16
 
17
- class ExpectedKeyVerifier
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.1.1
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-29 00:00:00.000000000 Z
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/context_overrides.rb
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