Необязательные локальные переменные в частичных шаблонах rails: как мне выйти из (определенного? foo) беспорядка?

Я был плохим парнем и использовал следующий синтаксис в своих частичных шаблонах для установки значений по умолчанию для локальных переменных, если значение не было явно определено в хеше locals при рендеринге частичного

<% foo = default_value unless (defined? foo) %>

Кажется, что это было хорошо до недавнего времени, когда (без каких-либо причин я мог различить), не прошедшие переменные начали вести себя так, как если бы они были определены как nil (а не undefined).

Как было указано различными полезными людьми на SO, http://api.rubyonrails.org/classes/ActionView/Base.html говорит, что не использовать

defined? foo

и вместо этого использовать

local_assigns.has_key? :foo

Я пытаюсь изменить свои способы, но это означает изменение большого количества шаблонов.

Можно/должен ли я просто заряжать вперед и внести это изменение во все шаблоны? Есть ли какая-то хитрость, на которую мне нужно следить? Насколько усердно мне нужно проверить каждый?

Ответ 1

Я делаю это:

<% some_local = default_value if local_assigns[:some_local].nil? %>

Ответ 2

Так как local_assigns является хешем, вы также можете использовать fetch с опцией default_value.

local_assigns.fetch :foo, default_value

Это вернет default_value, если foo не был установлен.

ВНИМАНИЕ:

Будьте осторожны с local_assigns.fetch :foo, default_value, когда default_value - это метод, так как он будет вызываться в любом случае, чтобы передать его результат на fetch.

Если ваш default_value - это метод, вы можете обернуть его в блок: local_assigns.fetch(:foo) { default_value }, чтобы предотвратить его вызов, когда он не нужен.

Ответ 3

Как насчет

<% foo ||= default_value %>

Это говорит: "Используйте foo, если это не nil или true. В противном случае назначьте default_value foo"

Ответ 4

Я думаю, это нужно повторить здесь (из http://api.rubyonrails.org/classes/ActionView/Base.html):

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

<% if local_assigns.has_key? :headline %>
  Headline: <%= headline %>
<% end %>

Тестирование с использованием определенных? заголовок не будет работать. Это ограничение реализации.

Ответ 5

Я знаю, что это старый поток, но здесь мой небольшой вклад: я использовал бы local_assigns[:foo].presence в условном внутри частичного. Затем я устанавливаю foo только в случае необходимости в вызове рендеринга:

<%= render 'path/to/my_partial', always_present_local_var: "bar", foo: "baz" %>

Посмотрите на официальное руководство Rails здесь . Действителен с RoR 3.1.0.

Ответ 6

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

<% options = local_assigns.reverse_merge(:include_css => true, :include_js => true) %>
<%= include_stylesheets :national_header_css if options[:include_css] %>
<%= include_javascripts :national_header_js if options[:include_js] %>

Ответ 7

Это производная от ответа Пабло. Это позволяет установить значение по умолчанию ( "полный" ), и в конце "режим" устанавливается как в local_assign, так и в локальной локальной переменной.

Haml/тонкий:

- mode ||= local_assigns[:mode] = local_assigns.fetch(:mode, 'full')

Еврорадио:

<% mode ||= local_assigns[:mode] = local_assigns.fetch(:mode, 'full') %>

Ответ 8

В моем случае я использую:

<% variable ||= "" %>

в моем частичном.
У меня нет идеи, если это хорошо, но для меня все в порядке.

Ответ 9

Более интуитивно понятный и компактный:

<% some_local = default_value unless local_assigns[:some_local] %>

Ответ 10

Если вы не хотите передавать локальную переменную в частичную при каждом ее вызове, вы делаете это:

<% local_param = defined?(local_param) ? local_param : nil %>

Таким образом вы избегаете ошибки undefined variable. Это позволит вам называть ваши частичные с/без локальных переменных.

Ответ 11

Помощник может быть создан таким образом:

somearg = opt(:somearg) { :defaultvalue }

Реализовано как:

module OptHelper
  def opt(name, &block)
    was_assigned, value = eval(
      "[ local_assigns.has_key?(:#{name}), local_assigns[:#{name}] ]", 
      block.binding)
    if was_assigned
      value
    else
      yield
    end
  end
end

Подробнее о том, как и почему, см. мой блог.

Обратите внимание, что это решение позволяет вам передавать nil или false как значение без его переопределения.