Skip to content

Instantly share code, notes, and snippets.

@serradura
Last active March 30, 2023 12:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save serradura/8d78880f0aef8924d3242824bc4c9917 to your computer and use it in GitHub Desktop.
Save serradura/8d78880f0aef8924d3242824bc4c9917 to your computer and use it in GitHub Desktop.
NanoCase: Abstraction inspired by the u-case gem (compatible with old and new Rubies)
class NanoCase
class Result
UNDEFINED = :undefined
attr_reader :type, :data
def initialize(type, data)
@type = type
@data = data
end
def success?
raise NotImplementedError
end
def failure?
!success?
end
def on_success(type = UNDEFINED, &block)
self.tap { block.call(data) if success? && self.type == type }
end
def on_failure(type = UNDEFINED, &block)
self.tap { block.call(data) if failure? && self.type == type }
end
def and_then(&block)
success? ? block.call(data) : self
end
end
class Success < Result
def success?
true
end
end
class Failure < Result
def success?
false
end
end
def self.call(**args)
result = new(**args).call
return result if result.is_a?(Result)
raise TypeError, "#{self.name}#call must return a Result"
end
def initialize(**)
raise NotImplementedError
end
def call
raise NotImplementedError
end
private
def Success(type = Result::UNDEFINED, data: nil)
Success.new(type, data).freeze
end
def Failure(type = Result::UNDEFINED, data: nil)
Failure.new(type, data).freeze
end
end
class CreateUser < NanoCase
attr_reader :name
def initialize(name:)
@name = name
end
def call
return Failure(data: 'name must be a String') unless name.is_a?(String)
Success data: {user: {name: name}}
end
end
# ---
puts CreateUser.call(name: nil).inspect
#<NanoCase::Failure:0x0062adb0 @type=:undefined, @data="name must be a String">
CreateUser.call(name: nil).on_failure { |data| p data }
CreateUser.call(name: nil).on_success { raise 'Never will reach here' }
CreateUser.call(name: nil).on_failure(:different_type) { raise 'Never will reach here' }
# ---
puts CreateUser.call(name: 'Serra').inspect
#<NanoCase::Success:0x0062abb8 @type=:undefined, @data={:user=>{:name=>"Serra"}}>
CreateUser.call(name: 'Serra').on_success { |data| p data }
CreateUser.call(name: 'Serra').on_failure { raise 'Never will reach here' }
CreateUser.call(name: 'Serra').on_success(:different_type) { raise 'Never will reach here' }
class CreateUser < NanoCase
attr_reader :name
def initialize(name:)
@name = name
end
def call
validate_name
.and_then { create_user }
end
private
def validate_name
name.is_a?(String) ? Success() : Failure(data: 'name must be a String')
end
def create_user
Success(data: {user: {name: name}})
end
end
# ---
result1 = CreateUser.call(name: nil)
puts result1.inspect
#<NanoCase::Failure:0x00628ab8 @type=:undefined, @data="name must be a String">
result1.on_failure { |data| p data }
# "name must be a String"
result1.on_success { raise 'Never will reach here' }
result1.on_failure(:different_type) { raise 'Never will reach here' }
# ---
result2 = CreateUser.call(name: 'Serradura')
puts result2.inspect
#<NanoCase::Success:0x006288d8 @type=:undefined, @data={:user=>{:name=>"Serradura"}}>
result2.on_success { |data| p data }
# {:user=>{:name=>"Serradura"}}
result2.on_failure { raise 'Never will reach here' }
result2.on_success(:different_type) { raise 'Never will reach here' }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment