-
-
Save waiting-for-dev/b83460a3b6c9a3afc2603ea23dba3726 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'logger' | |
require 'byebug' | |
require 'dry/monads' | |
require 'dry/monads/do' | |
require 'dry/matcher/result_matcher' | |
module Dry | |
module Transaction | |
module Steps | |
class Abstract | |
include Dry::Monads[:result] | |
attr_reader :name | |
attr_reader :object | |
def initialize(name, object = nil) | |
@name = name | |
@object = object | |
end | |
def call(input) | |
object.public_send(name, input) | |
end | |
def bind(object) | |
self.class.new(name, object) | |
end | |
end | |
class Step < Abstract | |
end | |
class Map < Abstract | |
end | |
class Tee < Abstract | |
def call(input) | |
super | |
Success(input) | |
end | |
end | |
end | |
module DSL | |
def step(name) | |
__steps__ << Steps::Step.new(name) | |
end | |
def map(name) | |
__steps__ << Steps::Map.new(name) | |
end | |
def tee(name) | |
__steps__ << Steps::Tee.new(name) | |
end | |
def __steps__ | |
@__steps__ ||= [] | |
end | |
end | |
def self.included(klass) | |
super | |
klass.class_eval do | |
extend DSL | |
include Dry::Monads[:result, :try] | |
include Dry::Monads::Do.for(:call) | |
include Dry::Matcher.for(:call, with: Dry::Matcher::ResultMatcher) | |
end | |
end | |
attr_reader :steps | |
def initialize(*) | |
@steps = self.class.__steps__.map { |step| step.bind(self) } | |
end | |
def call(input) | |
steps.reduce(input) do |value, curr_step| | |
case curr_step | |
when Steps::Tee | |
curr_step.(value) | |
else | |
yield(curr_step.(value)) | |
end | |
end | |
end | |
end | |
end | |
RSpec.describe Dry::Transaction do | |
subject(:transaction) do | |
Class.new do | |
include Dry::Transaction | |
step :validate | |
map :transform | |
tee :log | |
attr_reader :logs | |
def initialize | |
super | |
@logs = [] | |
end | |
def validate(params) | |
if params.key?('email') | |
Success(params) | |
else | |
Failure('no email') | |
end | |
end | |
def transform(params) | |
Success(email: params.fetch('email')) | |
end | |
def log(values) | |
logs << values | |
end | |
def call(input) | |
res = yield super | |
res = yield extract(res) | |
Success(res) | |
end | |
private | |
def extract(params) | |
Success(params[:email]) | |
end | |
end.new | |
end | |
it 'works with a success matcher' do | |
result = nil | |
transaction.('email' => '[email protected]') do |m| | |
m.success do |value| | |
result = value | |
end | |
m.failure do |_| | |
end | |
end | |
expect(transaction.logs).to eql('email' => '[email protected]') | |
expect(result).to eql('[email protected]') | |
end | |
it 'works with a failure matcher' do | |
result = nil | |
transaction.('foo': '[email protected]') do |m| | |
m.success do |value| | |
result = value | |
end | |
m.failure do |error| | |
result = error | |
end | |
end | |
expect(transaction.logs).to eql('email' => '[email protected]') | |
expect(result).to eql('no email') | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment