pathway 0.12.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29e0024427e53290982fedc6eadaacbbf3555627e94930d1c52d3bdcbb90a5dd
4
- data.tar.gz: 98fbe3a72e2aa4c1a3a6b0c1a9ed610231256364b4db44e7840da261bac5a47f
3
+ metadata.gz: dc09be062809a2afe5a1874e1da97c15be0353059116e2932d8224d0857b3899
4
+ data.tar.gz: dbc788f70ba9f95bfa700c790aff1a1c2313b2802b58417e4598314263c2af6a
5
5
  SHA512:
6
- metadata.gz: 81ee05e3aec188a87df7b59032099cfcfb7c59e4641ab6b0db8ebe02bcf693d53fec4db3de16bb62d96e8e84cdf6f45e995411429c53818c748b320da7aa06a2
7
- data.tar.gz: f64c5e2d2bc08afcca58b62a53dbb98511f59d6f7588e59732c434f0abaae103ac00c2153e76f6b2b2eeb034855bdf1c2561cfa44ce15885ab12be5fa815f1b3
6
+ metadata.gz: e39b0e7702a87427d242f722deb03ccf18186bb47e271a1fbb2c218bd1bde948db7fece51805a2b8de0c3f232273f624dd4c28cf743a50aa83bf00262c161cf2
7
+ data.tar.gz: 2b528a4f6c8f8172294265fafe0e3751c62a3412bc67a2deeef09f124dd38a419302502e7c7be77ead661088b3ccb54c2eb0f35c65e848a0e5947117470d294f
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  strategy:
13
13
  matrix:
14
- ruby-version: [2.6, 2.7, 3.0, 3.1, 3.2, 3.3]
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.2'
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,16 @@
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
+
6
+ ## [0.12.3] - 2024-08-13
7
+ ### Changed
8
+ - Renamed config option `:auto_wire_options` to `:auto_wire` at `:dry_validation` plugin
9
+ - Updated `Pathway::State#use` to accept block with postional parameters
10
+ - Updated `Pathway::State#use` to raise an `ArgumentError` exception on invalid arguments
11
+ ### Added
12
+ - Provide alias `Pathway::State#use` to `Pathway::State#unwrap`
13
+
1
14
  ## [0.12.2] - 2024-08-06
2
15
  ### Added
3
16
  - Add `Pathway::State#unwrap` and `Pathway::State#u` to access internal state
data/README.md CHANGED
@@ -407,12 +407,12 @@ end
407
407
 
408
408
  If you are familiar with `dry-validation` you probably know it provides a way to [inject options](https://dry-rb.org/gems/dry-validation/1.4/external-dependencies/) before calling the contract.
409
409
 
410
- In those scenarios, you must either set the `auto_wire_options: true` plugin argument or specify how to map options from the execution state to the contract when calling `step :validate`.
410
+ In those scenarios, you must either set the `auto_wire: true` plugin argument or specify how to map options from the execution state to the contract when calling `step :validate`.
411
411
  Lets see and example for the first case:
412
412
 
413
413
  ```ruby
414
414
  class CreateNugget < Pathway::Operation
415
- plugin :dry_validation, auto_wire_options: true
415
+ plugin :dry_validation, auto_wire: true
416
416
 
417
417
  context :user_name
418
418
 
@@ -438,7 +438,7 @@ class CreateNugget < Pathway::Operation
438
438
  end
439
439
  ```
440
440
 
441
- Here the defined contract needs a `:user_name` option, so we tell the operation to grab the attribute with the same name from the state by activating `:auto_wire_options`, afterwards, when the validation runs, the contract will already have the user name available.
441
+ Here the defined contract needs a `:user_name` option, so we tell the operation to grab the attribute with the same name from the state by activating `:auto_wire`, afterwards, when the validation runs, the contract will already have the user name available.
442
442
 
443
443
  Mind you, this option is `false` by default, so be sure to set it to `true` at `Pathway::Operation` if you'd rather have it enabled for all your operations.
444
444
 
@@ -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
- def self.apply(operation, **kwargs)
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
- if Gem.loaded_specs['dry-validation'].version < Gem::Version.new('0.11')
11
- fail 'unsupported dry-validation gem version'
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
- ruby2_keywords def call(*args, &bl)
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, &bl)
10
- fail 'must provide a step or a block but not both' if !step_name.nil? == block_given?
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(-> steps, _ {
15
+ around(->(runner, _) {
16
16
  db.transaction(savepoint: true) do
17
- raise Sequel::Rollback if steps.call.failure?
17
+ raise Sequel::Rollback if runner.call.failure?
18
18
  end
19
- }, &bl)
19
+ }, &dsl_bl)
20
20
  end
