2013-10-22 71 views
1

我正在尝试编写一个DSL来包装Mongoid的聚合管道(即Mongo DB)。根据实例eval将实例变量传递给DSL

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

所以我可以做:

class Project 
    include MongoidAggregationHelper 
end 

result = Project.pipeline do 
    match dept_id: 1 
end 

#...works! 

“匹配”是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 
+0

你可以试一下'def pipeline(* args,&block)'和'Project.pipeline dept_id:dept_id do'吗? – MrYoshiji

+0

好的建议@MrYoshiji,在生产代码中我这样做,它的工作原理。我希望避免必须将变量作为选项散列传递,然后使用之前声明的变量(如果有的话)。 –

+0

我发布了一个替代答案 – MrYoshiji

回答

1

你可以做到以下几点:(论点无限量的,还挺有使用哈希)

# 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 
+0

获得它希望我可以做到这一点,无需通过任何东西,但看起来像它不可能 –