From 5777ce49f921f3e45937d2de40db5f55ac45ee9a Mon Sep 17 00:00:00 2001 From: AnotherRegularDude Date: Sat, 14 Dec 2024 15:12:58 +0300 Subject: [PATCH] Refacor, plugins feature, initializer feature Now supports plugins like sequel, can choose initializer, remove return engine --- Gemfile | 1 + Gemfile.lock | 3 +- Steepfile | 10 +++++ lib/resol.rb | 13 +++++- lib/resol/configuration.rb | 13 ------ lib/resol/plugins.rb | 28 ++++++------ lib/resol/plugins/return_in_service.rb | 25 +++++++++++ lib/resol/result.rb | 61 ++++++++++++-------------- lib/resol/return_engine/catch.rb | 24 ---------- lib/resol/return_engine/return.rb | 21 --------- lib/resol/service.rb | 55 ++++++++++++++++++----- spec/initializers_spec.rb | 4 ++ 12 files changed, 142 insertions(+), 116 deletions(-) create mode 100644 Steepfile create mode 100644 lib/resol/plugins/return_in_service.rb delete mode 100644 lib/resol/return_engine/catch.rb delete mode 100644 lib/resol/return_engine/return.rb create mode 100644 spec/initializers_spec.rb diff --git a/Gemfile b/Gemfile index 57bab9c..be40a33 100644 --- a/Gemfile +++ b/Gemfile @@ -14,3 +14,4 @@ gem "rubocop-config-umbrellio" gem "simplecov" gem "simplecov-lcov" gem "smart_initializer" +gem "qonfig", "0.28.0" diff --git a/Gemfile.lock b/Gemfile.lock index 531abc5..440ee56 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -55,7 +55,7 @@ GEM pry (0.15.0) coderay (~> 1.1) method_source (~> 1.0) - qonfig (0.29.0) + qonfig (0.28.0) racc (1.8.1) rack (3.1.8) rainbow (3.1.1) @@ -149,6 +149,7 @@ DEPENDENCIES ci-helper dry-initializer pry + qonfig (= 0.28.0) rake resol! rspec diff --git a/Steepfile b/Steepfile new file mode 100644 index 0000000..6d21d44 --- /dev/null +++ b/Steepfile @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +D = Steep::Diagnostic + +target :lib do + signature "sig" + check "lib/resol/result.rb" + + configure_code_diagnostics(D::Ruby.default) +end diff --git a/lib/resol.rb b/lib/resol.rb index 1f73abb..3b35961 100644 --- a/lib/resol.rb +++ b/lib/resol.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true require_relative "resol/version" -require_relative "resol/return_engine" require_relative "resol/configuration" +require_relative "resol/initializers" require_relative "resol/service" +require_relative "resol/plugins" module Resol extend self @@ -15,4 +16,14 @@ def config def configure yield config end + + # rubocop:disable Naming/MethodName + def Success(...) + Success.new(...) + end + + def Failure(...) + Failure.new(...) + end + # rubocop:enable Naming/MethodName end diff --git a/lib/resol/configuration.rb b/lib/resol/configuration.rb index cb4318f..a724604 100644 --- a/lib/resol/configuration.rb +++ b/lib/resol/configuration.rb @@ -4,29 +4,16 @@ module Resol module Configuration extend self - DEFAULTS = { return_engine: Resol::ReturnEngine::Catch }.freeze - - DEFAULTS.each_key do |attr_name| - define_method(attr_name) { values[attr_name] } - define_method(:"#{attr_name}=") { |value| values[attr_name] = value } - end - def smart_config return nil if smart_not_loaded? SmartCore::Initializer::Configuration.config end - def to_h = values.dup - private def smart_not_loaded? !defined?(SmartCore::Initializer::Configuration) end - - def values - @values ||= DEFAULTS.dup - end end end diff --git a/lib/resol/plugins.rb b/lib/resol/plugins.rb index 0aa771a..396273e 100644 --- a/lib/resol/plugins.rb +++ b/lib/resol/plugins.rb @@ -4,25 +4,22 @@ module Resol module Plugins + PLUGINS_PATH = Pathname("resol/plugins") class Manager - include Singleton - def initialize - self.patched_class = Resol::Service self.plugins = [] - self.plugin_lib_path = PathName("resol/plugins") end - def plugin(plugin_name, ...) - plugin_module = find_plugin_module(plugin_name) - plugin_module.apply(target_class, ...) if plugin_module.respond_to?(:apply) + def plugin(plugin_name) + return if plugins.include?(plugin_name) + plugin_module = find_plugin_module(plugin_name) if defined?(plugin_module::InstanceMethods) - target_class.include(plugin_module::InstanceMethods) + target_class.prepend(plugin_module::InstanceMethods) end if defined?(plugin_module::ClassMethods) - target_class.extend(plugin_module::ClassMethods) + target_class.singleton_class.prepend(plugin_module::ClassMethods) end plugins << plugin_name @@ -30,16 +27,21 @@ def plugin(plugin_name, ...) private - attr_accessor :target_class, :plugins, :plugin_lib_path + attr_accessor :plugins def find_plugin_module(plugin_name) - require plugin_lib_path.join(plugin_name) + require PLUGINS_PATH.join(plugin_name) + Plugins.const_get(classify_plugin_name(plugin_name)) rescue LoadError, NameError => e raise "Failed to load plugin '#{plugin_name}': #{e.message}" end - def camel_case(string) - string.split("_").map(&:capitalize).join + def classify_plugin_name(string) + string.split(/_|-/).map!(&:capitalize).join + end + + def target_class + Resol::Service end end end diff --git a/lib/resol/plugins/return_in_service.rb b/lib/resol/plugins/return_in_service.rb new file mode 100644 index 0000000..be375a2 --- /dev/null +++ b/lib/resol/plugins/return_in_service.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Resol + module Plugins + module ReturnInService + module ClassMethods + private + + def handle_catch(_service) + yield + end + + def call_service(service) + service.call.tap { |res| return unless res.is_a?(Service::Result) } + end + end + + module InstanceMethods + private + + def proceed_return(_service, data) = data + end + end + end +end diff --git a/lib/resol/result.rb b/lib/resol/result.rb index 029a3ca..0fe8a48 100644 --- a/lib/resol/result.rb +++ b/lib/resol/result.rb @@ -3,26 +3,11 @@ module Resol class UnwrapError < StandardError; end - class Result - # @!method success? - # @!method failure? - # @!method value_or - # @!method value! - - def initialize(*); end - - def or - yield(@value) if failure? - end - - def either(success_proc, failure_proc) - success? ? success_proc.call(@value) : failure_proc.call(@value) - end - end + class Result; end class Success < Result def initialize(value) - super + super() @value = value end @@ -34,7 +19,7 @@ def failure? false end - def value_or(*) + def value_or(_other_value = nil) @value end @@ -42,14 +27,26 @@ def value! @value end - def error - nil + def error = nil + + def or = nil + + def either(success_proc, _failure_proc) + success_proc.call(@value) + end + + def bind + yield @value + end + + def fmap(&) + Resol.Success(bind(&)) end end class Failure < Result def initialize(error) - super + super() @value = error end @@ -62,11 +59,7 @@ def failure? end def value_or(other_value = nil) - if block_given? - yield(@value) - else - other_value - end + block_given? ? yield(@value) : other_value end def value! @@ -76,13 +69,17 @@ def value! def error @value end - end - def self.Success(...) - Success.new(...) - end + def or + yield @value + end + + def either(_success_proc, failure_proc) + failure_proc.call(@value) + end + + def bind = self - def self.Failure(...) - Failure.new(...) + alias fmap bind end end diff --git a/lib/resol/return_engine/catch.rb b/lib/resol/return_engine/catch.rb deleted file mode 100644 index 6407728..0000000 --- a/lib/resol/return_engine/catch.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module Resol - module ReturnEngine - module Catch - extend self - - def wrap_call(service) - catch(service) do - yield - NOT_EXITED - end - end - - def uncaught_call?(return_obj) - return_obj == NOT_EXITED - end - - def handle_return(service, data) - throw(service, data) - end - end - end -end diff --git a/lib/resol/return_engine/return.rb b/lib/resol/return_engine/return.rb deleted file mode 100644 index a724cb6..0000000 --- a/lib/resol/return_engine/return.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module Resol - module ReturnEngine - module Return - extend self - - def wrap_call(_service) - yield - end - - def uncaught_call?(return_obj) - !return_obj.is_a?(Resol::Service::Result) - end - - def handle_return(_service, data) - data - end - end - end -end diff --git a/lib/resol/service.rb b/lib/resol/service.rb index a462e04..7974bfb 100644 --- a/lib/resol/service.rb +++ b/lib/resol/service.rb @@ -3,7 +3,6 @@ require_relative "builder" require_relative "callbacks" require_relative "result" -require_relative "initializers" module Resol class Service @@ -28,31 +27,47 @@ def message end end + module ChildMethodRestriction + def plugin(*) + raise NoMethodError + end + + def manager + raise NoMethodError + end + end + include Resol::Builder include Resol::Callbacks Result = Struct.new(:data) + NOT_EXITED = Object.new.freeze class << self def inherited(klass) klass.const_set(:Failure, Class.new(klass::Failure)) + klass.extend(ChildMethodRestriction) super end def use_initializer!(initializer_lib) - Resol::Initializers.apply!(self, initializer_lib) + Initializers.apply!(self, initializer_lib) end - def call(*, **, &) + def plugin(...) + manager.plugin(...) + end + + def call(*, **) service = build(*, **) - result = return_engine.wrap_call(service) do + result = handle_catch(service) do service.instance_variable_set(:@__performing__, true) __run_callbacks__(service) - service.call(&) + call_service(service) end - if return_engine.uncaught_call?(result) + if result == NOT_EXITED error_message = "No `#success!` or `#fail!` called in `#call` method in #{service.class}." raise InvalidCommandImplementation, error_message else @@ -62,13 +77,27 @@ def call(*, **, &) Resol::Failure(e) end - def return_engine - Resol::Configuration.return_engine - end - def call!(...) call(...).value_or { |error| raise error } end + + private + + def manager + @manager ||= Plugins::Manager.new + end + + def handle_catch(service) + catch(service) do + yield + NOT_EXITED + end + end + + def call_service(service) + service.call + NOT_EXITED + end end # @!method call @@ -85,7 +114,7 @@ def fail!(code, data = nil) def success!(data = nil) check_performing do - self.class.return_engine.handle_return(self, Result.new(data)) + proceed_return(self, Result.new(data)) end end @@ -100,5 +129,9 @@ def check_performing raise InvalidCommandCall, error_message end end + + def proceed_return(data) + throw(self, data) + end end end diff --git a/spec/initializers_spec.rb b/spec/initializers_spec.rb new file mode 100644 index 0000000..6f82e86 --- /dev/null +++ b/spec/initializers_spec.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +RSpec.describe Resol::Initializers do +end