Я хотел сделать похожие вещи, и в итоге получил сложный, но очень полезный класс, который я назвал DslProxy. Это часть моей жемчужины железных расширений, но вы можете вытащить ее и использовать или взглянуть на нее и посмотреть, как она работает.
Документы для DslProxy здесь: http://rubydoc.info/gems/iron-extensions/1.1.2/DslProxy
Репозиторий Github находится здесь: https://github.com/irongaze/iron-extensions
По сути, сделать это правильно сложно. Как уже отмечали другие, instance_eval, который обычно очень удобен для метапрограммирования, теряет вызывающий контекст / привязку, и поэтому вы теряете переменные вашего экземпляра. Все становится еще более пугающим, если вы хотите вкладывать в эти вызовы строителя.
Вот пример того, что может делать мой DslProxy:
class ItemBuilder
def actions(&block)
@actions = []
DslProxy.exec(self, &block)
@actions
end
def item(*args)
@actions << Item.new(*args)
end
end
# ... in your view ...
<%
@times = 5
builder = ItemBuilder.new
builder.actions do
item :foo, link_to(...)
@times.times do
item :bob, link_to(...)
end
end
%>
Контекст вызова сохраняется (например, вызовы link_to работают), переменные экземпляра распространяются (например, доступен @times), а методы, которые определяет экземпляр ItemBuilder, доступны без явного получателя (например, вызовы элемента работают как ожидалось) ).
Это, как и все метапрограммирование, является сложным. Возможно, вам будет полезно посмотреть спецификацию для этого класса здесь: https://github.com/irongaze/iron-extensions/blob/master/spec/extensions/dsl_proxy_spec.rb
Не стесняйтесь обращаться ко мне с вопросами или публиковать сообщения на моем трекере GitHub. : -)