2013-10-22 70 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

獲得它希望我可以做到這一點,無需通過任何東西,但看起來像它不可能 –