Блочный вызов в Ruby on Rails - PullRequest
2 голосов
/ 16 июня 2010

Я пытаюсь очистить свой код и избавиться от множества уродливых хешей.В моих представлениях я определяю несколько действий, подобных этому:

@actions = {
  :interest => {'Show interest', link_to(..), :disabled => true},
  :follow   => {'Follow this case', link_to(..)}
  ...
}

По мере роста этих хэшей удобство обслуживания уменьшается.Я хочу преобразовать вышеуказанный формат во что-то вроде:

actions do
   item :interest, 'Show interest', link_to(..), :disabled => true
   item :follow,   'Follow',        link_to(..)
   ...
end

Как мне структурировать мои вспомогательные методы, чтобы разрешить это?Предпочтительно, метод 'item' должен быть доступен только в блоке 'actions', а не в глобальной области.

Спасибо!

Ответы [ 4 ]

2 голосов
/ 16 июня 2010

я думаю, что этот метод называется «чистой комнатой», где у вас есть анонимный объект, содержащий метод, который вы хотите вызвать, чтобы метод был доступен только из вашего блока:

def actions(&block)
  cleanroom = Class.new{
    def item(*args)
      puts "these args were passed in: #{args.inspect}"
    end
  }
  cr = cleanroom.new
  cr.instance_eval &block
end

конечно, этот метод "item" просто помещает некоторый текст, но вы любите все, что вам нужно.

actions do
  item "foo", "bar", "baz"
end  #=> these args were passed in: ["foo", "bar", "baz"]
1 голос
/ 28 августа 2012

Я хотел сделать похожие вещи, и в итоге получил сложный, но очень полезный класс, который я назвал 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. : -)

1 голос
/ 16 июня 2010

Вот аналогичное решение, фактически создающее вашу структуру данных и избегающее создания нового класса при каждом вызове действий:

def action
  class << @actions ||= {}
    def item(name, *args) self[name] = args end
  end
  @actions.instance_eval(&Proc.new) if block_given?
  @actions
end

Теперь вы можете использовать dsl для построения этой структуры:

actions do
  item :interest, 'Show interest', link_to(..), :disabled => true
end

actions # => { :interest => [ 'Show interest', link_to(..), :disabled => true ] }

actions.item :follow, 'Follow', link_to(..)
0 голосов
/ 18 июня 2010

Я немного поэкспериментировал и в итоге нашел решение, которое работает до сих пор:

def actions(&block)
   @actions ||= []
   def item(id, content = '', options = {})
     @actions << [id, {
       :content => content || ''
     }.merge(options)]
   end
   block.call
end

Что в терминах позволяет мне делать следующее в моих взглядах:

actions do
  item :primary, link_to('Write letter', ...), :disabled => true
end

И переменная @ actions заполнена этими значениями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...