From b975267747b34bed56310dc6e7fe4057f387b2e7 Mon Sep 17 00:00:00 2001 From: Pablo Herrero Date: Wed, 7 Aug 2024 03:17:47 -0300 Subject: [PATCH] Improve code with with new ruby features --- lib/pathway.rb | 90 ++++++++++++---------------- lib/pathway/plugins/sequel_models.rb | 22 +++---- lib/pathway/plugins/simple_auth.rb | 4 +- lib/pathway/result.rb | 58 +++++------------- lib/pathway/version.rb | 2 +- spec/state_spec.rb | 6 +- 6 files changed, 69 insertions(+), 113 deletions(-) diff --git a/lib/pathway.rb b/lib/pathway.rb index 61a18ad..a033cd5 100644 --- a/lib/pathway.rb +++ b/lib/pathway.rb @@ -34,6 +34,7 @@ class DSL class Error attr_reader :type, :message, :details + singleton_class.send :attr_accessor, :default_messages @default_messages = {} @@ -44,13 +45,8 @@ def initialize(type:, message: nil, details: nil) @details = details || {} end - def deconstruct - [type, message, details] - end - - def deconstruct_keys(_) - { type: type, message: message, details: details } - end + def deconstruct = [type, message, details] + def deconstruct_keys(_) = { type:, message:, details: } private @@ -61,64 +57,58 @@ def default_message_for(type) class State extend Forwardable + delegate %i([] []= fetch store include? values_at deconstruct_keys) => :@hash def initialize(operation, values = {}) @hash = operation.context.merge(values) @result_key = operation.result_key end - delegate %i([] []= fetch store include? values_at deconstruct_keys) => :@hash - def update(kargs) @hash.update(kargs) self end - def result - @hash[@result_key] - end - - def to_hash - @hash - end + def result = @hash[@result_key] + def to_hash = @hash - def unwrap(&bl) + def use(&bl) raise 'a block must be provided' if !block_given? + params = bl.parameters - unless params.all? { |(type,_)| [:block, :key, :keyreq, :keyrest].member?(type) } - raise 'only keyword arguments are supported for unwraping' - end - if params.any? {|(type,_)| type == :keyrest } + if !params.all? { _1 in [:block|:key|:keyreq|:keyrest, _] } + raise 'only keyword arguments are supported' + elsif params in [*, [:keyrest, _], *] bl.call(**to_hash) else - keys = params.select {|(type,_)| type == :key || type == :keyreq }.map(&:last) + keys = params.select { _1 in [:key|:keyreq, _] }.map(&:last) bl.call(**to_hash.slice(*keys)) end end alias_method :to_h, :to_hash - alias_method :u, :unwrap + alias_method :u, :use + alias_method :unwrap, :use end module Plugins module Base module ClassMethods attr_accessor :result_key - alias :result_at :result_key= + + alias_method :result_at, :result_key= def process(&bl) dsl = self::DSL define_method(:call) do |input| - dsl.new(State.new(self, input: input), self) + dsl.new(State.new(self, input:), self) .run(&bl) .then(&:result) end end - def call(ctx,...) - new(ctx).call(...) - end + def call(ctx,...) = new(ctx).call(...) def inherited(subclass) super @@ -132,18 +122,16 @@ module InstanceMethods delegate :result_key => 'self.class' delegate %i[result success failure] => Result - alias :wrap :result + alias_method :wrap, :result - def call(*) - fail 'must implement at subclass' - end + def call(*) = raise 'must implement at subclass' def error(type, message: nil, details: nil) - failure(Error.new(type: type, message: message, details: details)) + failure(Error.new(type:, message:, details:)) end def wrap_if_present(value, type: :not_found, message: nil, details: {}) - value.nil? ? error(type, message: message, details: details) : success(value) + value.nil? ? error(type, message:, details:) : success(value) end end @@ -165,43 +153,41 @@ def run(&bl) # Execute step and preserve the former state def step(callable,...) bl = _callable(callable) - @result = @result.tee { |state| bl.call(state,...) } end # Execute step and modify the former state setting the key - def set(callable, *args, to: @operation.result_key) + def set(callable, *args, to: @operation.result_key, **kwargs) bl = _callable(callable) @result = @result.then do |state| - wrap(bl.call(state, *args)) + wrap(bl.call(state, *args, **kwargs)) .then { |value| state.update(to => value) } end end # Execute step and replace the current state completely - def map(callable) + def map(callable,...) bl = _callable(callable) - @result = @result.then(bl) + @result = @result.then { |state| bl.call(state,...) } end - def around(wrapper, &steps) + def around(strategy, &nested_block) @result.then do |state| - seq = -> (dsl = self) { @result = dsl.run(&steps) } - _callable(wrapper).call(seq, state) + runner = ->(dsl = self) { @result = dsl.run(&nested_block) } + + _callable(strategy).call(runner, state) end end - def if_true(cond, &steps) + def if_true(cond, &nested_block) cond = _callable(cond) - around(-> seq, state { - seq.call if cond.call(state) - }, &steps) + around(->(runner, state) { runner.call if cond.call(state) }, &nested_block) end - def if_false(cond, &steps) + def if_false(cond, &nested_block) cond = _callable(cond) - if_true(-> state { !cond.call(state) }, &steps) + if_true(->(state) { !cond.call(state) }, &nested_block) end alias_method :sequence, :around @@ -209,16 +195,14 @@ def if_false(cond, &steps) private - def wrap(obj) - Result.result(obj) - end + def wrap(obj) = Result.result(obj) def _callable(callable) case callable when Proc - -> *args, **kwargs { @operation.instance_exec(*args, **kwargs, &callable) } + ->(*args, **kwargs) { @operation.instance_exec(*args, **kwargs, &callable) } when Symbol - -> *args, **kwargs { @operation.send(callable, *args, **kwargs) } + ->(*args, **kwargs) { @operation.send(callable, *args, **kwargs) } else callable end diff --git a/lib/pathway/plugins/sequel_models.rb b/lib/pathway/plugins/sequel_models.rb index 4128179..2afcba8 100644 --- a/lib/pathway/plugins/sequel_models.rb +++ b/lib/pathway/plugins/sequel_models.rb @@ -7,30 +7,30 @@ module Plugins module SequelModels module DSLMethods def transaction(step_name = nil, &bl) - fail 'must provide a step or a block but not both' if !step_name.nil? == block_given? + raise 'must provide a step or a block but not both' if !step_name.nil? == block_given? if step_name transaction { step step_name } else - around(-> steps, _ { + around(->(runner, _) { db.transaction(savepoint: true) do - raise Sequel::Rollback if steps.call.failure? + raise Sequel::Rollback if runner.call.failure? end }, &bl) end end def after_commit(step_name = nil, &bl) - fail 'must provide a step or a block but not both' if !step_name.nil? == block_given? + raise 'must provide a step or a block but not both' if !step_name.nil? == block_given? if step_name after_commit { step step_name } else - around(-> steps, state { - dsl = self.class::DSL.new(State.new(self, state.to_h.dup), self) + around(->(runner, state) { + dsl_copy = self.class::DSL.new(State.new(self, state.to_h.dup), self) db.after_commit do - steps.call(dsl) + runner.call(dsl_copy) end }, &bl) end @@ -41,10 +41,10 @@ module ClassMethods attr_accessor :model_class, :search_field, :model_not_found def model(model_class, search_by: model_class.primary_key, set_result_key: true, set_context_param: true, error_message: nil) - self.model_class = model_class - self.search_field = search_by - self.result_key = Inflector.underscore(Inflector.demodulize(model_class.name)).to_sym if set_result_key - self.model_not_found = error_message || "#{Inflector.humanize(Inflector.underscore(Inflector.demodulize(model_class.name)))} not found".freeze + self.model_class = model_class + self.search_field = search_by + self.result_key = Inflector.underscore(Inflector.demodulize(model_class.name)).to_sym if set_result_key + self.model_not_found = error_message || "#{Inflector.humanize(Inflector.underscore(Inflector.demodulize(model_class.name)))} not found".freeze self.context(result_key => Contextualizer::OPTIONAL) if set_result_key && set_context_param end diff --git a/lib/pathway/plugins/simple_auth.rb b/lib/pathway/plugins/simple_auth.rb index a69e55f..6d6402f 100644 --- a/lib/pathway/plugins/simple_auth.rb +++ b/lib/pathway/plugins/simple_auth.rb @@ -26,9 +26,7 @@ def authorize_with(*objs) authorized?(*objs) ? wrap(objs) : error(:forbidden) end - def authorized?(*) - true - end + def authorized?(*) = true end end end diff --git a/lib/pathway/result.rb b/lib/pathway/result.rb index b32f0d9..2f255e2 100644 --- a/lib/pathway/result.rb +++ b/lib/pathway/result.rb @@ -6,13 +6,8 @@ class Result attr_reader :value, :error class Success < Result - def initialize(value) - @value = value - end - - def success? - true - end + def initialize(value)= @value = value + def success? = true def then(bl=nil) result(block_given? ? yield(value): bl.call(value)) @@ -25,29 +20,18 @@ def tee(bl=nil, &block) private - alias :value_for_deconstruct :value + alias_method :value_for_deconstruct, :value end class Failure < Result - def initialize(error) - @error = error - end - - def success? - false - end - - def then(_=nil) - self - end - - def tee(_=nil) - self - end + def initialize(error)= @error = error + def success? = false + def then(_=nil) = self + def tee(_=nil) = self private - alias :value_for_deconstruct :error + alias_method :value_for_deconstruct, :error end module Mixin @@ -55,10 +39,16 @@ module Mixin Failure = Result::Failure end - def deconstruct - [value_for_deconstruct] + def self.success(value) = Success.new(value) + def self.failure(error) = Failure.new(error) + + def self.result(object) + object.is_a?(Result) ? object : success(object) end + def failure? = !success? + def deconstruct = [value_for_deconstruct] + def deconstruct_keys(keys) if value_for_deconstruct.respond_to?(:deconstruct_keys) value_for_deconstruct.deconstruct_keys(keys) @@ -67,22 +57,6 @@ def deconstruct_keys(keys) end end - def failure? - !success? - end - - def self.success(value) - Success.new(value) - end - - def self.failure(error) - Failure.new(error) - end - - def self.result(object) - object.is_a?(Result) ? object : success(object) - end - delegate :result => 'self.class' end end diff --git a/lib/pathway/version.rb b/lib/pathway/version.rb index 87941e4..59b9f10 100644 --- a/lib/pathway/version.rb +++ b/lib/pathway/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Pathway - VERSION = '0.12.2' + VERSION = '1.0.0b1' end diff --git a/spec/state_spec.rb b/spec/state_spec.rb index 13cbfb2..b6f3471 100644 --- a/spec/state_spec.rb +++ b/spec/state_spec.rb @@ -55,15 +55,15 @@ class SimpleOp < Operation it 'fails if at least one positional param is defined', :aggregate_failures do expect { state.unwrap {|pos, input:| } } - .to raise_error('only keyword arguments are supported for unwraping') + .to raise_error('only keyword arguments are supported') expect { state.unwrap {|input| } } - .to raise_error('only keyword arguments are supported for unwraping') + .to raise_error('only keyword arguments are supported') end context 'and it takes a block argument' do it 'fails if it has positional params' do expect { state.unwrap {|input, &bl| } } - .to raise_error('only keyword arguments are supported for unwraping') + .to raise_error('only keyword arguments are supported') end it 'does not fails if only keyword params', :aggregate_failures do