resol 0.8.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 +12 -0
- data/Gemfile.lock +111 -75
- 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 -27
- data/lib/resol/version.rb +1 -1
- data/lib/resol.rb +9 -3
- metadata +11 -122
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
|
@@ -3,3 +3,15 @@
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
|
+
|
|
7
|
+
gem "bundler-audit"
|
|
8
|
+
gem "ci-helper"
|
|
9
|
+
gem "pry"
|
|
10
|
+
gem "rake"
|
|
11
|
+
gem "rspec"
|
|
12
|
+
gem "rubocop-config-umbrellio"
|
|
13
|
+
gem "simplecov"
|
|
14
|
+
gem "simplecov-lcov"
|
|
15
|
+
|
|
16
|
+
gem "dry-initializer"
|
|
17
|
+
gem "smart_initializer"
|
data/Gemfile.lock
CHANGED
|
@@ -1,126 +1,161 @@
|
|
|
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.
|
|
11
|
-
|
|
10
|
+
activesupport (7.2.2.1)
|
|
11
|
+
base64
|
|
12
|
+
benchmark (>= 0.3)
|
|
13
|
+
bigdecimal
|
|
14
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
15
|
+
connection_pool (>= 2.2.5)
|
|
16
|
+
drb
|
|
12
17
|
i18n (>= 1.6, < 2)
|
|
18
|
+
logger (>= 1.4.2)
|
|
13
19
|
minitest (>= 5.1)
|
|
14
|
-
|
|
20
|
+
securerandom (>= 0.3)
|
|
21
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
15
22
|
ast (2.4.2)
|
|
16
|
-
|
|
23
|
+
base64 (0.2.0)
|
|
24
|
+
benchmark (0.4.0)
|
|
25
|
+
bigdecimal (3.1.9)
|
|
26
|
+
bundler-audit (0.9.2)
|
|
17
27
|
bundler (>= 1.2.0, < 3)
|
|
18
28
|
thor (~> 1.0)
|
|
19
|
-
ci-helper (0.
|
|
20
|
-
colorize (~>
|
|
21
|
-
dry-inflector (~> 0
|
|
22
|
-
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)
|
|
23
33
|
coderay (1.1.3)
|
|
24
|
-
colorize (
|
|
25
|
-
concurrent-ruby (1.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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)
|
|
30
44
|
concurrent-ruby (~> 1.0)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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)
|
|
35
58
|
ast (~> 2.4.1)
|
|
36
|
-
|
|
59
|
+
racc
|
|
60
|
+
pry (0.15.2)
|
|
37
61
|
coderay (~> 1.1)
|
|
38
62
|
method_source (~> 1.0)
|
|
39
|
-
qonfig (0.
|
|
40
|
-
|
|
63
|
+
qonfig (0.30.0)
|
|
64
|
+
base64 (>= 0.2)
|
|
65
|
+
racc (1.8.1)
|
|
66
|
+
rack (3.1.8)
|
|
41
67
|
rainbow (3.1.1)
|
|
42
|
-
rake (13.
|
|
43
|
-
regexp_parser (2.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
rspec-
|
|
47
|
-
rspec-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
rspec-expectations (3.11.0)
|
|
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)
|
|
52
77
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
53
|
-
rspec-support (~> 3.
|
|
54
|
-
rspec-mocks (3.
|
|
78
|
+
rspec-support (~> 3.13.0)
|
|
79
|
+
rspec-mocks (3.13.2)
|
|
55
80
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
56
|
-
rspec-support (~> 3.
|
|
57
|
-
rspec-support (3.
|
|
58
|
-
rubocop (1.
|
|
81
|
+
rspec-support (~> 3.13.0)
|
|
82
|
+
rspec-support (3.13.2)
|
|
83
|
+
rubocop (1.69.2)
|
|
84
|
+
json (~> 2.3)
|
|
85
|
+
language_server-protocol (>= 3.17.0)
|
|
59
86
|
parallel (~> 1.10)
|
|
60
|
-
parser (>= 3.
|
|
87
|
+
parser (>= 3.3.0.2)
|
|
61
88
|
rainbow (>= 2.2.2, < 4.0)
|
|
62
|
-
regexp_parser (>=
|
|
63
|
-
|
|
64
|
-
rubocop-ast (>= 1.15.1, < 2.0)
|
|
89
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
90
|
+
rubocop-ast (>= 1.36.2, < 2.0)
|
|
65
91
|
ruby-progressbar (~> 1.7)
|
|
66
|
-
unicode-display_width (>=
|
|
67
|
-
rubocop-ast (1.
|
|
68
|
-
parser (>= 3.
|
|
69
|
-
rubocop-config-umbrellio (1.
|
|
70
|
-
rubocop (~> 1.
|
|
71
|
-
rubocop-
|
|
72
|
-
rubocop-
|
|
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)
|
|
73
100
|
rubocop-rake (~> 0.6.0)
|
|
74
|
-
rubocop-rspec (~>
|
|
75
|
-
rubocop-sequel (~> 0.3.
|
|
76
|
-
rubocop-
|
|
77
|
-
rubocop (
|
|
78
|
-
|
|
79
|
-
|
|
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)
|
|
80
109
|
activesupport (>= 4.2.0)
|
|
81
110
|
rack (>= 1.1)
|
|
82
|
-
rubocop (>= 1.
|
|
111
|
+
rubocop (>= 1.52.0, < 2.0)
|
|
112
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
|
83
113
|
rubocop-rake (0.6.0)
|
|
84
114
|
rubocop (~> 1.0)
|
|
85
|
-
rubocop-rspec (
|
|
86
|
-
rubocop (~> 1.
|
|
87
|
-
rubocop-sequel (0.3.
|
|
115
|
+
rubocop-rspec (3.3.0)
|
|
116
|
+
rubocop (~> 1.61)
|
|
117
|
+
rubocop-sequel (0.3.8)
|
|
88
118
|
rubocop (~> 1.0)
|
|
89
|
-
ruby-progressbar (1.
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
ruby-progressbar (1.13.0)
|
|
120
|
+
securerandom (0.4.1)
|
|
121
|
+
sequel (5.88.0)
|
|
122
|
+
bigdecimal
|
|
123
|
+
simplecov (0.22.0)
|
|
92
124
|
docile (~> 1.1)
|
|
93
125
|
simplecov-html (~> 0.11)
|
|
94
126
|
simplecov_json_formatter (~> 0.1)
|
|
95
|
-
simplecov-html (0.
|
|
127
|
+
simplecov-html (0.13.1)
|
|
96
128
|
simplecov-lcov (0.8.0)
|
|
97
129
|
simplecov_json_formatter (0.1.4)
|
|
98
|
-
smart_engine (0.
|
|
99
|
-
smart_initializer (0.
|
|
130
|
+
smart_engine (0.17.0)
|
|
131
|
+
smart_initializer (0.11.1)
|
|
100
132
|
qonfig (~> 0.24)
|
|
133
|
+
smart_engine (~> 0.16)
|
|
134
|
+
smart_types (~> 0.8)
|
|
135
|
+
smart_types (0.8.0)
|
|
101
136
|
smart_engine (~> 0.11)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
smart_engine (~> 0.11)
|
|
105
|
-
symbiont-ruby (0.7.0)
|
|
106
|
-
thor (1.2.1)
|
|
107
|
-
tzinfo (2.0.4)
|
|
137
|
+
thor (1.3.2)
|
|
138
|
+
tzinfo (2.0.6)
|
|
108
139
|
concurrent-ruby (~> 1.0)
|
|
109
|
-
umbrellio-sequel-plugins (0.
|
|
140
|
+
umbrellio-sequel-plugins (0.17.0)
|
|
110
141
|
sequel
|
|
111
|
-
|
|
112
|
-
|
|
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)
|
|
113
146
|
|
|
114
147
|
PLATFORMS
|
|
115
148
|
arm64-darwin-21
|
|
116
149
|
x86_64-darwin-19
|
|
117
150
|
x86_64-darwin-20
|
|
118
151
|
x86_64-darwin-21
|
|
152
|
+
x86_64-darwin-22
|
|
119
153
|
x86_64-linux
|
|
120
154
|
|
|
121
155
|
DEPENDENCIES
|
|
122
156
|
bundler-audit
|
|
123
157
|
ci-helper
|
|
158
|
+
dry-initializer
|
|
124
159
|
pry
|
|
125
160
|
rake
|
|
126
161
|
resol!
|
|
@@ -128,6 +163,7 @@ DEPENDENCIES
|
|
|
128
163
|
rubocop-config-umbrellio
|
|
129
164
|
simplecov
|
|
130
165
|
simplecov-lcov
|
|
166
|
+
smart_initializer
|
|
131
167
|
|
|
132
168
|
BUNDLED WITH
|
|
133
|
-
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,59 +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(&
|
|
49
|
-
:uncaught
|
|
74
|
+
service.call(&)
|
|
50
75
|
end
|
|
76
|
+
return Resol::Success(result.data) if service.__result_method__called__
|
|
51
77
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
raise InvalidCommandImplementation, error_message
|
|
55
|
-
else
|
|
56
|
-
Resol::Success(result.data)
|
|
57
|
-
end
|
|
78
|
+
error_message = "No `#success!` or `#fail!` called in `#call` method in #{service.class}."
|
|
79
|
+
raise InvalidCommandImplementation, error_message
|
|
58
80
|
rescue self::Failure => e
|
|
59
81
|
Resol::Failure(e)
|
|
60
82
|
end
|
|
61
83
|
|
|
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.
|
|
62
96
|
def call!(...)
|
|
63
97
|
call(...).value_or { |error| raise error }
|
|
64
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
|
|
65
109
|
end
|
|
66
110
|
|
|
67
|
-
|
|
111
|
+
attr_accessor :__result_method__called__
|
|
68
112
|
|
|
69
113
|
private
|
|
70
114
|
|
|
71
115
|
attr_reader :__performing__
|
|
72
116
|
|
|
73
117
|
def fail!(code, data = nil)
|
|
74
|
-
check_performing
|
|
75
|
-
|
|
76
|
-
end
|
|
118
|
+
check_performing!
|
|
119
|
+
raise self.class::Failure.new(code, data)
|
|
77
120
|
end
|
|
78
121
|
|
|
79
122
|
def success!(data = nil)
|
|
80
|
-
check_performing
|
|
81
|
-
|
|
82
|
-
|
|
123
|
+
check_performing!
|
|
124
|
+
result_method_called!
|
|
125
|
+
proceed_return(self, Result.new(data))
|
|
83
126
|
end
|
|
84
127
|
|
|
85
|
-
def check_performing
|
|
86
|
-
if __performing__
|
|
87
|
-
yield
|
|
88
|
-
else
|
|
89
|
-
error_message =
|
|
90
|
-
"It looks like #call instance method was called directly in #{self.class}. " \
|
|
91
|
-
"You must always use class-level `.call` or `.call!` method."
|
|
128
|
+
def check_performing!
|
|
129
|
+
return if __performing__
|
|
92
130
|
|
|
93
|
-
|
|
94
|
-
|
|
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)
|
|
95
144
|
end
|
|
96
145
|
end
|
|
97
146
|
end
|
data/lib/resol/version.rb
CHANGED
data/lib/resol.rb
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
4
|
-
|
|
3
|
+
require "dry-configurable"
|
|
5
4
|
require_relative "resol/version"
|
|
5
|
+
|
|
6
|
+
require_relative "resol/injector"
|
|
7
|
+
require_relative "resol/plugins"
|
|
6
8
|
require_relative "resol/service"
|
|
7
9
|
|
|
8
10
|
module Resol
|
|
9
|
-
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
extend Dry::Configurable
|
|
14
|
+
|
|
15
|
+
setting :classes_allowed_to_patch, default: ["Resol::Service"]
|
|
10
16
|
end
|
metadata
CHANGED
|
@@ -1,141 +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:
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: bundler-audit
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: ci-helper
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: pry
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: rake
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - ">="
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - ">="
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rspec
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - ">="
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '0'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - ">="
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '0'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: rubocop-config-umbrellio
|
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
|
100
|
-
requirements:
|
|
101
|
-
- - ">="
|
|
102
|
-
- !ruby/object:Gem::Version
|
|
103
|
-
version: '0'
|
|
104
|
-
type: :development
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - ">="
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0'
|
|
111
|
-
- !ruby/object:Gem::Dependency
|
|
112
|
-
name: simplecov
|
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - ">="
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0'
|
|
118
|
-
type: :development
|
|
119
|
-
prerelease: false
|
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
-
requirements:
|
|
122
|
-
- - ">="
|
|
123
|
-
- !ruby/object:Gem::Version
|
|
124
|
-
version: '0'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: simplecov-lcov
|
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
|
128
|
-
requirements:
|
|
129
|
-
- - ">="
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: '0'
|
|
132
|
-
type: :development
|
|
133
|
-
prerelease: false
|
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
-
requirements:
|
|
136
|
-
- - ">="
|
|
137
|
-
- !ruby/object:Gem::Version
|
|
138
|
-
version: '0'
|
|
25
|
+
version: 1.2.0
|
|
139
26
|
description: Gem for creating (any) object patterns
|
|
140
27
|
email:
|
|
141
28
|
- nulldefiner@gmail.com
|
|
@@ -158,6 +45,10 @@ files:
|
|
|
158
45
|
- lib/resol.rb
|
|
159
46
|
- lib/resol/builder.rb
|
|
160
47
|
- lib/resol/callbacks.rb
|
|
48
|
+
- lib/resol/injector.rb
|
|
49
|
+
- lib/resol/plugins.rb
|
|
50
|
+
- lib/resol/plugins/dummy.rb
|
|
51
|
+
- lib/resol/plugins/return_in_service.rb
|
|
161
52
|
- lib/resol/result.rb
|
|
162
53
|
- lib/resol/service.rb
|
|
163
54
|
- lib/resol/version.rb
|
|
@@ -165,7 +56,6 @@ homepage: https://github.com/umbrellio/resol
|
|
|
165
56
|
licenses:
|
|
166
57
|
- MIT
|
|
167
58
|
metadata: {}
|
|
168
|
-
post_install_message:
|
|
169
59
|
rdoc_options: []
|
|
170
60
|
require_paths:
|
|
171
61
|
- lib
|
|
@@ -173,15 +63,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
173
63
|
requirements:
|
|
174
64
|
- - ">="
|
|
175
65
|
- !ruby/object:Gem::Version
|
|
176
|
-
version:
|
|
66
|
+
version: 3.1.0
|
|
177
67
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
178
68
|
requirements:
|
|
179
69
|
- - ">="
|
|
180
70
|
- !ruby/object:Gem::Version
|
|
181
71
|
version: '0'
|
|
182
72
|
requirements: []
|
|
183
|
-
rubygems_version: 3.
|
|
184
|
-
signing_key:
|
|
73
|
+
rubygems_version: 3.6.9
|
|
185
74
|
specification_version: 4
|
|
186
75
|
summary: Gem for creating (any) object patterns
|
|
187
76
|
test_files: []
|