Skip to content

Instantly share code, notes, and snippets.

@dnagir
Last active May 3, 2019 02:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dnagir/71907e1b09e8be817d0644b5be1c29eb to your computer and use it in GitHub Desktop.
Save dnagir/71907e1b09e8be817d0644b5be1c29eb to your computer and use it in GitHub Desktop.
Dry Transaction Inheritance Repro
require 'bundler/inline'
gemfile do
gem 'dry-validation', '=1.0.0.rc1'
gem 'dry-transaction', '=0.13.0'
gem 'minitest', '=5.11.3'
end
require 'dry-validation'
require 'dry/transaction'
require 'minitest/autorun'
# Generilise any API commands
class ApiCommand
include Dry::Transaction
def self.build(validate_with:, and_then:)
Class.new(ApiCommand) do
def validation_class
validate_with
end
def command_class
and_then
end
end
end
def self.handle(params)
new.call(params)
end
step :validate
step :build_command
step :execute
private
def validate(params)
result = validation_class.new.call(params)
result.errors.any? ? Failure(result.errors) : Success(result.to_h)
end
def build_command(valid_params)
Success command_class.new(valid_params)
end
def execute(valid_command)
Success 'yay'
end
end
class BlogValidator < Dry::Validation::Contract
params do
required(:name).filled
end
end
CreateBlog = Struct.new(:name, keyword_init: true) # in practice - Dry::Struct
# Concrete Api command
class CreateBlogCommandWithInheritance < ApiCommand.build(validate_with: BlogValidator, and_then: CreateBlog)
end
# Same as before logically (but is failing) - copy-paste with no inheritance
class CreateBlogCommandUsualUse
include Dry::Transaction
step :validate
step :build_command
step :execute
private
def validation_class
BlogValidator
end
def command_class
CreateBlog
end
def validate(params)
result = validation_class.new.call(params)
result.errors.any? ? Failure(result.errors) : Success(result.to_h)
end
def build_command(valid_params)
Success command_class.new(valid_params)
end
def execute(valid_command)
Success 'yay'
end
end
class DefinedDirectlyOnAClassAndWorksFine < Minitest::Test
include Dry::Monads::Result::Mixin
def test_invalid
assert_equal({ name: ["must be filled"] }, CreateBlogCommandUsualUse.new.call(name: '').failure.to_h)
end
def test_valid
assert_equal Success('yay'), CreateBlogCommandUsualUse.new.call(name: 'foo')
end
end
class InheritedAndHasUnexpectedBahviour < Minitest::Test
include Dry::Monads::Result::Mixin
def test_invalid
assert_equal({ name: ["must be filled"] }, CreateBlogCommandWithInheritance.new.call(name: '').failure.to_h)
end
def test_valid
assert_equal Success('yay'), CreateBlogCommandWithInheritance.new.call(name: 'foo')
end
end
# Running:
FF..
Finished in 0.003001s, 1332.8891 runs/s, 1332.8891 assertions/s.
1) Failure:
InheritedAndHasUnexpectedBahviour#test_invalid [-:120]:
Expected: {:name=>["must be filled"]}
Actual: {}
2) Failure:
InheritedAndHasUnexpectedBahviour#test_valid [-:124]:
Expected: Success("yay")
Actual: Success({:name=>"foo"})
4 runs, 4 assertions, 2 failures, 0 errors, 0 skips
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment