Как написать DSL, который не изолирован от среды блока? - PullRequest
2 голосов
/ 25 июля 2011

Я пытаюсь написать небольшой Ruby DSL и наткнулся на неудобство.Прямо сейчас мой код "DSL" https://gist.github.com/0379b07f516f4f322204 и мой код реализации (внутри файла .html.erb):

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

Код реализации не очень похож на DSL из-заt.field вместо просто field.Заменив @block.call в коде DSL (строка 34) на instance_exec(@block), я почти получаю то, что хочу, но затем теряю все качества ActionView (а именно link_to).

Есть лиспособ сделать доступными в блоке методы экземпляра моего маленького класса DSL, в то же время сохраняя доступность вспомогательных методов ActionPack, включенных в Rails?

Ответы [ 2 ]

3 голосов
/ 31 октября 2012

Я разместил подробный ответ на аналогичный вопрос StackOverflow .

Краткий ответ: посмотрите на камень Docile - это очень просто для вас.

1 голос
/ 26 июля 2011

Можно (хотя и смешно и запутанно) получить оба, используя делегирование метода :

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
...