lib/kind/functional/action.rb
# frozen_string_literal: true
require 'kind/functional'
require 'kind/functional/steps'
module Kind
module Functional::Action
CALL_TMPL = [
'def call(%s)',
' result = call!(%s)',
'',
' return result if Kind::Result::Monad === result',
'',
' raise Kind::Error, "#{self.class.name}#call! must return a Kind::Success or Kind::Failure"',
'end'
].join("\n").freeze
module Macros
def kind_functional_action!
return self if Kind.is?(Result::Methods, self)
public_methods = self.public_instance_methods - ::Object.new.methods
unless public_methods.include?(:call!)
raise Kind::Error.new("expected #{self} to implement `#call!`")
end
if public_methods.size > 1
raise Kind::Error.new("#{self} can only have `#call!` as its public method")
end
call_parameters = public_instance_method(:call!).parameters
if call_parameters.empty?
raise ArgumentError, "#{self.name}#call! must receive at least one argument"
end
def self.inherited(_)
raise RuntimeError, "#{self.name} is a Kind::Functional::Action and it can't be inherited"
end
call_parameters.flatten!
call_with_args = call_parameters.include?(:req) || call_parameters.include?(:rest)
call_with_kargs = call_parameters.include?(:keyreq) || call_parameters.include?(:keyrest)
call_tmpl_args = '*args, **kargs' if call_with_args && call_with_kargs
call_tmpl_args = '*args' if call_with_args && !call_with_kargs
call_tmpl_args = '**kargs' if !call_with_args && call_with_kargs
self.class_eval(
"def to_proc; @to_proc ||= method(:call!).to_proc; end" \
"\n" \
"def curry; @curry ||= to_proc.curry; end" \
"\n" \
"#{CALL_TMPL % [call_tmpl_args, call_tmpl_args]}"
)
if Kind.of_module?(self)
self.send(:extend, Functional::Steps)
else
self.send(:include, Functional::Steps)
self.send(:include, Functional::Behavior)
__dependencies__.freeze
end
self.send(:protected, :call!)
self
end
end
def self.included(base)
Kind.of_class(base).send(:extend, Functional::DependencyInjection)
base.send(:extend, Macros)
end
def self.extended(base)
base.send(:extend, Kind.of_module(base))
base.send(:extend, Macros)
end
private_constant :Macros, :CALL_TMPL
end
end