基于实例eval将实例变量传递给DSL

时间:2013-10-22 14:03:38

标签: ruby-on-rails ruby dsl instance-variables proc

我正在尝试编写一个DSL来封装Mongoid的聚合管道(即Mongo DB)。

我创建了一个模块,当包含它时,添加一个接受块的类方法,它将一个块传递给一个将请求传递给Mongoid的对象(通过方法丢失)。

所以我能做到:

class Project
  include MongoidAggregationHelper
end

result = Project.pipeline do
  match dept_id: 1
end

#...works!

“match”是Mongoid聚合管道上的一种方法,它被拦截并传递。

但是在块外部设置的实例变量不可用,因为它是在代理类的上下文中执行的。

dept_id = 1

result = Project.pipeline do
  match dept_id: dept_id
end

#...fails, dept_id not found :(

用块传递/重新定义外部实例变量的任何方法吗?

以下是修剪后的代码:

module MongoidAggregationHelper
  def self.included base
    base.extend ClassMethods
  end

  module ClassMethods
    def pipeline &block
      p = Pipeline.new self
      p.instance_eval &block
      return p.execute
    end
  end

  class Pipeline
    attr_accessor :cmds, :mongoid_class
    def initialize klass
      self.mongoid_class = klass
    end

    def method_missing name, opts={}
      #...proxy to mongoid...
    end

    def execute
      #...execute pipeline, return the results...
    end
  end
end

1 个答案:

答案 0 :(得分:2)

您可以执行以下操作:(无限量的参数,有点必须使用哈希)

# definition
def pipeline(*args, &block)
  # your logic here
end

# usage
dept_id = 1
result = Project.pipeline(dept_id: dept_id) do
  match dept_id: dept_id
end

或者您可以使用命名参数,如果您知道执行DSL需要多少参数

# definition
def pipeline(dept_id, needed_variable, default_variable = false, &block)
  # your logic here
end

# usage
dept_id = 1
result = Project.pipeline(dept_id, other_variable) do
  match dept_id: dept_id
end