pathway 0.12.3 → 1.1.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 +14 -0
- data/README.md +0 -4
- data/bin/bundle +109 -0
- data/bin/byebug +13 -3
- data/bin/coderay +27 -0
- data/bin/erb +27 -0
- data/bin/htmldiff +27 -0
- data/bin/irb +27 -0
- data/bin/ldiff +27 -0
- data/bin/pry +27 -0
- data/bin/rake +13 -3
- data/bin/rbs +27 -0
- data/bin/rdbg +27 -0
- data/bin/rdoc +27 -0
- data/bin/ri +27 -0
- data/bin/rspec +13 -3
- data/bin/ruby-lsp +27 -0
- data/bin/ruby-lsp-check +27 -0
- data/bin/ruby-lsp-launcher +27 -0
- data/bin/ruby-lsp-test-exec +27 -0
- data/bin/sequel +27 -0
- data/bin/yard +27 -0
- data/bin/yardoc +27 -0
- data/bin/yri +27 -0
- 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 +5 -7
- data/lib/pathway/plugins/sequel_models.rb +35 -25
- data/lib/pathway/plugins/simple_auth.rb +1 -3
- data/lib/pathway/result.rb +18 -44
- data/lib/pathway/rspec/matchers/fail_on.rb +6 -6
- data/lib/pathway/version.rb +1 -1
- data/lib/pathway.rb +42 -59
- data/pathway.gemspec +5 -4
- metadata +55 -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
data/bin/sequel
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'sequel' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../.ruby-lsp/Gemfile", __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require "bundler/setup"
|
26
|
+
|
27
|
+
load Gem.bin_path("sequel", "sequel")
|
data/bin/yard
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'yard' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../.ruby-lsp/Gemfile", __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require "bundler/setup"
|
26
|
+
|
27
|
+
load Gem.bin_path("yard", "yard")
|
data/bin/yardoc
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'yardoc' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../.ruby-lsp/Gemfile", __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require "bundler/setup"
|
26
|
+
|
27
|
+
load Gem.bin_path("yard", "yardoc")
|
data/bin/yri
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'yri' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../.ruby-lsp/Gemfile", __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require "bundler/setup"
|
26
|
+
|
27
|
+
load Gem.bin_path("yard", "yri")
|
@@ -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, &)
|
16
|
+
if block_given?
|
17
|
+
base ||= _base_contract
|
18
|
+
self.contract_class = Class.new(base, &)
|
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(...)
|
27
|
+
contract { params(...) }
|
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(**)
|
37
|
+
@builded_contract || contract_class.new(**)
|
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, **)
|
69
|
+
result = contract(**).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,15 +4,15 @@ 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
|
12
12
|
|
13
13
|
class Responder
|
14
|
-
def self.respond(
|
15
|
-
r = new(
|
14
|
+
def self.respond(...)
|
15
|
+
r = new(...)
|
16
16
|
r.respond
|
17
17
|
end
|
18
18
|
|
@@ -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,43 @@ 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, if: nil, unless: nil, &steps)
|
10
|
+
_with_db_steps(steps, step_name, *_opts_if_unless(binding)) do |runner|
|
11
|
+
db.transaction(savepoint: true) do
|
12
|
+
raise Sequel::Rollback if runner.call.failure?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
db.transaction(savepoint: true) do
|
17
|
-
raise Sequel::Rollback if steps.call.failure?
|
18
|
-
end
|
19
|
-
}, &bl)
|
17
|
+
def after_commit(step_name = nil, if: nil, unless: nil, &steps)
|
18
|
+
_with_db_steps(steps, step_name, *_opts_if_unless(binding)) do |runner, state|
|
19
|
+
dsl_copy = _dsl_for(state)
|
20
|
+
db.after_commit { runner.call(dsl_copy) }
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
|
24
|
+
def after_rollback(step_name = nil, if: nil, unless: nil, &steps)
|
25
|
+
_with_db_steps(steps, step_name, *_opts_if_unless(binding)) do |runner, state|
|
26
|
+
dsl_copy = _dsl_for(state)
|
27
|
+
db.after_rollback(savepoint: true) { runner.call(dsl_copy) }
|
28
|
+
end
|
29
|
+
end
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
around(-> steps, state {
|
30
|
-
dsl = self.class::DSL.new(State.new(self, state.to_h.dup), self)
|
31
|
+
private
|
32
|
+
|
33
|
+
def _opts_if_unless(bg) = %i[if unless].map { bg.local_variable_get(_1) }
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
def _with_db_steps(steps, step_name=nil, if_cond=nil, unless_cond=nil, &db_logic)
|
36
|
+
raise ArgumentError, 'options :if and :unless are mutually exclusive' if if_cond && unless_cond
|
37
|
+
raise ArgumentError, 'must provide either a step or a block but not both' if !!step_name == !!steps
|
38
|
+
steps ||= proc { step step_name }
|
39
|
+
|
40
|
+
if if_cond
|
41
|
+
if_true(if_cond) { _with_db_steps(steps, &db_logic) }
|
42
|
+
elsif unless_cond
|
43
|
+
if_false(unless_cond) { _with_db_steps(steps, &db_logic) }
|
44
|
+
else
|
45
|
+
around(db_logic, &steps)
|
36
46
|
end
|
37
47
|
end
|
38
48
|
end
|
@@ -41,10 +51,10 @@ module Pathway
|
|
41
51
|
attr_accessor :model_class, :search_field, :model_not_found
|
42
52
|
|
43
53
|
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
|
54
|
+
self.model_class = model_class
|
55
|
+
self.search_field = search_by
|
56
|
+
self.result_key = Inflector.underscore(Inflector.demodulize(model_class.name)).to_sym if set_result_key
|
57
|
+
self.model_not_found = error_message || "#{Inflector.humanize(Inflector.underscore(Inflector.demodulize(model_class.name)))} not found".freeze
|
48
58
|
|
49
59
|
self.context(result_key => Contextualizer::OPTIONAL) if set_result_key && set_context_param
|
50
60
|
end
|
data/lib/pathway/result.rb
CHANGED
@@ -6,48 +6,32 @@ 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))
|
19
14
|
end
|
20
15
|
|
21
|
-
def tee(
|
22
|
-
follow = self.then(
|
16
|
+
def tee(...)
|
17
|
+
follow = self.then(...)
|
23
18
|
follow.failure? ? follow : self
|
24
19
|
end
|
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