我创建了一个DSL来定义ruby中的工厂。有没有更好的方法呢?

时间:2014-05-28 05:25:14

标签: ruby metaprogramming

我有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__做的那样。我不能定义一个额外的方法,还有另一种方法来做到这一点。 :(

任何见解都将不胜感激。

2 个答案:

答案 0 :(得分:0)

一种方法是使用throwcatch

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