pathway 0.12.3 → 1.0.0
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/.github/workflows/tests.yml +2 -2
- data/CHANGELOG.md +5 -0
- data/README.md +0 -4
- data/lib/pathway/plugins/auto_deconstruct_state.rb +14 -4
- data/lib/pathway/plugins/dry_validation.rb +73 -12
- data/lib/pathway/plugins/responder.rb +3 -5
- data/lib/pathway/plugins/sequel_models.rb +15 -15
- data/lib/pathway/plugins/simple_auth.rb +1 -3
- data/lib/pathway/result.rb +16 -42
- data/lib/pathway/rspec/matchers/fail_on.rb +6 -6
- data/lib/pathway/version.rb +1 -1
- data/lib/pathway.rb +35 -54
- data/pathway.gemspec +4 -4
- metadata +22 -29
- data/lib/pathway/plugins/auto_deconstruct_state/ruby3.rb +0 -22
- data/lib/pathway/plugins/dry_validation/v0_11.rb +0 -96
- data/lib/pathway/plugins/dry_validation/v0_12.rb +0 -95
- data/lib/pathway/plugins/dry_validation/v1_0.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc09be062809a2afe5a1874e1da97c15be0353059116e2932d8224d0857b3899
|
4
|
+
data.tar.gz: dbc788f70ba9f95bfa700c790aff1a1c2313b2802b58417e4598314263c2af6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e39b0e7702a87427d242f722deb03ccf18186bb47e271a1fbb2c218bd1bde948db7fece51805a2b8de0c3f232273f624dd4c28cf743a50aa83bf00262c161cf2
|
7
|
+
data.tar.gz: 2b528a4f6c8f8172294265fafe0e3751c62a3412bc67a2deeef09f124dd38a419302502e7c7be77ead661088b3ccb54c2eb0f35c65e848a0e5947117470d294f
|
data/.github/workflows/tests.yml
CHANGED
@@ -11,7 +11,7 @@ jobs:
|
|
11
11
|
runs-on: ubuntu-latest
|
12
12
|
strategy:
|
13
13
|
matrix:
|
14
|
-
ruby-version: [
|
14
|
+
ruby-version: [3.1, 3.2, 3.3]
|
15
15
|
steps:
|
16
16
|
- uses: actions/checkout@v3
|
17
17
|
- name: Set up Ruby
|
@@ -23,7 +23,7 @@ jobs:
|
|
23
23
|
- name: Run tests
|
24
24
|
run: bundle exec rake
|
25
25
|
- name: Coveralls GitHub Action
|
26
|
-
if: matrix.ruby-version == '3.
|
26
|
+
if: matrix.ruby-version == '3.3'
|
27
27
|
uses: coverallsapp/github-action@v2
|
28
28
|
with:
|
29
29
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## [1.0.0] - 2025-05-19
|
2
|
+
### Changed
|
3
|
+
- Removed support for `Ruby` versions older than 3.0
|
4
|
+
- Removed support for `dry-validation` versions older than 1.0
|
5
|
+
|
1
6
|
## [0.12.3] - 2024-08-13
|
2
7
|
### Changed
|
3
8
|
- Renamed config option `:auto_wire_options` to `:auto_wire` at `:dry_validation` plugin
|
data/README.md
CHANGED
@@ -474,10 +474,6 @@ end
|
|
474
474
|
|
475
475
|
The `with:` parameter can always be specified for `step :validate`, and allows you to override the default mapping regardless if auto-wiring is active or not.
|
476
476
|
|
477
|
-
##### Older versions of `dry-validation`
|
478
|
-
|
479
|
-
Pathway supports the `dry-validation` gem down to version `0.11` (inclusive) in case you still have unmigrated code. When using versions below `1.0` the concept of contract is not present and instead of calling the `contract` method to set up your validation logic, you must use the `form` method. Everything else remains the same except, obviously, that you would have to use `dry-definition`'s [old API](https://dry-rb.org/gems/dry-validation/0.13/) which is a bit different from the current one.
|
480
|
-
|
481
477
|
#### `SimpleAuth` plugin
|
482
478
|
|
483
479
|
This very simple plugin adds a custom step called `:authorize`, that can be used to check for permissions and halt the operation with a `:forbidden` error when they aren't fulfilled.
|
@@ -1,12 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
if RUBY_VERSION =~ /^3\./
|
4
|
-
require 'pathway/plugins/auto_deconstruct_state/ruby3'
|
5
|
-
end
|
6
|
-
|
7
3
|
module Pathway
|
8
4
|
module Plugins
|
9
5
|
module AutoDeconstructState
|
6
|
+
module DSLMethods
|
7
|
+
private
|
8
|
+
|
9
|
+
def _callable(callable)
|
10
|
+
if callable.is_a?(Symbol) && @operation.respond_to?(callable, true) &&
|
11
|
+
@operation.method(callable).arity != 0 &&
|
12
|
+
@operation.method(callable).parameters.all? { _1 in [:key|:keyreq|:keyrest|:block,*] }
|
13
|
+
|
14
|
+
-> state { @operation.send(callable, **state) }
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
10
20
|
end
|
11
21
|
end
|
12
22
|
end
|
@@ -5,21 +5,82 @@ require 'dry/validation'
|
|
5
5
|
module Pathway
|
6
6
|
module Plugins
|
7
7
|
module DryValidation
|
8
|
-
|
8
|
+
module ClassMethods
|
9
|
+
attr_reader :contract_class, :contract_options
|
10
|
+
attr_accessor :auto_wire
|
11
|
+
|
12
|
+
alias_method :auto_wire_options, :auto_wire
|
13
|
+
alias_method :auto_wire_options=, :auto_wire=
|
14
|
+
|
15
|
+
def contract(base = nil, &block)
|
16
|
+
if block_given?
|
17
|
+
base ||= _base_contract
|
18
|
+
self.contract_class = Class.new(base, &block)
|
19
|
+
elsif base
|
20
|
+
self.contract_class = base
|
21
|
+
else
|
22
|
+
raise ArgumentError, 'Either a contract class or a block must be provided'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def params(*args, **kwargs, &block)
|
27
|
+
contract { params(*args, **kwargs, &block) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def contract_class= klass
|
31
|
+
@contract_class = klass
|
32
|
+
@contract_options = (klass.dry_initializer.options - Dry::Validation::Contract.dry_initializer.options).map(&:target)
|
33
|
+
@builded_contract = @contract_options.empty? && klass.schema ? klass.new : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_contract(**opts)
|
37
|
+
@builded_contract || contract_class.new(**opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def inherited(subclass)
|
41
|
+
super
|
42
|
+
subclass.auto_wire = auto_wire
|
43
|
+
subclass.contract_class = contract_class
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def _base_contract
|
49
|
+
superclass.respond_to?(:contract_class) ? superclass.contract_class : Dry::Validation::Contract
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module InstanceMethods
|
54
|
+
extend Forwardable
|
55
|
+
|
56
|
+
delegate %i[build_contract contract_options auto_wire_options auto_wire] => 'self.class'
|
57
|
+
alias_method :contract, :build_contract
|
58
|
+
|
59
|
+
def validate(state, with: nil)
|
60
|
+
if auto_wire && contract_options.any?
|
61
|
+
with ||= contract_options.zip(contract_options).to_h
|
62
|
+
end
|
63
|
+
opts = Hash(with).map { |to, from| [to, state[from]] }.to_h
|
64
|
+
validate_with(state[:input], **opts)
|
65
|
+
.then { |params| state.update(params:) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_with(input, **opts)
|
69
|
+
result = contract(**opts).call(input)
|
70
|
+
|
71
|
+
result.success? ? wrap(result.values.to_h) : error(:validation, details: result.errors.to_h)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.apply(operation, auto_wire_options: (auto_wire_options_was_not_used=true; false), auto_wire: auto_wire_options)
|
9
76
|
#:nocov:
|
10
|
-
|
11
|
-
|
12
|
-
elsif Gem.loaded_specs['dry-validation'].version < Gem::Version.new('0.12')
|
13
|
-
require 'pathway/plugins/dry_validation/v0_11'
|
14
|
-
operation.plugin(Plugins::DryValidation::V0_11, **kwargs)
|
15
|
-
elsif Gem.loaded_specs['dry-validation'].version < Gem::Version.new('1.0')
|
16
|
-
require 'pathway/plugins/dry_validation/v0_12'
|
17
|
-
operation.plugin(Plugins::DryValidation::V0_12, **kwargs)
|
18
|
-
else
|
19
|
-
require 'pathway/plugins/dry_validation/v1_0'
|
20
|
-
operation.plugin(Plugins::DryValidation::V1_0, **kwargs)
|
77
|
+
unless auto_wire_options_was_not_used
|
78
|
+
warn "[DEPRECATION] `auto_wire_options` is deprecated. Please use `auto_wire` instead"
|
21
79
|
end
|
22
80
|
#:nocov:
|
81
|
+
|
82
|
+
operation.auto_wire = auto_wire
|
83
|
+
operation.contract_class = Dry::Validation::Contract
|
23
84
|
end
|
24
85
|
end
|
25
86
|
end
|
@@ -4,8 +4,8 @@ module Pathway
|
|
4
4
|
module Plugins
|
5
5
|
module Responder
|
6
6
|
module ClassMethods
|
7
|
-
|
8
|
-
result = super(*args)
|
7
|
+
def call(*args, **kwargs, &bl)
|
8
|
+
result = super(*args, **kwargs)
|
9
9
|
block_given? ? Responder.respond(result, &bl) : result
|
10
10
|
end
|
11
11
|
end
|
@@ -21,9 +21,7 @@ module Pathway
|
|
21
21
|
instance_eval(&bl)
|
22
22
|
end
|
23
23
|
|
24
|
-
def success(&bl)
|
25
|
-
@ok = bl
|
26
|
-
end
|
24
|
+
def success(&bl)= @ok = bl
|
27
25
|
|
28
26
|
def failure(type = nil, &bl)
|
29
27
|
if type.nil?
|
@@ -6,33 +6,33 @@ module Pathway
|
|
6
6
|
module Plugins
|
7
7
|
module SequelModels
|
8
8
|
module DSLMethods
|
9
|
-
def transaction(step_name = nil, &
|
10
|
-
|
9
|
+
def transaction(step_name = nil, &dsl_bl)
|
10
|
+
raise 'must provide a step or a block but not both' if !step_name.nil? == block_given?
|
11
11
|
|
12
12
|
if step_name
|
13
13
|
transaction { step step_name }
|
14
14
|
else
|
15
|
-
around(->
|
15
|
+
around(->(runner, _) {
|
16
16
|
db.transaction(savepoint: true) do
|
17
|
-
raise Sequel::Rollback if
|
17
|
+
raise Sequel::Rollback if runner.call.failure?
|
18
18
|
end
|
19
|
-
}, &
|
19
|
+
}, &dsl_bl)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def after_commit(step_name = nil, &
|
24
|
-
|
23
|
+
def after_commit(step_name = nil, &dsl_bl)
|
24
|
+
raise 'must provide a step or a block but not both' if !step_name.nil? == block_given?
|
25
25
|
|
26
26
|
if step_name
|
27
27
|
after_commit { step step_name }
|
28
28
|
else
|
29
|
-
around(->
|
30
|
-
|
29
|
+
around(->(runner, state) {
|
30
|
+
dsl_copy = self.class::DSL.new(State.new(self, state.to_h.dup), self)
|
31
31
|
|
32
32
|
db.after_commit do
|
33
|
-
|
33
|
+
runner.call(dsl_copy)
|
34
34
|
end
|
35
|
-
}, &
|
35
|
+
}, &dsl_bl)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -41,10 +41,10 @@ module Pathway
|
|
41
41
|
attr_accessor :model_class, :search_field, :model_not_found
|
42
42
|
|
43
43
|
def model(model_class, search_by: model_class.primary_key, set_result_key: true, set_context_param: true, error_message: nil)
|
44
|
-
self.model_class
|
45
|
-
self.search_field
|
46
|
-
self.result_key
|
47
|
-
self.model_not_found
|
44
|
+
self.model_class = model_class
|
45
|
+
self.search_field = search_by
|
46
|
+
self.result_key = Inflector.underscore(Inflector.demodulize(model_class.name)).to_sym if set_result_key
|
47
|
+
self.model_not_found = error_message || "#{Inflector.humanize(Inflector.underscore(Inflector.demodulize(model_class.name)))} not found".freeze
|
48
48
|
|
49
49
|
self.context(result_key => Contextualizer::OPTIONAL) if set_result_key && set_context_param
|
50
50
|
end
|
data/lib/pathway/result.rb
CHANGED
@@ -6,13 +6,8 @@ module Pathway
|
|
6
6
|
attr_reader :value, :error
|
7
7
|
|
8
8
|
class Success < Result
|
9
|
-
def initialize(value)
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def success?
|
14
|
-
true
|
15
|
-
end
|
9
|
+
def initialize(value) = @value = value
|
10
|
+
def success? = true
|
16
11
|
|
17
12
|
def then(bl=nil)
|
18
13
|
result(block_given? ? yield(value): bl.call(value))
|
@@ -25,29 +20,18 @@ module Pathway
|
|
25
20
|
|
26
21
|
private
|
27
22
|
|
28
|
-
|
23
|
+
alias_method :value_for_deconstruct, :value
|
29
24
|
end
|
30
25
|
|
31
26
|
class Failure < Result
|
32
|
-
def initialize(error)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def success?
|
37
|
-
false
|
38
|
-
end
|
39
|
-
|
40
|
-
def then(_=nil)
|
41
|
-
self
|
42
|
-
end
|
43
|
-
|
44
|
-
def tee(_=nil)
|
45
|
-
self
|
46
|
-
end
|
27
|
+
def initialize(error) = @error = error
|
28
|
+
def success? = false
|
29
|
+
def then(_=nil) = self
|
30
|
+
def tee(_=nil) = self
|
47
31
|
|
48
32
|
private
|
49
33
|
|
50
|
-
|
34
|
+
alias_method :value_for_deconstruct, :error
|
51
35
|
end
|
52
36
|
|
53
37
|
module Mixin
|
@@ -55,10 +39,16 @@ module Pathway
|
|
55
39
|
Failure = Result::Failure
|
56
40
|
end
|
57
41
|
|
58
|
-
def
|
59
|
-
|
42
|
+
def self.success(value) = Success.new(value)
|
43
|
+
def self.failure(error) = Failure.new(error)
|
44
|
+
|
45
|
+
def self.result(object)
|
46
|
+
object.is_a?(Result) ? object : success(object)
|
60
47
|
end
|
61
48
|
|
49
|
+
def failure? = !success?
|
50
|
+
def deconstruct = [value_for_deconstruct]
|
51
|
+
|
62
52
|
def deconstruct_keys(keys)
|
63
53
|
if value_for_deconstruct.respond_to?(:deconstruct_keys)
|
64
54
|
value_for_deconstruct.deconstruct_keys(keys)
|
@@ -67,22 +57,6 @@ module Pathway
|
|
67
57
|
end
|
68
58
|
end
|
69
59
|
|
70
|
-
def failure?
|
71
|
-
!success?
|
72
|
-
end
|
73
|
-
|
74
|
-
def self.success(value)
|
75
|
-
Success.new(value)
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.failure(error)
|
79
|
-
Failure.new(error)
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.result(object)
|
83
|
-
object.is_a?(Result) ? object : success(object)
|
84
|
-
end
|
85
|
-
|
86
60
|
delegate :result => 'self.class'
|
87
61
|
end
|
88
62
|
end
|
@@ -22,22 +22,22 @@ RSpec::Matchers.define :fail_on do |input|
|
|
22
22
|
@type = type
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
alias_method :with_type, :type
|
26
|
+
alias_method :and_type, :type
|
27
27
|
|
28
28
|
chain :message do |message|
|
29
29
|
@message = message
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
alias_method :with_message, :message
|
33
|
+
alias_method :and_message, :message
|
34
34
|
|
35
35
|
chain :details do |details|
|
36
36
|
@details = details
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
alias_method :with_details, :details
|
40
|
+
alias_method :and_details, :details
|
41
41
|
|
42
42
|
description do
|
43
43
|
'fail' + (@type ? " with :#@type error" : '')
|
data/lib/pathway/version.rb
CHANGED
data/lib/pathway.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'ruby2_keywords'
|
4
3
|
require 'forwardable'
|
5
4
|
require 'dry/inflector'
|
6
5
|
require 'contextualizer'
|
@@ -11,7 +10,7 @@ module Pathway
|
|
11
10
|
Inflector = Dry::Inflector.new
|
12
11
|
class Operation
|
13
12
|
class << self
|
14
|
-
|
13
|
+
def plugin(name,...)
|
15
14
|
require "pathway/plugins/#{Inflector.underscore(name)}" if name.is_a?(Symbol)
|
16
15
|
|
17
16
|
plugin = name.is_a?(Module) ? name : Plugins.const_get(Inflector.camelize(name))
|
@@ -20,7 +19,7 @@ module Pathway
|
|
20
19
|
self.include plugin::InstanceMethods if plugin.const_defined? :InstanceMethods
|
21
20
|
self::DSL.include plugin::DSLMethods if plugin.const_defined? :DSLMethods
|
22
21
|
|
23
|
-
plugin.apply(self
|
22
|
+
plugin.apply(self,...) if plugin.respond_to?(:apply)
|
24
23
|
end
|
25
24
|
|
26
25
|
def inherited(subclass)
|
@@ -45,13 +44,8 @@ module Pathway
|
|
45
44
|
@details = details || {}
|
46
45
|
end
|
47
46
|
|
48
|
-
def deconstruct
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
def deconstruct_keys(_)
|
53
|
-
{ type: type, message: message, details: details }
|
54
|
-
end
|
47
|
+
def deconstruct = [type, message, details]
|
48
|
+
def deconstruct_keys(_) = { type:, message:, details: }
|
55
49
|
|
56
50
|
private
|
57
51
|
|
@@ -62,35 +56,29 @@ module Pathway
|
|
62
56
|
|
63
57
|
class State
|
64
58
|
extend Forwardable
|
59
|
+
delegate %i([] []= fetch store include? values_at deconstruct_keys) => :@hash
|
65
60
|
|
66
61
|
def initialize(operation, values = {})
|
67
62
|
@hash = operation.context.merge(values)
|
68
63
|
@result_key = operation.result_key
|
69
64
|
end
|
70
65
|
|
71
|
-
delegate %i([] []= fetch store include? values_at deconstruct_keys) => :@hash
|
72
|
-
|
73
66
|
def update(kargs)
|
74
67
|
@hash.update(kargs)
|
75
68
|
self
|
76
69
|
end
|
77
70
|
|
78
|
-
def result
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
def to_hash
|
83
|
-
@hash
|
84
|
-
end
|
71
|
+
def result = @hash[@result_key]
|
72
|
+
def to_hash = @hash
|
85
73
|
|
86
74
|
def use(&bl)
|
87
75
|
raise ArgumentError, 'a block must be provided' if !block_given?
|
88
|
-
if bl.parameters
|
76
|
+
if bl.parameters in [*, [:rest|:keyrest,], *]
|
89
77
|
raise ArgumentError, 'rest arguments are not supported'
|
90
78
|
end
|
91
79
|
|
92
|
-
keys = bl.parameters.select {
|
93
|
-
names = bl.parameters.select {
|
80
|
+
keys = bl.parameters.select { _1 in :key|:keyreq, }.map(&:last)
|
81
|
+
names = bl.parameters.select { _1 in :req|:opt, }.map(&:last)
|
94
82
|
|
95
83
|
if keys.any? && names.any?
|
96
84
|
raise ArgumentError, 'cannot mix positional and keyword arguments'
|
@@ -110,20 +98,19 @@ module Pathway
|
|
110
98
|
module Base
|
111
99
|
module ClassMethods
|
112
100
|
attr_accessor :result_key
|
113
|
-
|
101
|
+
|
102
|
+
alias_method :result_at, :result_key=
|
114
103
|
|
115
104
|
def process(&bl)
|
116
105
|
dsl = self::DSL
|
117
106
|
define_method(:call) do |input|
|
118
|
-
dsl.new(State.new(self, input:
|
107
|
+
dsl.new(State.new(self, input:), self)
|
119
108
|
.run(&bl)
|
120
109
|
.then(&:result)
|
121
110
|
end
|
122
111
|
end
|
123
112
|
|
124
|
-
|
125
|
-
new(ctx).call(*params)
|
126
|
-
end
|
113
|
+
def call(ctx,...) = new(ctx).call(...)
|
127
114
|
|
128
115
|
def inherited(subclass)
|
129
116
|
super
|
@@ -137,18 +124,16 @@ module Pathway
|
|
137
124
|
delegate :result_key => 'self.class'
|
138
125
|
delegate %i[result success failure] => Result
|
139
126
|
|
140
|
-
|
127
|
+
alias_method :wrap, :result
|
141
128
|
|
142
|
-
def call(*)
|
143
|
-
fail 'must implement at subclass'
|
144
|
-
end
|
129
|
+
def call(*) = raise 'must implement at subclass'
|
145
130
|
|
146
131
|
def error(type, message: nil, details: nil)
|
147
|
-
failure(Error.new(type
|
132
|
+
failure(Error.new(type:, message:, details:))
|
148
133
|
end
|
149
134
|
|
150
135
|
def wrap_if_present(value, type: :not_found, message: nil, details: {})
|
151
|
-
value.nil? ? error(type, message
|
136
|
+
value.nil? ? error(type, message:, details:) : success(value)
|
152
137
|
end
|
153
138
|
end
|
154
139
|
|
@@ -168,45 +153,43 @@ module Pathway
|
|
168
153
|
end
|
169
154
|
|
170
155
|
# Execute step and preserve the former state
|
171
|
-
|
156
|
+
def step(callable,...)
|
172
157
|
bl = _callable(callable)
|
173
|
-
|
174
|
-
@result = @result.tee { |state| bl.call(state, *args) }
|
158
|
+
@result = @result.tee { |state| bl.call(state,...) }
|
175
159
|
end
|
176
160
|
|
177
161
|
# Execute step and modify the former state setting the key
|
178
|
-
def set(callable, *args, to: @operation.result_key)
|
162
|
+
def set(callable, *args, to: @operation.result_key, **kwargs, &bl)
|
179
163
|
bl = _callable(callable)
|
180
164
|
|
181
165
|
@result = @result.then do |state|
|
182
|
-
wrap(bl.call(state, *args))
|
166
|
+
wrap(bl.call(state, *args, **kwargs, &bl))
|
183
167
|
.then { |value| state.update(to => value) }
|
184
168
|
end
|
185
169
|
end
|
186
170
|
|
187
171
|
# Execute step and replace the current state completely
|
188
|
-
def map(callable)
|
172
|
+
def map(callable,...)
|
189
173
|
bl = _callable(callable)
|
190
|
-
@result = @result.then(
|
174
|
+
@result = @result.then { |state| bl.call(state,...) }
|
191
175
|
end
|
192
176
|
|
193
|
-
def around(
|
177
|
+
def around(execution_strategy, &dsl_block)
|
194
178
|
@result.then do |state|
|
195
|
-
|
196
|
-
|
179
|
+
dsl_runner = ->(dsl = self) { @result = dsl.run(&dsl_block) }
|
180
|
+
|
181
|
+
_callable(execution_strategy).call(dsl_runner, state)
|
197
182
|
end
|
198
183
|
end
|
199
184
|
|
200
|
-
def if_true(cond, &
|
185
|
+
def if_true(cond, &dsl_block)
|
201
186
|
cond = _callable(cond)
|
202
|
-
around(->
|
203
|
-
seq.call if cond.call(state)
|
204
|
-
}, &steps)
|
187
|
+
around(->(dsl_runner, state) { dsl_runner.call if cond.call(state) }, &dsl_block)
|
205
188
|
end
|
206
189
|
|
207
|
-
def if_false(cond, &
|
190
|
+
def if_false(cond, &dsl_block)
|
208
191
|
cond = _callable(cond)
|
209
|
-
if_true(->
|
192
|
+
if_true(->(state) { !cond.call(state) }, &dsl_block)
|
210
193
|
end
|
211
194
|
|
212
195
|
alias_method :sequence, :around
|
@@ -214,16 +197,14 @@ module Pathway
|
|
214
197
|
|
215
198
|
private
|
216
199
|
|
217
|
-
def wrap(obj)
|
218
|
-
Result.result(obj)
|
219
|
-
end
|
200
|
+
def wrap(obj) = Result.result(obj)
|
220
201
|
|
221
202
|
def _callable(callable)
|
222
203
|
case callable
|
223
204
|
when Proc
|
224
|
-
->
|
205
|
+
->(*args, **kwargs) { @operation.instance_exec(*args, **kwargs, &callable) }
|
225
206
|
when Symbol
|
226
|
-
->
|
207
|
+
->(*args, **kwargs) { @operation.send(callable, *args, **kwargs) }
|
227
208
|
else
|
228
209
|
callable
|
229
210
|
end
|
data/pathway.gemspec
CHANGED
@@ -27,13 +27,12 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
|
-
spec.required_ruby_version = ">=
|
30
|
+
spec.required_ruby_version = ">= 3.1.0"
|
31
31
|
|
32
32
|
spec.add_dependency "dry-inflector", ">= 0.1.0"
|
33
|
-
spec.add_dependency "contextualizer", "~> 0.0
|
34
|
-
spec.add_dependency "ruby2_keywords"
|
33
|
+
spec.add_dependency "contextualizer", "~> 0.1.0"
|
35
34
|
|
36
|
-
spec.add_development_dependency "dry-validation", ">= 0
|
35
|
+
spec.add_development_dependency "dry-validation", ">= 1.0"
|
37
36
|
spec.add_development_dependency "bundler", ">= 2.4.10"
|
38
37
|
spec.add_development_dependency "sequel", "~> 5.0"
|
39
38
|
spec.add_development_dependency "rake", "~> 13.0"
|
@@ -41,6 +40,7 @@ Gem::Specification.new do |spec|
|
|
41
40
|
spec.add_development_dependency "simplecov-lcov", '~> 0.8.0'
|
42
41
|
spec.add_development_dependency "simplecov"
|
43
42
|
spec.add_development_dependency "pry"
|
43
|
+
spec.add_development_dependency "byebug"
|
44
44
|
spec.add_development_dependency "pry-byebug"
|
45
45
|
spec.add_development_dependency "pry-doc"
|
46
46
|
spec.add_development_dependency "pry-stack"
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pathway
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pablo Herrero
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: dry-inflector
|
@@ -30,42 +29,28 @@ dependencies:
|
|
30
29
|
requirements:
|
31
30
|
- - "~>"
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0
|
32
|
+
version: 0.1.0
|
34
33
|
type: :runtime
|
35
34
|
prerelease: false
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
37
36
|
requirements:
|
38
37
|
- - "~>"
|
39
38
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: ruby2_keywords
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
39
|
+
version: 0.1.0
|
55
40
|
- !ruby/object:Gem::Dependency
|
56
41
|
name: dry-validation
|
57
42
|
requirement: !ruby/object:Gem::Requirement
|
58
43
|
requirements:
|
59
44
|
- - ">="
|
60
45
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0
|
46
|
+
version: '1.0'
|
62
47
|
type: :development
|
63
48
|
prerelease: false
|
64
49
|
version_requirements: !ruby/object:Gem::Requirement
|
65
50
|
requirements:
|
66
51
|
- - ">="
|
67
52
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0
|
53
|
+
version: '1.0'
|
69
54
|
- !ruby/object:Gem::Dependency
|
70
55
|
name: bundler
|
71
56
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +149,20 @@ dependencies:
|
|
164
149
|
- - ">="
|
165
150
|
- !ruby/object:Gem::Version
|
166
151
|
version: '0'
|
152
|
+
- !ruby/object:Gem::Dependency
|
153
|
+
name: byebug
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
type: :development
|
160
|
+
prerelease: false
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
167
166
|
- !ruby/object:Gem::Dependency
|
168
167
|
name: pry-byebug
|
169
168
|
requirement: !ruby/object:Gem::Requirement
|
@@ -228,11 +227,7 @@ files:
|
|
228
227
|
- bin/setup
|
229
228
|
- lib/pathway.rb
|
230
229
|
- lib/pathway/plugins/auto_deconstruct_state.rb
|
231
|
-
- lib/pathway/plugins/auto_deconstruct_state/ruby3.rb
|
232
230
|
- lib/pathway/plugins/dry_validation.rb
|
233
|
-
- lib/pathway/plugins/dry_validation/v0_11.rb
|
234
|
-
- lib/pathway/plugins/dry_validation/v0_12.rb
|
235
|
-
- lib/pathway/plugins/dry_validation/v1_0.rb
|
236
231
|
- lib/pathway/plugins/responder.rb
|
237
232
|
- lib/pathway/plugins/sequel_models.rb
|
238
233
|
- lib/pathway/plugins/simple_auth.rb
|
@@ -254,7 +249,6 @@ licenses:
|
|
254
249
|
metadata:
|
255
250
|
bug_tracker_uri: https://github.com/pabloh/pathway/issues
|
256
251
|
source_code_uri: https://github.com/pabloh/pathway
|
257
|
-
post_install_message:
|
258
252
|
rdoc_options: []
|
259
253
|
require_paths:
|
260
254
|
- lib
|
@@ -262,15 +256,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
262
256
|
requirements:
|
263
257
|
- - ">="
|
264
258
|
- !ruby/object:Gem::Version
|
265
|
-
version:
|
259
|
+
version: 3.1.0
|
266
260
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
267
261
|
requirements:
|
268
262
|
- - ">="
|
269
263
|
- !ruby/object:Gem::Version
|
270
264
|
version: '0'
|
271
265
|
requirements: []
|
272
|
-
rubygems_version: 3.
|
273
|
-
signing_key:
|
266
|
+
rubygems_version: 3.6.9
|
274
267
|
specification_version: 4
|
275
268
|
summary: Define your business logic in simple steps.
|
276
269
|
test_files: []
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pathway
|
4
|
-
module Plugins
|
5
|
-
module AutoDeconstructState
|
6
|
-
module DSLMethods
|
7
|
-
private
|
8
|
-
|
9
|
-
def _callable(callable)
|
10
|
-
if callable.is_a?(Symbol) && @operation.respond_to?(callable, true) &&
|
11
|
-
@operation.method(callable).arity != 0 &&
|
12
|
-
@operation.method(callable).parameters.all? { _1 in [:key|:keyreq|:keyrest|:block,*] }
|
13
|
-
|
14
|
-
-> state { @operation.send(callable, **state) }
|
15
|
-
else
|
16
|
-
super
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pathway
|
4
|
-
module Plugins
|
5
|
-
module DryValidation
|
6
|
-
module V0_11
|
7
|
-
module ClassMethods
|
8
|
-
attr_reader :form_class, :form_options
|
9
|
-
attr_accessor :auto_wire
|
10
|
-
|
11
|
-
alias_method :auto_wire_options, :auto_wire
|
12
|
-
alias_method :auto_wire_options=, :auto_wire=
|
13
|
-
|
14
|
-
def form(base = nil, **opts, &block)
|
15
|
-
if block_given?
|
16
|
-
base ||= _base_form
|
17
|
-
self.form_class = _block_definition(base, opts, &block)
|
18
|
-
elsif base
|
19
|
-
self.form_class = _form_class(base)
|
20
|
-
else
|
21
|
-
raise ArgumentError, 'Either a form class or a block must be provided'
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def form_class= klass
|
26
|
-
@builded_form = klass.options.empty? ? klass.new : nil
|
27
|
-
@form_class = klass
|
28
|
-
@form_options = klass.options.keys
|
29
|
-
end
|
30
|
-
|
31
|
-
def build_form(opts = {})
|
32
|
-
@builded_form || form_class.new(opts)
|
33
|
-
end
|
34
|
-
|
35
|
-
def inherited(subclass)
|
36
|
-
super
|
37
|
-
subclass.form_class = form_class
|
38
|
-
subclass.auto_wire = auto_wire
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def _base_form
|
44
|
-
superclass.respond_to?(:form_class) ? superclass.form_class : Dry::Validation::Schema::Form
|
45
|
-
end
|
46
|
-
|
47
|
-
def _form_class(form)
|
48
|
-
form.is_a?(Class) ? form : form.class
|
49
|
-
end
|
50
|
-
|
51
|
-
def _form_opts(opts = {})
|
52
|
-
opts.merge(build: false)
|
53
|
-
end
|
54
|
-
|
55
|
-
def _block_definition(base, opts, &block)
|
56
|
-
Dry::Validation.Form(_form_class(base), _form_opts(opts), &block)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
module InstanceMethods
|
61
|
-
extend Forwardable
|
62
|
-
|
63
|
-
delegate %i[build_form form_options auto_wire_options] => 'self.class'
|
64
|
-
delegate %i[build_form form_options auto_wire_options auto_wire] => 'self.class'
|
65
|
-
alias_method :form, :build_form
|
66
|
-
|
67
|
-
def validate(state, with: nil)
|
68
|
-
if auto_wire && form_options.any?
|
69
|
-
with ||= form_options.zip(form_options).to_h
|
70
|
-
end
|
71
|
-
opts = Hash(with).map { |opt, key| [opt, state[key]] }.to_h
|
72
|
-
validate_with(state[:input], opts)
|
73
|
-
.then { |params| state.update(params: params) }
|
74
|
-
end
|
75
|
-
|
76
|
-
def validate_with(params, opts = {})
|
77
|
-
val = form(opts).call(params)
|
78
|
-
|
79
|
-
val.success? ? wrap(val.output) : error(:validation, details: val.messages)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def self.apply(operation, auto_wire_options: (auto_wire_options_was_not_used=true; false), auto_wire: auto_wire_options)
|
84
|
-
#:nocov:
|
85
|
-
unless auto_wire_options_was_not_used
|
86
|
-
warn "[DEPRECATION] `auto_wire_options` is deprecated. Please use `auto_wire` instead"
|
87
|
-
end
|
88
|
-
#:nocov:
|
89
|
-
|
90
|
-
operation.auto_wire = auto_wire
|
91
|
-
operation.form_class = Dry::Validation::Schema::Form
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pathway
|
4
|
-
module Plugins
|
5
|
-
module DryValidation
|
6
|
-
module V0_12
|
7
|
-
module ClassMethods
|
8
|
-
attr_reader :form_class, :form_options
|
9
|
-
attr_accessor :auto_wire
|
10
|
-
|
11
|
-
alias_method :auto_wire_options, :auto_wire
|
12
|
-
alias_method :auto_wire_options=, :auto_wire=
|
13
|
-
|
14
|
-
def form(base = nil, **opts, &block)
|
15
|
-
if block_given?
|
16
|
-
base ||= _base_form
|
17
|
-
self.form_class = _block_definition(base, opts, &block)
|
18
|
-
elsif base
|
19
|
-
self.form_class = _form_class(base)
|
20
|
-
else
|
21
|
-
raise ArgumentError, 'Either a form class or a block must be provided'
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def form_class= klass
|
26
|
-
@builded_form = klass.options.empty? ? klass.new : nil
|
27
|
-
@form_class = klass
|
28
|
-
@form_options = klass.options.keys
|
29
|
-
end
|
30
|
-
|
31
|
-
def build_form(opts = {})
|
32
|
-
@builded_form || form_class.new(opts)
|
33
|
-
end
|
34
|
-
|
35
|
-
def inherited(subclass)
|
36
|
-
super
|
37
|
-
subclass.form_class = form_class
|
38
|
-
subclass.auto_wire = auto_wire
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def _base_form
|
44
|
-
superclass.respond_to?(:form_class) ? superclass.form_class : Dry::Validation::Schema::Params
|
45
|
-
end
|
46
|
-
|
47
|
-
def _form_class(form)
|
48
|
-
form.is_a?(Class) ? form : form.class
|
49
|
-
end
|
50
|
-
|
51
|
-
def _form_opts(opts = {})
|
52
|
-
opts.merge(build: false)
|
53
|
-
end
|
54
|
-
|
55
|
-
def _block_definition(base, opts, &block)
|
56
|
-
Dry::Validation.Params(_form_class(base), _form_opts(opts), &block)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
module InstanceMethods
|
61
|
-
extend Forwardable
|
62
|
-
|
63
|
-
delegate %i[build_form form_options auto_wire_options auto_wire] => 'self.class'
|
64
|
-
alias_method :form, :build_form
|
65
|
-
|
66
|
-
def validate(state, with: nil)
|
67
|
-
if auto_wire && form_options.any?
|
68
|
-
with ||= form_options.zip(form_options).to_h
|
69
|
-
end
|
70
|
-
opts = Hash(with).map { |opt, key| [opt, state[key]] }.to_h
|
71
|
-
validate_with(state[:input], opts)
|
72
|
-
.then { |params| state.update(params: params) }
|
73
|
-
end
|
74
|
-
|
75
|
-
def validate_with(params, opts = {})
|
76
|
-
val = form(opts).call(params)
|
77
|
-
|
78
|
-
val.success? ? wrap(val.output) : error(:validation, details: val.messages)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.apply(operation, auto_wire_options: (auto_wire_options_was_not_used=true; false), auto_wire: auto_wire_options)
|
83
|
-
#:nocov:
|
84
|
-
unless auto_wire_options_was_not_used
|
85
|
-
warn "[DEPRECATION] `auto_wire_options` is deprecated. Please use `auto_wire` instead"
|
86
|
-
end
|
87
|
-
#:nocov:
|
88
|
-
|
89
|
-
operation.auto_wire = auto_wire
|
90
|
-
operation.form_class = Dry::Validation::Schema::Params
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pathway
|
4
|
-
module Plugins
|
5
|
-
module DryValidation
|
6
|
-
module V1_0
|
7
|
-
module ClassMethods
|
8
|
-
attr_reader :contract_class, :contract_options
|
9
|
-
attr_accessor :auto_wire
|
10
|
-
|
11
|
-
alias_method :auto_wire_options, :auto_wire
|
12
|
-
alias_method :auto_wire_options=, :auto_wire=
|
13
|
-
|
14
|
-
def contract(base = nil, &block)
|
15
|
-
if block_given?
|
16
|
-
base ||= _base_contract
|
17
|
-
self.contract_class = Class.new(base, &block)
|
18
|
-
elsif base
|
19
|
-
self.contract_class = base
|
20
|
-
else
|
21
|
-
raise ArgumentError, 'Either a contract class or a block must be provided'
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
ruby2_keywords def params(*args, &block)
|
26
|
-
contract { params(*args, &block) }
|
27
|
-
end
|
28
|
-
|
29
|
-
def contract_class= klass
|
30
|
-
@contract_class = klass
|
31
|
-
@contract_options = (klass.dry_initializer.options - Dry::Validation::Contract.dry_initializer.options).map(&:target)
|
32
|
-
@builded_contract = @contract_options.empty? && klass.schema ? klass.new : nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def build_contract(**opts)
|
36
|
-
@builded_contract || contract_class.new(**opts)
|
37
|
-
end
|
38
|
-
|
39
|
-
def inherited(subclass)
|
40
|
-
super
|
41
|
-
subclass.auto_wire = auto_wire
|
42
|
-
subclass.contract_class = contract_class
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def _base_contract
|
48
|
-
superclass.respond_to?(:contract_class) ? superclass.contract_class : Dry::Validation::Contract
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
module InstanceMethods
|
53
|
-
extend Forwardable
|
54
|
-
|
55
|
-
delegate %i[build_contract contract_options auto_wire_options auto_wire] => 'self.class'
|
56
|
-
alias_method :contract, :build_contract
|
57
|
-
|
58
|
-
def validate(state, with: nil)
|
59
|
-
if auto_wire && contract_options.any?
|
60
|
-
with ||= contract_options.zip(contract_options).to_h
|
61
|
-
end
|
62
|
-
opts = Hash(with).map { |to, from| [to, state[from]] }.to_h
|
63
|
-
validate_with(state[:input], **opts)
|
64
|
-
.then { |params| state.update(params: params) }
|
65
|
-
end
|
66
|
-
|
67
|
-
def validate_with(input, **opts)
|
68
|
-
result = contract(**opts).call(input)
|
69
|
-
|
70
|
-
result.success? ? wrap(result.values.to_h) : error(:validation, details: result.errors.to_h)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def self.apply(operation, auto_wire_options: (auto_wire_options_was_not_used=true; false), auto_wire: auto_wire_options)
|
75
|
-
#:nocov:
|
76
|
-
unless auto_wire_options_was_not_used
|
77
|
-
warn "[DEPRECATION] `auto_wire_options` is deprecated. Please use `auto_wire` instead"
|
78
|
-
end
|
79
|
-
#:nocov:
|
80
|
-
|
81
|
-
operation.auto_wire = auto_wire
|
82
|
-
operation.contract_class = Dry::Validation::Contract
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|