resol 0.9.0 → 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/test.yml +2 -2
- data/.rubocop.yml +1 -1
- data/Gemfile +3 -0
- data/Gemfile.lock +94 -79
- data/README.md +46 -8
- data/lib/resol/injector.rb +47 -0
- data/lib/resol/plugins/dummy.rb +7 -0
- data/lib/resol/plugins/return_in_service.rb +27 -0
- data/lib/resol/plugins.rb +57 -0
- data/lib/resol/result.rb +39 -28
- data/lib/resol/service.rb +76 -30
- data/lib/resol/version.rb +1 -1
- data/lib/resol.rb +9 -4
- metadata +11 -14
- data/lib/resol/configuration.rb +0 -43
- data/lib/resol/return_engine/catch.rb +0 -24
- data/lib/resol/return_engine/return.rb +0 -21
- data/lib/resol/return_engine.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7a4e82d0b30c1d86f47db975689b82768d672fd2c2db7088d2ac41a68346b90
|
|
4
|
+
data.tar.gz: 838c52bf67feb5442213b6e485a3f024ee8323e5c36618492743ad761455dc7e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e5bf12a60de921e46f762c74d9dac977b6761d045a214895788dfc5374e19977563c73aa737cc8ebe691dd2b4034f99a2fc78d6ad2df1479232cf0fb57ce7842
|
|
7
|
+
data.tar.gz: 4aedc9b8ca631ce5b2e1c8cb79eb1eea49d0aa2085e162fbf4f2fb671dd58e860be66f2c90fd42241fbd81f932775700f733c631f3dd97b53c4d1f8ce5d481d7
|
data/.github/workflows/test.yml
CHANGED
|
@@ -16,7 +16,7 @@ jobs:
|
|
|
16
16
|
- uses: actions/checkout@v2
|
|
17
17
|
- uses: ruby/setup-ruby@v1
|
|
18
18
|
with:
|
|
19
|
-
ruby-version: "3.
|
|
19
|
+
ruby-version: "3.4"
|
|
20
20
|
bundler-cache: true
|
|
21
21
|
- name: Run Linter
|
|
22
22
|
run: bundle exec ci-helper RubocopLint
|
|
@@ -43,7 +43,7 @@ jobs:
|
|
|
43
43
|
strategy:
|
|
44
44
|
fail-fast: false
|
|
45
45
|
matrix:
|
|
46
|
-
ruby: ["
|
|
46
|
+
ruby: ["3.1", "3.2", "3.3"]
|
|
47
47
|
experimental: [false]
|
|
48
48
|
include:
|
|
49
49
|
- ruby: head
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,118 +1,130 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
resol (0.
|
|
5
|
-
|
|
4
|
+
resol (1.0.0)
|
|
5
|
+
dry-configurable (~> 1.2.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
activesupport (7.
|
|
10
|
+
activesupport (7.2.2.1)
|
|
11
11
|
base64
|
|
12
|
+
benchmark (>= 0.3)
|
|
12
13
|
bigdecimal
|
|
13
|
-
concurrent-ruby (~> 1.0, >= 1.
|
|
14
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
14
15
|
connection_pool (>= 2.2.5)
|
|
15
16
|
drb
|
|
16
17
|
i18n (>= 1.6, < 2)
|
|
18
|
+
logger (>= 1.4.2)
|
|
17
19
|
minitest (>= 5.1)
|
|
18
|
-
|
|
19
|
-
tzinfo (~> 2.0)
|
|
20
|
+
securerandom (>= 0.3)
|
|
21
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
20
22
|
ast (2.4.2)
|
|
21
23
|
base64 (0.2.0)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
benchmark (0.4.0)
|
|
25
|
+
bigdecimal (3.1.9)
|
|
26
|
+
bundler-audit (0.9.2)
|
|
24
27
|
bundler (>= 1.2.0, < 3)
|
|
25
28
|
thor (~> 1.0)
|
|
26
|
-
ci-helper (0.
|
|
27
|
-
colorize (~>
|
|
28
|
-
dry-inflector (~> 0
|
|
29
|
-
umbrellio-sequel-plugins (~> 0.
|
|
29
|
+
ci-helper (0.7.0)
|
|
30
|
+
colorize (~> 1.1)
|
|
31
|
+
dry-inflector (~> 1.0)
|
|
32
|
+
umbrellio-sequel-plugins (~> 0.14)
|
|
30
33
|
coderay (1.1.3)
|
|
31
|
-
colorize (
|
|
32
|
-
concurrent-ruby (1.
|
|
33
|
-
connection_pool (2.
|
|
34
|
-
diff-lcs (1.5.
|
|
35
|
-
docile (1.4.
|
|
36
|
-
drb (2.2.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
colorize (1.1.0)
|
|
35
|
+
concurrent-ruby (1.3.4)
|
|
36
|
+
connection_pool (2.5.0)
|
|
37
|
+
diff-lcs (1.5.1)
|
|
38
|
+
docile (1.4.1)
|
|
39
|
+
drb (2.2.1)
|
|
40
|
+
dry-configurable (1.2.0)
|
|
41
|
+
dry-core (~> 1.0, < 2)
|
|
42
|
+
zeitwerk (~> 2.6)
|
|
43
|
+
dry-core (1.1.0)
|
|
40
44
|
concurrent-ruby (~> 1.0)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
logger
|
|
46
|
+
zeitwerk (~> 2.6)
|
|
47
|
+
dry-inflector (1.2.0)
|
|
48
|
+
dry-initializer (3.2.0)
|
|
49
|
+
i18n (1.14.6)
|
|
50
|
+
concurrent-ruby (~> 1.0)
|
|
51
|
+
json (2.9.1)
|
|
52
|
+
language_server-protocol (3.17.0.3)
|
|
53
|
+
logger (1.6.5)
|
|
54
|
+
method_source (1.1.0)
|
|
55
|
+
minitest (5.25.4)
|
|
56
|
+
parallel (1.26.3)
|
|
57
|
+
parser (3.3.6.0)
|
|
47
58
|
ast (~> 2.4.1)
|
|
48
59
|
racc
|
|
49
|
-
pry (0.
|
|
60
|
+
pry (0.15.2)
|
|
50
61
|
coderay (~> 1.1)
|
|
51
62
|
method_source (~> 1.0)
|
|
52
|
-
qonfig (0.
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
qonfig (0.30.0)
|
|
64
|
+
base64 (>= 0.2)
|
|
65
|
+
racc (1.8.1)
|
|
66
|
+
rack (3.1.8)
|
|
55
67
|
rainbow (3.1.1)
|
|
56
|
-
rake (13.1
|
|
57
|
-
regexp_parser (2.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
rspec-
|
|
61
|
-
rspec-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
rspec-expectations (3.12.3)
|
|
68
|
+
rake (13.2.1)
|
|
69
|
+
regexp_parser (2.10.0)
|
|
70
|
+
rspec (3.13.0)
|
|
71
|
+
rspec-core (~> 3.13.0)
|
|
72
|
+
rspec-expectations (~> 3.13.0)
|
|
73
|
+
rspec-mocks (~> 3.13.0)
|
|
74
|
+
rspec-core (3.13.2)
|
|
75
|
+
rspec-support (~> 3.13.0)
|
|
76
|
+
rspec-expectations (3.13.3)
|
|
66
77
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
67
|
-
rspec-support (~> 3.
|
|
68
|
-
rspec-mocks (3.
|
|
78
|
+
rspec-support (~> 3.13.0)
|
|
79
|
+
rspec-mocks (3.13.2)
|
|
69
80
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
70
|
-
rspec-support (~> 3.
|
|
71
|
-
rspec-support (3.
|
|
72
|
-
rubocop (1.
|
|
81
|
+
rspec-support (~> 3.13.0)
|
|
82
|
+
rspec-support (3.13.2)
|
|
83
|
+
rubocop (1.69.2)
|
|
73
84
|
json (~> 2.3)
|
|
85
|
+
language_server-protocol (>= 3.17.0)
|
|
74
86
|
parallel (~> 1.10)
|
|
75
|
-
parser (>= 3.
|
|
87
|
+
parser (>= 3.3.0.2)
|
|
76
88
|
rainbow (>= 2.2.2, < 4.0)
|
|
77
|
-
regexp_parser (>=
|
|
78
|
-
|
|
79
|
-
rubocop-ast (>= 1.28.0, < 2.0)
|
|
89
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
90
|
+
rubocop-ast (>= 1.36.2, < 2.0)
|
|
80
91
|
ruby-progressbar (~> 1.7)
|
|
81
|
-
unicode-display_width (>= 2.4.0, <
|
|
82
|
-
rubocop-ast (1.
|
|
83
|
-
parser (>= 3.
|
|
84
|
-
rubocop-
|
|
85
|
-
rubocop (~> 1.
|
|
86
|
-
|
|
87
|
-
rubocop (~> 1.
|
|
88
|
-
rubocop-
|
|
89
|
-
rubocop-rails (~> 2.19.0)
|
|
92
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
93
|
+
rubocop-ast (1.37.0)
|
|
94
|
+
parser (>= 3.3.1.0)
|
|
95
|
+
rubocop-config-umbrellio (1.69.0.101)
|
|
96
|
+
rubocop (~> 1.69.0)
|
|
97
|
+
rubocop-factory_bot (~> 2.26.0)
|
|
98
|
+
rubocop-performance (~> 1.23.0)
|
|
99
|
+
rubocop-rails (~> 2.28.0)
|
|
90
100
|
rubocop-rake (~> 0.6.0)
|
|
91
|
-
rubocop-rspec (~>
|
|
92
|
-
rubocop-sequel (~> 0.3.
|
|
93
|
-
rubocop-
|
|
94
|
-
rubocop (
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
rubocop-rspec (~> 3.3.0)
|
|
102
|
+
rubocop-sequel (~> 0.3.0)
|
|
103
|
+
rubocop-factory_bot (2.26.1)
|
|
104
|
+
rubocop (~> 1.61)
|
|
105
|
+
rubocop-performance (1.23.1)
|
|
106
|
+
rubocop (>= 1.48.1, < 2.0)
|
|
107
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
|
108
|
+
rubocop-rails (2.28.0)
|
|
97
109
|
activesupport (>= 4.2.0)
|
|
98
110
|
rack (>= 1.1)
|
|
99
|
-
rubocop (>= 1.
|
|
111
|
+
rubocop (>= 1.52.0, < 2.0)
|
|
112
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
|
100
113
|
rubocop-rake (0.6.0)
|
|
101
114
|
rubocop (~> 1.0)
|
|
102
|
-
rubocop-rspec (
|
|
103
|
-
rubocop (~> 1.
|
|
104
|
-
|
|
105
|
-
rubocop-sequel (0.3.4)
|
|
115
|
+
rubocop-rspec (3.3.0)
|
|
116
|
+
rubocop (~> 1.61)
|
|
117
|
+
rubocop-sequel (0.3.8)
|
|
106
118
|
rubocop (~> 1.0)
|
|
107
119
|
ruby-progressbar (1.13.0)
|
|
108
|
-
|
|
109
|
-
sequel (5.
|
|
120
|
+
securerandom (0.4.1)
|
|
121
|
+
sequel (5.88.0)
|
|
110
122
|
bigdecimal
|
|
111
123
|
simplecov (0.22.0)
|
|
112
124
|
docile (~> 1.1)
|
|
113
125
|
simplecov-html (~> 0.11)
|
|
114
126
|
simplecov_json_formatter (~> 0.1)
|
|
115
|
-
simplecov-html (0.
|
|
127
|
+
simplecov-html (0.13.1)
|
|
116
128
|
simplecov-lcov (0.8.0)
|
|
117
129
|
simplecov_json_formatter (0.1.4)
|
|
118
130
|
smart_engine (0.17.0)
|
|
@@ -122,14 +134,15 @@ GEM
|
|
|
122
134
|
smart_types (~> 0.8)
|
|
123
135
|
smart_types (0.8.0)
|
|
124
136
|
smart_engine (~> 0.11)
|
|
125
|
-
|
|
126
|
-
thor (1.3.0)
|
|
137
|
+
thor (1.3.2)
|
|
127
138
|
tzinfo (2.0.6)
|
|
128
139
|
concurrent-ruby (~> 1.0)
|
|
129
|
-
umbrellio-sequel-plugins (0.
|
|
140
|
+
umbrellio-sequel-plugins (0.17.0)
|
|
130
141
|
sequel
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
unicode-display_width (3.1.3)
|
|
143
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
|
144
|
+
unicode-emoji (4.0.4)
|
|
145
|
+
zeitwerk (2.6.18)
|
|
133
146
|
|
|
134
147
|
PLATFORMS
|
|
135
148
|
arm64-darwin-21
|
|
@@ -142,6 +155,7 @@ PLATFORMS
|
|
|
142
155
|
DEPENDENCIES
|
|
143
156
|
bundler-audit
|
|
144
157
|
ci-helper
|
|
158
|
+
dry-initializer
|
|
145
159
|
pry
|
|
146
160
|
rake
|
|
147
161
|
resol!
|
|
@@ -149,6 +163,7 @@ DEPENDENCIES
|
|
|
149
163
|
rubocop-config-umbrellio
|
|
150
164
|
simplecov
|
|
151
165
|
simplecov-lcov
|
|
166
|
+
smart_initializer
|
|
152
167
|
|
|
153
168
|
BUNDLED WITH
|
|
154
|
-
2.
|
|
169
|
+
2.6.2
|
data/README.md
CHANGED
|
@@ -54,7 +54,38 @@ and method `.call!` returns a value or throws an error (in case of `fail!` has b
|
|
|
54
54
|
|
|
55
55
|
#### Params defining
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
`Resol` supports two gems, which provide abstract initialization flow for classes:
|
|
58
|
+
|
|
59
|
+
1. [smart_initializer](https://github.com/smart-rb/smart_initializer), which was default provider for a very long time.
|
|
60
|
+
2. [dry-initializer](https://dry-rb.org/gems/dry-initializer/3.1), additional provider with DSL almost identical to the smart_core's DSL.
|
|
61
|
+
|
|
62
|
+
There is an _important restriction_ on using different initializers in different services.
|
|
63
|
+
Descendants of a parent, into which initializer logic has already been imported, cannot override the provider
|
|
64
|
+
|
|
65
|
+
You can use both providers for a different services:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
# Types is a namespace for all types, defined by smart_types.
|
|
69
|
+
class FirstService < Resol::Service
|
|
70
|
+
inject_initializer :smartcore_injector
|
|
71
|
+
|
|
72
|
+
param :first, Types::String
|
|
73
|
+
param :second, Types::Integer
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Types is a namespace for all types, defined by dry-types.
|
|
77
|
+
class SecondService < Resol::Service
|
|
78
|
+
inject_initializer :dry_injector
|
|
79
|
+
|
|
80
|
+
param :first, Types::Strict::String
|
|
81
|
+
param :second, Types::Strict::Integer
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Both initializers support inheritance. And base features for initialization flow
|
|
86
|
+
like default value, arguments accessors visibility level, coercible attributes and so on.
|
|
87
|
+
|
|
88
|
+
List of all supported initializers you can see at `DependencyContainer` definition.
|
|
58
89
|
|
|
59
90
|
#### Return a result
|
|
60
91
|
|
|
@@ -95,13 +126,15 @@ end
|
|
|
95
126
|
|
|
96
127
|
Methods:
|
|
97
128
|
|
|
98
|
-
- `success?`
|
|
99
|
-
- `failure?`
|
|
100
|
-
- `value!`
|
|
101
|
-
- `value_or(other_value, &block)`
|
|
102
|
-
- `error`
|
|
103
|
-
- `or(&block)`
|
|
104
|
-
- `either(success_proc, failure_proc)`
|
|
129
|
+
- `success?` — returns `true` for success result and `false` for failure result
|
|
130
|
+
- `failure?` — returns `true` for failure result and `false` for success result
|
|
131
|
+
- `value!` — unwraps a result object, returns the value for success result, and throws an error for failure result
|
|
132
|
+
- `value_or(other_value, &block)` — returns a value for success result or `other_value` for failure result (either calls `block` in case it given)
|
|
133
|
+
- `error` — returns `nil` for success result and error object (with code and data) for failure result
|
|
134
|
+
- `or(&block)` — calls block for failure result, for success result does nothing
|
|
135
|
+
- `either(success_proc, failure_proc)` — for success result calls success_proc with result value in args, for failure result calls failure_proc with error in args.
|
|
136
|
+
- `bind` — using with `block` for success result resolve value and pass it to the `block`, used to chain multiple monads. Block can return anything. Failure result ignore block and return `self`.
|
|
137
|
+
- `fmap` — like the `bind`, but wraps value returned by block by success monad.
|
|
105
138
|
|
|
106
139
|
### Error object
|
|
107
140
|
|
|
@@ -115,6 +148,11 @@ methods `code` and `data`.
|
|
|
115
148
|
Configuration constant references to `SmartCore::Initializer::Configuration`. You can read
|
|
116
149
|
about available configuration options [here](https://github.com/smart-rb/smart_initializer#configuration).
|
|
117
150
|
|
|
151
|
+
### Plugin System
|
|
152
|
+
|
|
153
|
+
Resol implements the basic logic of using plugins to extend and change the base service class.
|
|
154
|
+
You can write your own plugin and applie it by calling `Resol::Service#plugin(plugin_name)`.
|
|
155
|
+
|
|
118
156
|
## Development
|
|
119
157
|
|
|
120
158
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run the tests.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resol
|
|
4
|
+
# The Injector class is responsible for injecting initialization logic provider.
|
|
5
|
+
#
|
|
6
|
+
# Supported providers:
|
|
7
|
+
# - :smart -> uses the `smart_core/initializer`
|
|
8
|
+
# - :dry -> uses the `dry-initializer`
|
|
9
|
+
class Injector
|
|
10
|
+
InjectMarker = Module.new
|
|
11
|
+
NAME_TO_METHOD_MAPPING = {
|
|
12
|
+
smart: :inject_smart,
|
|
13
|
+
dry: :inject_dry,
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def self.inject_smart(service)
|
|
17
|
+
require "smart_core/initializer"
|
|
18
|
+
|
|
19
|
+
service.include(SmartCore::Initializer)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.inject_dry(service)
|
|
23
|
+
require "dry/initializer"
|
|
24
|
+
|
|
25
|
+
service.extend(Dry::Initializer)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(initializer_name)
|
|
29
|
+
self.called_method = NAME_TO_METHOD_MAPPING.fetch(initializer_name.to_sym)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def inject!(service_class)
|
|
33
|
+
error!("parent or this class already injected") if service_class.include?(InjectMarker)
|
|
34
|
+
|
|
35
|
+
self.class.public_send(called_method, service_class)
|
|
36
|
+
service_class.include(InjectMarker)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
attr_accessor :called_method
|
|
42
|
+
|
|
43
|
+
def error!(msg)
|
|
44
|
+
raise msg
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resol
|
|
4
|
+
module Plugins
|
|
5
|
+
module ReturnInService
|
|
6
|
+
module ClassMethods
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def handle_catch(_service)
|
|
10
|
+
yield
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module InstanceMethods
|
|
15
|
+
module PrependedMethods
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def proceed_return(_service, data) = data
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.included(service)
|
|
22
|
+
service.prepend(PrependedMethods)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module Resol
|
|
6
|
+
module Plugins
|
|
7
|
+
PLUGINS_PATH = Pathname("resol/plugins")
|
|
8
|
+
|
|
9
|
+
class Manager
|
|
10
|
+
def self.resolve_module(module_name)
|
|
11
|
+
Plugins.const_get(module_name)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
self.allowed_classes = resolve_allowed_classes
|
|
16
|
+
self.plugins = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def plugin(caller_class, plugin_name)
|
|
20
|
+
plugin_name = plugin_name.to_s
|
|
21
|
+
|
|
22
|
+
return unless allowed_classes.include?(caller_class)
|
|
23
|
+
return if plugins.include?(plugin_name)
|
|
24
|
+
|
|
25
|
+
plugin_module = find_plugin_module(plugin_name)
|
|
26
|
+
if defined?(plugin_module::InstanceMethods)
|
|
27
|
+
caller_class.include(plugin_module::InstanceMethods)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if defined?(plugin_module::ClassMethods)
|
|
31
|
+
caller_class.extend(plugin_module::ClassMethods)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
plugins << plugin_name
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_accessor :allowed_classes, :plugins
|
|
40
|
+
|
|
41
|
+
def resolve_allowed_classes
|
|
42
|
+
Resol.config.classes_allowed_to_patch.map { |name| Object.const_get(name) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def find_plugin_module(plugin_name)
|
|
46
|
+
require PLUGINS_PATH.join(plugin_name)
|
|
47
|
+
self.class.resolve_module(classify_plugin_name(plugin_name))
|
|
48
|
+
rescue LoadError, NameError => e
|
|
49
|
+
raise ArgumentError, "Failed to load plugin '#{plugin_name}': #{e.message}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def classify_plugin_name(string)
|
|
53
|
+
string.split(/_|-/).map!(&:capitalize).join
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/resol/result.rb
CHANGED
|
@@ -3,26 +3,13 @@
|
|
|
3
3
|
module Resol
|
|
4
4
|
class UnwrapError < StandardError; end
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# @!method value_or
|
|
10
|
-
# @!method value!
|
|
11
|
-
|
|
12
|
-
def initialize(*); end
|
|
13
|
-
|
|
14
|
-
def or
|
|
15
|
-
yield(@value) if failure?
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def either(success_proc, failure_proc)
|
|
19
|
-
success? ? success_proc.call(@value) : failure_proc.call(@value)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
6
|
+
# rubocop:disable Lint/EmptyClass
|
|
7
|
+
class Result; end
|
|
8
|
+
# rubocop:enable Lint/EmptyClass
|
|
22
9
|
|
|
23
10
|
class Success < Result
|
|
24
11
|
def initialize(value)
|
|
25
|
-
super
|
|
12
|
+
super()
|
|
26
13
|
@value = value
|
|
27
14
|
end
|
|
28
15
|
|
|
@@ -34,7 +21,7 @@ module Resol
|
|
|
34
21
|
false
|
|
35
22
|
end
|
|
36
23
|
|
|
37
|
-
def value_or(
|
|
24
|
+
def value_or(_other_value = nil)
|
|
38
25
|
@value
|
|
39
26
|
end
|
|
40
27
|
|
|
@@ -42,14 +29,26 @@ module Resol
|
|
|
42
29
|
@value
|
|
43
30
|
end
|
|
44
31
|
|
|
45
|
-
def error
|
|
46
|
-
|
|
32
|
+
def error = nil
|
|
33
|
+
|
|
34
|
+
def or = nil
|
|
35
|
+
|
|
36
|
+
def either(success_proc, _failure_proc)
|
|
37
|
+
success_proc.call(@value)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def bind
|
|
41
|
+
yield @value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fmap(&)
|
|
45
|
+
Resol.Success(bind(&))
|
|
47
46
|
end
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
class Failure < Result
|
|
51
50
|
def initialize(error)
|
|
52
|
-
super
|
|
51
|
+
super()
|
|
53
52
|
@value = error
|
|
54
53
|
end
|
|
55
54
|
|
|
@@ -62,11 +61,7 @@ module Resol
|
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
def value_or(other_value = nil)
|
|
65
|
-
|
|
66
|
-
yield(@value)
|
|
67
|
-
else
|
|
68
|
-
other_value
|
|
69
|
-
end
|
|
64
|
+
block_given? ? yield(@value) : other_value
|
|
70
65
|
end
|
|
71
66
|
|
|
72
67
|
def value!
|
|
@@ -76,13 +71,29 @@ module Resol
|
|
|
76
71
|
def error
|
|
77
72
|
@value
|
|
78
73
|
end
|
|
74
|
+
|
|
75
|
+
def or
|
|
76
|
+
yield @value
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def either(_success_proc, failure_proc)
|
|
80
|
+
failure_proc.call(@value)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def bind = self
|
|
84
|
+
|
|
85
|
+
alias fmap bind
|
|
79
86
|
end
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
# TODO: Should be in a module, which includes in classes.
|
|
89
|
+
# Example;
|
|
90
|
+
# rubocop:disable Naming/MethodName
|
|
91
|
+
def Success(...)
|
|
82
92
|
Success.new(...)
|
|
83
93
|
end
|
|
84
94
|
|
|
85
|
-
def
|
|
95
|
+
def Failure(...)
|
|
86
96
|
Failure.new(...)
|
|
87
97
|
end
|
|
98
|
+
# rubocop:enable Naming/MethodName
|
|
88
99
|
end
|
data/lib/resol/service.rb
CHANGED
|
@@ -27,7 +27,6 @@ module Resol
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
include SmartCore::Initializer
|
|
31
30
|
include Resol::Builder
|
|
32
31
|
include Resol::Callbacks
|
|
33
32
|
|
|
@@ -39,62 +38,109 @@ module Resol
|
|
|
39
38
|
super
|
|
40
39
|
end
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
# Allows you to configure an initializer library for this service,
|
|
42
|
+
# such as `dry-initializer` or `smart_core/initializer`.
|
|
43
|
+
#
|
|
44
|
+
# @param initializer_name [String, Symbol]
|
|
45
|
+
# The name of the initializer (e.g., `:dry` or `:smart`).
|
|
46
|
+
# @return [void]
|
|
47
|
+
def use_initializer!(initializer_name)
|
|
48
|
+
Resol::Injector.new(initializer_name).inject!(self)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def plugin(...)
|
|
52
|
+
manager.plugin(self, ...)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Calls the service using class-level invocation. Builds the service object,
|
|
56
|
+
# runs any callbacks, and invokes the `#call` instance method.
|
|
57
|
+
#
|
|
58
|
+
# @param args [Array] Positional arguments for building service instance.
|
|
59
|
+
# @param kwargs [Hash] Keyword arguments for building service instance.
|
|
60
|
+
# @yield [block] An optional block passed into service's `#call`.
|
|
61
|
+
#
|
|
62
|
+
# @return [Resol::Success, Resol::Failure]
|
|
63
|
+
# Returns a `Resol::Success` if the service completed via `success!`,
|
|
64
|
+
# or a `Resol::Failure` if exited with`fail!` calling.
|
|
65
|
+
#
|
|
66
|
+
# @raise [InvalidCommandImplementation]
|
|
67
|
+
# If neither `#success!` nor `#fail!` is called inside the `#call` method.
|
|
68
|
+
def call(*args, **kwargs, &)
|
|
43
69
|
service = build(*args, **kwargs)
|
|
44
70
|
|
|
45
|
-
result =
|
|
71
|
+
result = handle_catch(service) do
|
|
46
72
|
service.instance_variable_set(:@__performing__, true)
|
|
47
73
|
__run_callbacks__(service)
|
|
48
|
-
service.call(&
|
|
74
|
+
service.call(&)
|
|
49
75
|
end
|
|
76
|
+
return Resol::Success(result.data) if service.__result_method__called__
|
|
50
77
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
raise InvalidCommandImplementation, error_message
|
|
54
|
-
else
|
|
55
|
-
Resol::Success(result.data)
|
|
56
|
-
end
|
|
78
|
+
error_message = "No `#success!` or `#fail!` called in `#call` method in #{service.class}."
|
|
79
|
+
raise InvalidCommandImplementation, error_message
|
|
57
80
|
rescue self::Failure => e
|
|
58
81
|
Resol::Failure(e)
|
|
59
82
|
end
|
|
60
83
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
# Same as {call}, but attempts to resolve value from monad.
|
|
85
|
+
# If the result is a failure, it raises the error instead of returning a `Resol::Failure`.
|
|
86
|
+
#
|
|
87
|
+
# @param args [Array] Positional arguments for building the service instance.
|
|
88
|
+
# @param kwargs [Hash] Keyword arguments for building the service instance.
|
|
89
|
+
# @yield [block] An optional block passed to the service's `#call`.
|
|
90
|
+
#
|
|
91
|
+
# @return [Object]
|
|
92
|
+
# Returns a value, with which instance of service interrupts with {#success!}.
|
|
93
|
+
#
|
|
94
|
+
# @raise [self::Failure]
|
|
95
|
+
# Raises an error if the service fails.
|
|
65
96
|
def call!(...)
|
|
66
97
|
call(...).value_or { |error| raise error }
|
|
67
98
|
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def manager
|
|
103
|
+
@manager ||= Plugins::Manager.new
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def handle_catch(service, &)
|
|
107
|
+
catch(service, &)
|
|
108
|
+
end
|
|
68
109
|
end
|
|
69
110
|
|
|
70
|
-
|
|
111
|
+
attr_accessor :__result_method__called__
|
|
71
112
|
|
|
72
113
|
private
|
|
73
114
|
|
|
74
115
|
attr_reader :__performing__
|
|
75
116
|
|
|
76
117
|
def fail!(code, data = nil)
|
|
77
|
-
check_performing
|
|
78
|
-
|
|
79
|
-
end
|
|
118
|
+
check_performing!
|
|
119
|
+
raise self.class::Failure.new(code, data)
|
|
80
120
|
end
|
|
81
121
|
|
|
82
122
|
def success!(data = nil)
|
|
83
|
-
check_performing
|
|
84
|
-
|
|
85
|
-
|
|
123
|
+
check_performing!
|
|
124
|
+
result_method_called!
|
|
125
|
+
proceed_return(self, Result.new(data))
|
|
86
126
|
end
|
|
87
127
|
|
|
88
|
-
def check_performing
|
|
89
|
-
if __performing__
|
|
90
|
-
yield
|
|
91
|
-
else
|
|
92
|
-
error_message =
|
|
93
|
-
"It looks like #call instance method was called directly in #{self.class}. " \
|
|
94
|
-
"You must always use class-level `.call` or `.call!` method."
|
|
128
|
+
def check_performing!
|
|
129
|
+
return if __performing__
|
|
95
130
|
|
|
96
|
-
|
|
97
|
-
|
|
131
|
+
error_message =
|
|
132
|
+
"It looks like #call instance method was called directly in #{self.class}. " \
|
|
133
|
+
"You must always use class-level `.call` or `.call!` method."
|
|
134
|
+
|
|
135
|
+
raise InvalidCommandCall, error_message
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def result_method_called!
|
|
139
|
+
self.__result_method__called__ = true
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def proceed_return(service, data)
|
|
143
|
+
throw(service, data)
|
|
98
144
|
end
|
|
99
145
|
end
|
|
100
146
|
end
|
data/lib/resol/version.rb
CHANGED
data/lib/resol.rb
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
4
|
-
|
|
3
|
+
require "dry-configurable"
|
|
5
4
|
require_relative "resol/version"
|
|
6
|
-
|
|
7
|
-
require_relative "resol/
|
|
5
|
+
|
|
6
|
+
require_relative "resol/injector"
|
|
7
|
+
require_relative "resol/plugins"
|
|
8
8
|
require_relative "resol/service"
|
|
9
9
|
|
|
10
10
|
module Resol
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
extend Dry::Configurable
|
|
14
|
+
|
|
15
|
+
setting :classes_allowed_to_patch, default: ["Resol::Service"]
|
|
11
16
|
end
|
metadata
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: resol
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aleksei Bespalov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
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
|
-
name:
|
|
13
|
+
name: dry-configurable
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
18
|
+
version: 1.2.0
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
25
|
+
version: 1.2.0
|
|
27
26
|
description: Gem for creating (any) object patterns
|
|
28
27
|
email:
|
|
29
28
|
- nulldefiner@gmail.com
|
|
@@ -46,18 +45,17 @@ files:
|
|
|
46
45
|
- lib/resol.rb
|
|
47
46
|
- lib/resol/builder.rb
|
|
48
47
|
- lib/resol/callbacks.rb
|
|
49
|
-
- lib/resol/
|
|
48
|
+
- lib/resol/injector.rb
|
|
49
|
+
- lib/resol/plugins.rb
|
|
50
|
+
- lib/resol/plugins/dummy.rb
|
|
51
|
+
- lib/resol/plugins/return_in_service.rb
|
|
50
52
|
- lib/resol/result.rb
|
|
51
|
-
- lib/resol/return_engine.rb
|
|
52
|
-
- lib/resol/return_engine/catch.rb
|
|
53
|
-
- lib/resol/return_engine/return.rb
|
|
54
53
|
- lib/resol/service.rb
|
|
55
54
|
- lib/resol/version.rb
|
|
56
55
|
homepage: https://github.com/umbrellio/resol
|
|
57
56
|
licenses:
|
|
58
57
|
- MIT
|
|
59
58
|
metadata: {}
|
|
60
|
-
post_install_message:
|
|
61
59
|
rdoc_options: []
|
|
62
60
|
require_paths:
|
|
63
61
|
- lib
|
|
@@ -65,15 +63,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
65
63
|
requirements:
|
|
66
64
|
- - ">="
|
|
67
65
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
66
|
+
version: 3.1.0
|
|
69
67
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
68
|
requirements:
|
|
71
69
|
- - ">="
|
|
72
70
|
- !ruby/object:Gem::Version
|
|
73
71
|
version: '0'
|
|
74
72
|
requirements: []
|
|
75
|
-
rubygems_version: 3.
|
|
76
|
-
signing_key:
|
|
73
|
+
rubygems_version: 3.6.9
|
|
77
74
|
specification_version: 4
|
|
78
75
|
summary: Gem for creating (any) object patterns
|
|
79
76
|
test_files: []
|
data/lib/resol/configuration.rb
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Resol
|
|
4
|
-
class Configuration
|
|
5
|
-
DEFAULT_RETURN_ENGINE = ReturnEngine::Catch
|
|
6
|
-
|
|
7
|
-
class << self
|
|
8
|
-
def configure
|
|
9
|
-
SmartCore::Initializer::Configuration.configure do |c|
|
|
10
|
-
self.smartcore_config = c
|
|
11
|
-
yield self
|
|
12
|
-
self.smartcore_config = nil
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def return_engine
|
|
17
|
-
@return_engine || DEFAULT_RETURN_ENGINE
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def return_engine=(engine)
|
|
21
|
-
@return_engine = engine
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
attr_accessor :smartcore_config
|
|
27
|
-
|
|
28
|
-
def method_missing(meth, *args, &block)
|
|
29
|
-
# rubocop:disable Style/SafeNavigation
|
|
30
|
-
if smartcore_config && smartcore_config.respond_to?(meth)
|
|
31
|
-
# rubocop:enable Style/SafeNavigation
|
|
32
|
-
smartcore_config.__send__(meth, *args, &block)
|
|
33
|
-
else
|
|
34
|
-
super(meth, *args, &block)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def respond_to_missing?(meth, include_private)
|
|
39
|
-
smartcore_config.respond_to?(meth, include_private)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Resol
|
|
4
|
-
module ReturnEngine
|
|
5
|
-
module Catch
|
|
6
|
-
extend self
|
|
7
|
-
|
|
8
|
-
def wrap_call(service)
|
|
9
|
-
catch(service) do
|
|
10
|
-
yield
|
|
11
|
-
NOT_EXITED
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def uncaught_call?(return_obj)
|
|
16
|
-
return_obj == NOT_EXITED
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def handle_return(service, data)
|
|
20
|
-
throw(service, data)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Resol
|
|
4
|
-
module ReturnEngine
|
|
5
|
-
module Return
|
|
6
|
-
extend self
|
|
7
|
-
|
|
8
|
-
def wrap_call(_service)
|
|
9
|
-
yield
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def uncaught_call?(return_obj)
|
|
13
|
-
!return_obj.is_a?(Resol::Service::Result)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def handle_return(_service, data)
|
|
17
|
-
data
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
data/lib/resol/return_engine.rb
DELETED