21
21
  end
22
22
 
23
- def after_commit(step_name = nil, &bl)
24
- fail 'must provide a step or a block but not both' if !step_name.nil? == block_given?
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(-> steps, state {
30
- dsl = self.class::DSL.new(State.new(self, state.to_h.dup), self)
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
- steps.call(dsl)
33
+ runner.call(dsl_copy)
34
34
  end
35
- }, &bl)
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 = 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
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
@@ -26,9 +26,7 @@ module Pathway
26
26
  authorized?(*objs) ? wrap(objs) : error(:forbidden)
27
27
  end
28
28
 
29
- def authorized?(*)
30
- true
31
- end
29
+ def authorized?(*) = true
32
30
  end
33
31
  end
34
32
  end
@@ -6,13 +6,8 @@ module Pathway
6
6
  attr_reader :value, :error
7
7
 
8
8
  class Success < Result
9
- def initialize(value)
10
- @value = value
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
- alias :value_for_deconstruct :value
23
+ alias_method :value_for_deconstruct, :value
29
24
  end
30
25
 
31
26
  class Failure < Result
32
- def initialize(error)
33
- @error = error
34
- end
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
- alias :value_for_deconstruct :error
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 deconstruct
59
- [value_for_deconstruct]
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
- alias :with_type :type
26
- alias :and_type :type
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
- alias :with_message :message
33
- alias :and_message :message
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
- alias :with_details :details
40
- alias :and_details :details
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" : '')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pathway
4
- VERSION = '0.12.2'
4
+ VERSION = '1.0.0'
5
5
  end
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
- ruby2_keywords def plugin(name, *args)
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, *args) if plugin.respond_to?(:apply)
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
- [type, message, details]
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,64 +56,61 @@ 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
- @hash[@result_key]
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
- def unwrap(&bl)
87
- raise 'a block must be provided' if !block_given?
88
- params = bl.parameters
89
- unless params.all? { |(type,_)| [:block, :key, :keyreq, :keyrest].member?(type) }
90
- raise 'only keyword arguments are supported for unwraping'
74
+ def use(&bl)
75
+ raise ArgumentError, 'a block must be provided' if !block_given?
76
+ if bl.parameters in [*, [:rest|:keyrest,], *]
77
+ raise ArgumentError, 'rest arguments are not supported'
91
78
  end
92
79
 
93
- if params.any? {|(type,_)| type == :keyrest }
94
- bl.call(**to_hash)
95
- else
96
- keys = params.select {|(type,_)| type == :key || type == :keyreq }.map(&:last)
80
+ keys = bl.parameters.select { _1 in :key|:keyreq, }.map(&:last)
81
+ names = bl.parameters.select { _1 in :req|:opt, }.map(&:last)
82
+
83
+ if keys.any? && names.any?
84
+ raise ArgumentError, 'cannot mix positional and keyword arguments'
85
+ elsif keys.any?
97
86
  bl.call(**to_hash.slice(*keys))
87
+ else
88
+ bl.call(*to_hash.values_at(*names))
98
89
  end
99
90
  end
100
91
 
101
92
  alias_method :to_h, :to_hash
102
- alias_method :u, :unwrap
93
+ alias_method :u, :use
94
+ alias_method :unwrap, :use
103
95
  end
104
96
 
105
97
  module Plugins
106
98
  module Base
107
99
  module ClassMethods
108
100
  attr_accessor :result_key
