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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 737ecf288d9db1fc0440dda961c9e0a3a9c5072fba6f1f3a4049a67647a782bb
4
- data.tar.gz: c50a24b67fb2a5c20c37d9a4204ba6c2293b7d597c0aa46085422ff30fbc0d9e
3
+ metadata.gz: c7a4e82d0b30c1d86f47db975689b82768d672fd2c2db7088d2ac41a68346b90
4
+ data.tar.gz: 838c52bf67feb5442213b6e485a3f024ee8323e5c36618492743ad761455dc7e
5
5
  SHA512:
6
- metadata.gz: dec029e7a8b86070c7e3090d7f755415d57e88a572c8d67ef29feec935066cba996cfadd71ef3a93e7e4c502c218952bd00d299fa1473673ee4e6923367c199b
7
- data.tar.gz: 78332a4cc2eb93b82bc1f963ae141c9b1176ec11c7fc145dcbe1219493817d465f971fe94cdbd2d6727acc7b1cb4f0c9c546f51ce3bfc0cb1c3a11680567a62b
6
+ metadata.gz: e5bf12a60de921e46f762c74d9dac977b6761d045a214895788dfc5374e19977563c73aa737cc8ebe691dd2b4034f99a2fc78d6ad2df1479232cf0fb57ce7842
7
+ data.tar.gz: 4aedc9b8ca631ce5b2e1c8cb79eb1eea49d0aa2085e162fbf4f2fb671dd58e860be66f2c90fd42241fbd81f932775700f733c631f3dd97b53c4d1f8ce5d481d7
@@ -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.1"
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: ["2.7", "3.0"]
46
+ ruby: ["3.1", "3.2", "3.3"]
47
47
  experimental: [false]
48
48
  include:
49
49
  - ruby: head
data/.rubocop.yml CHANGED
@@ -3,7 +3,7 @@ inherit_gem:
3
3
 
4
4
  AllCops:
5
5
  DisplayCopNames: true
6
- TargetRubyVersion: 2.7
6
+ TargetRubyVersion: 3.1
7
7
 
8
8
  Naming/MethodParameterName:
9
9
  AllowedNames: ["x", "y", "z"]
data/Gemfile CHANGED
@@ -12,3 +12,6 @@ gem "rspec"
12
12
  gem "rubocop-config-umbrellio"
13
13
  gem "simplecov"
14
14
  gem "simplecov-lcov"
15
+
16
+ gem "dry-initializer"
17
+ gem "smart_initializer"
data/Gemfile.lock CHANGED
@@ -1,118 +1,130 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- resol (0.9.0)
5
- smart_initializer (~> 0.7)
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.1.2)
10
+ activesupport (7.2.2.1)
11
11
  base64
12
+ benchmark (>= 0.3)
12
13
  bigdecimal
13
- concurrent-ruby (~> 1.0, >= 1.0.2)
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
- mutex_m
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
- bigdecimal (3.1.4)
23
- bundler-audit (0.9.1)
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.5.0)
27
- colorize (~> 0.8)
28
- dry-inflector (~> 0.2)
29
- umbrellio-sequel-plugins (~> 0.4)
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 (0.8.1)
32
- concurrent-ruby (1.2.2)
33
- connection_pool (2.4.1)
34
- diff-lcs (1.5.0)
35
- docile (1.4.0)
36
- drb (2.2.0)
37
- ruby2_keywords
38
- dry-inflector (0.3.0)
39
- i18n (1.14.1)
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
- json (2.6.3)
42
- method_source (1.0.0)
43
- minitest (5.20.0)
44
- mutex_m (0.2.0)
45
- parallel (1.23.0)
46
- parser (3.2.2.4)
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.14.2)
60
+ pry (0.15.2)
50
61
  coderay (~> 1.1)
51
62
  method_source (~> 1.0)
