light_service-validated_context 0.1.0 → 0.2.0

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: b2320137a6c0f04088e028ad9978b2d98f8a8fcd371c503f264ea7cf740741fb
4
- data.tar.gz: bc4d7056281ee5dbbb0c8ef51b22abea07e9f9fe44bd0f3dc89efcca89933274
3
+ metadata.gz: 42b7fa071364c73605b8bd5fd1d374ea6942e575d386a714c477d67061ba8aeb
4
+ data.tar.gz: 7a9ee001a427055c2c30f90f982c98eadb0d8e3ee4209afd2acfa0e59c9da415
5
5
  SHA512:
6
- metadata.gz: d3d9723e894f7a2bf924c6be97456c928e789e301312d575d9c81b1ac3ebcb4dd8030303054eea561c384e357c4a4c25353fbeb7f9201c9551c475f5eceabbb7
7
- data.tar.gz: 372908640fc08d0e88b2a93748ba84ba7bd1b6d4ef32cbad8b218a6ea0250d0d9f21f5b745deb1948f1dc27487971af9c2d88a207a7b309063d9ea8d64181f21
6
+ metadata.gz: 78965b6a9605abf0e44e755be975c3277fdbc823df4d8296ab3850d38a2f62af053316dfdc86038b54540ce1348e68602541867fe6a268c500597be055b3d3a6
7
+ data.tar.gz: 43b9c95eb665b7667043ca18a36ad852cddfe3f5b6164176ac30c31f1f8697e390cdd242af069f6e82778e9eecfcb770c436ce650b9a4c2a0d42b518e5613ccf
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.0)
4
+ light_service-validated_context (0.2.0)
5
5
  dry-types (~> 1.7.0)
6
6
  light-service (>= 0.18.0)
7
7
  zeitwerk
@@ -83,6 +83,7 @@ GEM
83
83
 
84
84
  PLATFORMS
85
85
  arm64-darwin-21
86
+ x86_64-linux
86
87
 
87
88
  DEPENDENCIES
88
89
  debug
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.0"
5
+ VERSION = "0.2.0"
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.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-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