2011-07-25 8 views
2

我正在嘗試寫一個小型的Ruby DSL,並且偶然發現了一個不便之處。現在我的「DSL」的代碼是https://gist.github.com/0379b07f516f4f322204和我的執行代碼(.html.erb文件中)是:如何編寫與塊的環境不是隔離的DSL?

html_table_for @users do |t, user| 
    t.field :username, link_to(user.username, :action => 'show', :id => user.id) 
    t.field :email 
    t.field :roles, user.roles.map(&:code).join(', ') 
end 

的實現代碼看起來並不很像,因爲t.field而不是簡單地field的DSL。通過用instance_exec(@block)代替DSL代碼(第34行)中的@block.call,我幾乎可以得到我想要的,但是我失去了所有的ActionView優點(即link_to)。

有沒有辦法在塊中提供我的小DSL類的實例方法,同時保持Rails中包含的ActionPack的輔助方法的可用性?

回答

1

這是可能的(雖然哈克和混亂),以獲得兩個由using method delegation

class TableHelper 
    def initialize(&block) 
    # get a reference to 'self' in the block's scope: 
    @self_before_instance_eval = eval "self", block.binding 
    instance_eval &block 
    end 

    # delegate all unknown methods to the calling object: 
    def method_missing(method, *args, &block) 
    @self_before_instance_eval.send method, *args, &block 
    end 

    # Other helper methods: 

    def field(name, url) 
    # ... 
    end 

end 

現在你可以使用這樣的:雖然使用這種方法時

def some_helper(arg); end 

Table.new do 
    field :name, some_helper("foo") 
end 

要小心:仍不能使用來自調用對象的實例變量:

@field_url = "http://foo" 

Table.new do 
    # url will be nil, since the ivar does not exist on the Table 
    field :name, @field_url 
end