Last active
October 3, 2022 10:15
-
-
Save solnic/66d663790a956fc811ee376249ec0447 to your computer and use it in GitHub Desktop.
This file contains 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 |
@jslucas-root what a funny coincident that you wrote here because I started working on something last week 🤞
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can't wait to see this in a release