我有ruby应用程序,我想实现DSL来定义工厂。工厂是实例化某个对象,在进程中执行某些逻辑,执行一些验证并根据结果执行某些回调(成功或失败)的类:
f = Factory.new
f.create(foo: :bar) do |on|
on.success { puts 'hey from success action callback' }
on.failure { puts 'hey from failure action callback' }
end
好的,这并不难,但我也希望在方法失败或成功之后立即停止创建方法,例如:
def create(options = {})
# some logic
failed! # this method stops execution and yields the callback object
puts "you'll never see this"
end
我想出的是:https://gist.github.com/esdras/631a04769f24856c6d7f
请参阅以下部分版本:
require 'fiber'
class Factory
class Callbacks
# omitted some code here, this class is basically a container for
# success and failure callbacks
end
def failed!
@callbacks.failed!
resume_context
end
def succeeded!
@callbacks.succeeded!
resume_context
end
def resume_context ; Fiber.yield ; end
def self.handle(name, &method_body)
define_method "__original_#{name}__", &method_body
define_method name do |*args, &block|
@callbacks = Callbacks.new(self, block)
Fiber.new { send("__original_#{name}__", *args) }.resume
@callbacks
end
end
handle :create do |options = {}|
puts options.inspect
puts "in create"
succeeded!
puts 'after succeeded, never reached here'
end
end
正如您所看到的,类方法handle
定义了两种方法:__original_create__
和create
将__original_create__
包裹在Fiber
中以使其可以停止立即执行并执行回调。我的问题是:有更好的方法吗?没有创建__original_create__
方法,甚至没有使用Fibers?
我已经尝试过了:
def self.handle(name, &method_body)
define_method name do |*args, &block|
@callbacks = Callbacks.new(self, block)
Fiber.new { method_body.call *args }.resume
# above method_body is evaluated in the context of the class.
@callbacks
end
end
但是method_body
是在类的上下文中计算的,而不是实例:
我还尝试像这样实例method_body
:
def self.handle(name, &method_body)
define_method name do |*args, &block|
@callbacks = Callbacks.new(self, block)
Fiber.new { instance_eval &method_body }.resume
# above we lost the parameters defined by the handle method
@callbacks
end
end
但是我丢失了对以下定义的参数的引用:
handle :create do |param1, param2|
# method body
end
我找到的唯一方法是定义一个方法,将块传递给handle
方法,并在定义一个调用原始方法的包装器方法之后,就像我上面用__original_create__
做的那样。我不能定义一个额外的方法,还有另一种方法来做到这一点。 :(
任何见解都将不胜感激。
答案 0 :(得分:0)
一种方法是使用throw
和catch
。
def create(options = {})
case catch(:result) do
throw :result, :success if ...
throw :result, :error if ...
end
when :success then ...
when :error then ...
end
end
答案 1 :(得分:0)
我不确定你需要Fiber
,所以我会离开它,但你需要的是instance_exec
def self.handle(name, &method_body)
define_method name do |*args, &block|
@callbacks = Callbacks.new(self, block)
Fiber.new { instance_exec *args, &method_body }.resume
# above we lost the parameters defined by the handle method
@callbacks
end
end