2011-07-25 11 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のようには見えません。 DSLコード(行34)の@block.callinstance_exec(@block)に置き換えることで、私は欲しいものを手に入れましたが、その後ActionViewの良さ(つまりlink_to)を失います。

Railsに含まれるActionPackのヘルパーメソッドの可用性を維持しながら、私の小さなDSLクラスのインスタンスメソッドをブロックで利用できるようにする方法はありますか?

答えて

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 
関連する問題