109
- alias :result_at :result_key=
101
+
102
+ alias_method :result_at, :result_key=
110
103
 
111
104
  def process(&bl)
112
105
  dsl = self::DSL
113
106
  define_method(:call) do |input|
114
- dsl.new(State.new(self, input: input), self)
107
+ dsl.new(State.new(self, input:), self)
115
108
  .run(&bl)
116
109
  .then(&:result)
117
110
  end
118
111
  end
119
112
 
120
- ruby2_keywords def call(ctx, *params)
121
- new(ctx).call(*params)
122
- end
113
+ def call(ctx,...) = new(ctx).call(...)
123
114
 
124
115
  def inherited(subclass)
125
116
  super
@@ -133,18 +124,16 @@ module Pathway
133
124
  delegate :result_key => 'self.class'
134
125
  delegate %i[result success failure] => Result
135
126
 
136
- alias :wrap :result
127
+ alias_method :wrap, :result
137
128
 
138
- def call(*)
139
- fail 'must implement at subclass'
140
- end
129
+ def call(*) = raise 'must implement at subclass'
141
130
 
142
131
  def error(type, message: nil, details: nil)
143
- failure(Error.new(type: type, message: message, details: details))
132
+ failure(Error.new(type:, message:, details:))
144
133
  end
145
134
 
146
135
  def wrap_if_present(value, type: :not_found, message: nil, details: {})
147
- value.nil? ? error(type, message: message, details: details) : success(value)
136
+ value.nil? ? error(type, message:, details:) : success(value)
148
137
  end
149
138
  end
150
139
 
@@ -164,45 +153,43 @@ module Pathway
164
153
  end
165
154
 
166
155
  # Execute step and preserve the former state
167
- ruby2_keywords def step(callable, *args)
156
+ def step(callable,...)
168
157
  bl = _callable(callable)
169
-
170
- @result = @result.tee { |state| bl.call(state, *args) }
158
+ @result = @result.tee { |state| bl.call(state,...) }
171
159
  end
172
160
 
173
161
  # Execute step and modify the former state setting the key
174
- def set(callable, *args, to: @operation.result_key)
162
+ def set(callable, *args, to: @operation.result_key, **kwargs, &bl)
175
163
  bl = _callable(callable)
176
164
 
177
165
  @result = @result.then do |state|
178
- wrap(bl.call(state, *args))
166
+ wrap(bl.call(state, *args, **kwargs, &bl))
179
167
  .then { |value| state.update(to => value) }
180
168
  end
181
169
  end
182
170
 
183
171
  # Execute step and replace the current state completely
184
- def map(callable)
172
+ def map(callable,...)
185
173
  bl = _callable(callable)
186
- @result = @result.then(bl)
174
+ @result = @result.then { |state| bl.call(state,...) }
187
175
  end
188
176
 
189
- def around(wrapper, &steps)
177
+ def around(execution_strategy, &dsl_block)
190
178
  @result.then do |state|
191
- seq = -> (dsl = self) { @result = dsl.run(&steps) }
192
- _callable(wrapper).call(seq, state)
179
+ dsl_runner = ->(dsl = self) { @result = dsl.run(&dsl_block) }
180
+
181
+ _callable(execution_strategy).call(dsl_runner, state)
193
182
  end
194
183
  end
195
184
 
196
- def if_true(cond, &steps)
185
+ def if_true(cond, &dsl_block)
197
186
  cond = _callable(cond)
198
- around(-> seq, state {
199
- seq.call if cond.call(state)
200
- }, &steps)
187
+ around(->(dsl_runner, state) { dsl_runner.call if cond.call(state) }, &dsl_block)
201
188
  end
202
189
 
203
- def if_false(cond, &steps)
190
+ def if_false(cond, &dsl_block)
204
191
  cond = _callable(cond)
205
- if_true(-> state { !cond.call(state) }, &steps)
192
+ if_true(->(state) { !cond.call(state) }, &dsl_block)
206
193
  end
207
194
 
208
195
  alias_method :sequence, :around
@@ -210,16 +197,14 @@ module Pathway
210
197
 
211
198
  private