52
- qonfig (0.28.0)
53
- racc (1.7.3)
54
- rack (3.0.8)
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.0)
57
- regexp_parser (2.8.2)
58
- rexml (3.2.6)
59
- rspec (3.12.0)
60
- rspec-core (~> 3.12.0)
61
- rspec-expectations (~> 3.12.0)
62
- rspec-mocks (~> 3.12.0)
63
- rspec-core (3.12.2)
64
- rspec-support (~> 3.12.0)
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.12.0)
68
- rspec-mocks (3.12.6)
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.12.0)
71
- rspec-support (3.12.1)
72
- rubocop (1.50.2)
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.2.0.0)
87
+ parser (>= 3.3.0.2)
76
88
  rainbow (>= 2.2.2, < 4.0)
77
- regexp_parser (>= 1.8, < 3.0)
78
- rexml (>= 3.2.5, < 4.0)
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, < 3.0)
82
- rubocop-ast (1.30.0)
83
- parser (>= 3.2.1.0)
84
- rubocop-capybara (2.19.0)
85
- rubocop (~> 1.41)
86
- rubocop-config-umbrellio (1.50.0.85)
87
- rubocop (~> 1.50.0)
88
- rubocop-performance (~> 1.17.0)
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 (~> 2.20.0)
92
- rubocop-sequel (~> 0.3.3)
93
- rubocop-performance (1.17.1)
94
- rubocop (>= 1.7.0, < 2.0)
95
- rubocop-ast (>= 0.4.0)
96
- rubocop-rails (2.19.1)
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.33.0, < 2.0)
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 (2.20.0)
103
- rubocop (~> 1.33)
104
- rubocop-capybara (~> 2.17)
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
- ruby2_keywords (0.0.5)
109
- sequel (5.74.0)
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.12.3)
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
- symbiont-ruby (0.7.0)
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.14.0.189)
140
+ umbrellio-sequel-plugins (0.17.0)
130
141
  sequel
131
- symbiont-ruby
132
- unicode-display_width (2.5.0)
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.4.10
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
- All incoming params and options can be defined using a [smart_initializer](https://github.com/smart-rb/smart_initializer) gem interface.
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?` returns `true` for success result and `false` for failure result
99
- - `failure?` returns `true` for failure result and `false` for success result
100
- - `value!` unwraps a result object, returns the value for success result, and throws an error for failure result
101
- - `value_or(other_value, &block)` returns a value for success result or `other_value` for failure result (either calls `block` in case it given)
102
- - `error` returns `nil` for success result and error object (with code and data) for failure result
103
- - `or(&block)` calls block for failure result, for success result does nothing
104
- - `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.
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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resol
4
+ module Plugins
5
+ module Dummy; end
6
+ end
7
+ 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
- class Result
7
- # @!method success?
8
- # @!method failure?
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
- nil
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
- if block_given?
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
- def self.Success(...)
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 self.Failure(...)
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
- def call(*args, **kwargs, &block)
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 = return_engine.wrap_call(service) do
71
+ result = handle_catch(service) do
46
72
  service.instance_variable_set(:@__performing__, true)
47
73
  __run_callbacks__(service)
48
- service.call(&block)
74
+ service.call(&)
49
75
  end
76
+ return Resol::Success(result.data) if service.__result_method__called__
50
77
 
51
- if return_engine.uncaught_call?(result)
52
- error_message = "No `#success!` or `#fail!` called in `#call` method in #{service.class}."
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
- def return_engine
62
- Resol::Configuration.return_engine
63
- end
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
- # @!method call
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 do
78
- raise self.class::Failure.new(code, data)
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 do
84
- self.class.return_engine.handle_return(self, Result.new(data))
85
- end
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
- raise InvalidCommandCall, error_message
97
- end
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Resol
4
- VERSION = "0.9.0"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/resol.rb CHANGED
@@ -1,11 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "smart_core/initializer"
4
-
3
+ require "dry-configurable"
5
4
  require_relative "resol/version"
6
- require_relative "resol/return_engine"
7
- require_relative "resol/configuration"
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.9.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: 2023-12-11 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
- name: smart_initializer
13
+ name: dry-configurable
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '0.7'
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: '0.7'
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/configuration.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
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: 2.7.0
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.4.21
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: []
@@ -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
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Resol
4
- module ReturnEngine
5
- NOT_EXITED = Object.new.freeze
6
- DataWrapper = Struct.new(:data)
7
- end
8
- end
9
-
10
- require_relative "return_engine/catch"
11
- require_relative "return_engine/return"