212
199
 
213
- def wrap(obj)
214
- Result.result(obj)
215
- end
200
+ def wrap(obj) = Result.result(obj)
216
201
 
217
202
  def _callable(callable)
218
203
  case callable
219
204
  when Proc
220
- -> *args { @operation.instance_exec(*args, &callable) }.ruby2_keywords
205
+ ->(*args, **kwargs) { @operation.instance_exec(*args, **kwargs, &callable) }
221
206
  when Symbol
222
- -> *args { @operation.send(callable, *args) }.ruby2_keywords
207
+ ->(*args, **kwargs) { @operation.send(callable, *args, **kwargs) }
223
208
  else
224
209
  callable
225
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 = ">= 2.4.0"
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.4"
34
- spec.add_dependency "ruby2_keywords"
33
+ spec.add_dependency "contextualizer", "~> 0.1.0"
35
34
 
36
- spec.add_development_dependency "dry-validation", ">= 0.11"
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.12.2
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: 2024-08-06 00:00:00.000000000 Z
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.4
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.4
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.11'
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.11'
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: 2.4.0
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.5.10
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,86 +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_options
10
-
11
- def form(base = nil, **opts, &block)
12
- if block_given?
13
- base ||= _base_form
14
- self.form_class = _block_definition(base, opts, &block)
15
- elsif base
16
- self.form_class = _form_class(base)
17
- else
18
- raise ArgumentError, 'Either a form class or a block must be provided'
19
- end
20
- end
21
-
22
- def form_class= klass
23
- @builded_form = klass.options.empty? ? klass.new : nil
24
- @form_class = klass
25
- @form_options = klass.options.keys
26
- end
27
-
28
- def build_form(opts = {})
29
- @builded_form || form_class.new(opts)
30
- end
31
-
32
- def inherited(subclass)
33
- super
34
- subclass.form_class = form_class
35
- subclass.auto_wire_options = auto_wire_options
36
- end
37
-
38
- private
39
-
40
- def _base_form
41
- superclass.respond_to?(:form_class) ? superclass.form_class : Dry::Validation::Schema::Form
42
- end
43
-
44
- def _form_class(form)
45
- form.is_a?(Class) ? form : form.class
46
- end
47
-
48
- def _form_opts(opts = {})
49
- opts.merge(build: false)
50
- end
51
-
52
- def _block_definition(base, opts, &block)
53
- Dry::Validation.Form(_form_class(base), _form_opts(opts), &block)
54
- end
55
- end
56
-
57
- module InstanceMethods
58
- extend Forwardable
59
-
60
- delegate %i[build_form form_options auto_wire_options] => 'self.class'
61
- alias :form :build_form
62
-
63
- def validate(state, with: nil)
64
- if auto_wire_options && form_options.any?
65
- with ||= form_options.zip(form_options).to_h
66
- end
67
- opts = Hash(with).map { |opt, key| [opt, state[key]] }.to_h
68
- validate_with(state[:input], opts)
69
- .then { |params| state.update(params: params) }
70
- end
71
-
72
- def validate_with(params, opts = {})
73
- val = form(opts).call(params)
74
-
75
- val.success? ? wrap(val.output) : error(:validation, details: val.messages)
76
- end
77
- end
78
-
79
- def self.apply(operation, auto_wire_options: false)
80
- operation.form_class = Dry::Validation::Schema::Form
81
- operation.auto_wire_options = auto_wire_options
82
- end
83
- end
84
- end
85
- end
86
- end
@@ -1,86 +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_options
10
-
11
- def form(base = nil, **opts, &block)
12
- if block_given?
13
- base ||= _base_form
14
- self.form_class = _block_definition(base, opts, &block)
15
- elsif base
16
- self.form_class = _form_class(base)
17
- else
18
- raise ArgumentError, 'Either a form class or a block must be provided'
19
- end
20
- end
21
-
22
- def form_class= klass
23
- @builded_form = klass.options.empty? ? klass.new : nil
24
- @form_class = klass
25
- @form_options = klass.options.keys
26
- end
27
-
28
- def build_form(opts = {})
29
- @builded_form || form_class.new(opts)
30
- end
31
-
32
- def inherited(subclass)
33
- super
34
- subclass.form_class = form_class
35
- subclass.auto_wire_options = auto_wire_options
36
- end
37
-
38
- private
39
-
40
- def _base_form
41
- superclass.respond_to?(:form_class) ? superclass.form_class : Dry::Validation::Schema::Params
42
- end
43
-
44
- def _form_class(form)
45
- form.is_a?(Class) ? form : form.class
46
- end
47
-
48
- def _form_opts(opts = {})
49
- opts.merge(build: false)
50
- end
51
-
52
- def _block_definition(base, opts, &block)
53
- Dry::Validation.Params(_form_class(base), _form_opts(opts), &block)
54
- end
55
- end
56
-
57
- module InstanceMethods
58
- extend Forwardable
59
-
60
- delegate %i[build_form form_options auto_wire_options] => 'self.class'
61
- alias :form :build_form
62
-
63
- def validate(state, with: nil)
64
- if auto_wire_options && form_options.any?
65
- with ||= form_options.zip(form_options).to_h
66
- end
67
- opts = Hash(with).map { |opt, key| [opt, state[key]] }.to_h
68
- validate_with(state[:input], opts)
69
- .then { |params| state.update(params: params) }
70
- end
71
-
72
- def validate_with(params, opts = {})
73
- val = form(opts).call(params)
74
-
75
- val.success? ? wrap(val.output) : error(:validation, details: val.messages)
76
- end
77
- end
78
-
79
- def self.apply(operation, auto_wire_options: false)
80
- operation.form_class = Dry::Validation::Schema::Params
81
- operation.auto_wire_options = auto_wire_options
82
- end
83
- end
84
- end
85
- end
86
- end
@@ -1,78 +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_options
10
-
11
- def contract(base = nil, &block)
12
- if block_given?
13
- base ||= _base_contract
14
- self.contract_class = Class.new(base, &block)
15
- elsif base
16
- self.contract_class = base
17
- else
18
- raise ArgumentError, 'Either a contract class or a block must be provided'
19
- end
20
- end
21
-
22
- ruby2_keywords def params(*args, &block)
23
- contract { params(*args, &block) }
24
- end
25
-
26
- def contract_class= klass
27
- @contract_class = klass
28
- @contract_options = (klass.dry_initializer.options - Dry::Validation::Contract.dry_initializer.options).map(&:target)
29
- @builded_contract = @contract_options.empty? && klass.schema ? klass.new : nil
30
- end
31
-
32
- def build_contract(**opts)
33
- @builded_contract || contract_class.new(**opts)
34
- end
35
-
36
- def inherited(subclass)
37
- super
38
- subclass.contract_class = contract_class
39
- subclass.auto_wire_options = auto_wire_options
40
- end
41
-
42
- private
43
-
44
- def _base_contract
45
- superclass.respond_to?(:contract_class) ? superclass.contract_class : Dry::Validation::Contract
46
- end
47
- end
48
-
49
- module InstanceMethods
50
- extend Forwardable
51
-
52
- delegate %i[build_contract contract_options auto_wire_options] => 'self.class'
53
- alias :contract :build_contract
54
-
55
- def validate(state, with: nil)
56
- if auto_wire_options && contract_options.any?
57
- with ||= contract_options.zip(contract_options).to_h
58
- end
59
- opts = Hash(with).map { |to, from| [to, state[from]] }.to_h
60
- validate_with(state[:input], **opts)
61
- .then { |params| state.update(params: params) }
62
- end
63
-
64
- def validate_with(input, **opts)
65
- result = contract(**opts).call(input)
66
-
67
- result.success? ? wrap(result.values.to_h) : error(:validation, details: result.errors.to_h)
68
- end
69
- end
70
-
71
- def self.apply(operation, auto_wire_options: false)
72
- operation.contract_class = Dry::Validation::Contract
73
- operation.auto_wire_options = auto_wire_options
74
- end
75
- end
76
- end
77
- end
78
- end