rails 部分模板中的可选局部变量:我如何摆脱(定义的?foo)混乱?

Posted

技术标签:

【中文标题】rails 部分模板中的可选局部变量:我如何摆脱(定义的?foo)混乱?【英文标题】:optional local variables in rails partial templates: how do I get out of the (defined? foo) mess? 【发布时间】:2011-01-04 20:34:29 【问题描述】:

如果渲染部分时没有在 :locals 哈希中明确定义值,我在部分模板中使用以下语法为局部变量设置默认值 --

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

直到最近,这似乎工作得很好,当时(我无法辨别)未传递的变量开始表现得好像它们已被定义为 nil(而不是未定义)。

正如各种乐于助人的人在 SO 上指出的那样,http://api.rubyonrails.org/classes/ActionView/Base.html 使用

defined? foo

改为使用

local_assigns.has_key? :foo

我正在尝试修改我的方式,但这意味着要更改很多模板。

我可以/是否应该提前收费并在所有模板中进行此更改?我需要注意什么技巧吗?我需要多努力地测试每一项?

【问题讨论】:

这个问题已经很老了,所有的答案似乎都已经过时了,因为现在的Action View documentation,特别说可以用definded? foo"另外,你也可以用defined ? 使用前先检查变量是否已赋值的标题。” 【参考方案1】:

怎么样

<% foo ||= default_value %>

这表示“如果 foo 不是 nil 或 true,则使用它。否则将 default_value 分配给 foo”

【讨论】:

我不确定这是否有效,因为 foo 没有定义,除非它通过本地哈希传递。 这行得通,但如果你有这样的默认值,也许这表明你应该使用帮助器? 这里没有魔法。有关该主题的更多资源:groups.google.com/group/comp.lang.ruby/browse_thread/thread/… 我真的很喜欢这个版本,因为它的语法非常紧凑。我想最大的缺点是这意味着您不能将 nil 或 false 作为本地值传递,因为它将被默认覆盖。 @brahn,这是一个很好的观点。事实上,如果foo 是布尔值,则应该避免这种情况。它可能正确地具有false 的值,并被default_value 意外覆盖。【参考方案2】:

我这样做:

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

【讨论】:

虽然我真的很喜欢 hgimenez 建议的简洁语法(上图),但这种方法的优点是非常清楚:发生了什么。 (仍然有不让您将 nil 作为本地值传递的缺点) 你想要传递 nil 的用例是什么? 哦,我没有具体的案例。只是试图了解全部含义。这提醒我接受这个答案:-) 为了解决 nil 问题,我从 OP 的链接 (api.rubyonrails.org/classes/ActionView/Base.html) 标题: -- has_key 避免了 nil / false 的情况,并且可能可以像这里的答案一样缩短为一行 请检查 Pablo 的回答:local_assigns.fetch 完美处理具有 nil 值的键。仅当密钥完全设置时,它才会返回默认值。【参考方案3】:

可以创建如下所示的助手:

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

请参阅my blog,了解有关如何以及为什么的详细信息。

请注意,此解决方案确实允许您将 nil 或 false 作为值传递而不会被覆盖。

【讨论】:

【参考方案4】:

更直观、更紧凑:

&lt;% some_local = default_value unless local_assigns[:some_local] %&gt;

【讨论】:

我认为如果你用:locals =&gt; :some_local =&gt; false调用部分,这将失败【参考方案5】:

我认为允许多个默认变量的更好选择:

<% 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] %>

【讨论】:

【参考方案6】:

我认为这应该在这里重复(来自http://api.rubyonrails.org/classes/ActionView/Base.html):

如果您需要在特定的渲染调用中找出某个局部变量是否已被赋值,您需要使用以下模式:

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

使用定义的测试?标题行不通。这是一个实施限制。

【讨论】:

【参考方案7】:

由于local_assigns 是一个哈希,您也可以将fetch 与可选的default_value 一起使用。

local_assigns.fetch :foo, default_value

如果未设置 foo,这将返回 default_value

警告:

default_value 是一种方法时,请注意local_assigns.fetch :foo, default_value,因为无论如何都会调用它以将其结果传递给fetch

如果您的default_value 是一个方法,您可以将它包装在一个块中:local_assigns.fetch(:foo) default_value 以防止在不需要时调用它。

【讨论】:

值得明确地说:nil 值被保留在这里。如果哈希包含映射到nil:foo,那么fetch 将返回nil。也就是说,至少在我的 v1.9.3 上。我不记得 1.8 的表现如何。 完全正确。它让我想起了 local_assigns[:foo] || default_value 的问题,当 foo 返回一个虚假值时,将使用 default_value 代替。对于Memoization@some_value ||= expensive_method来说通常是一个问题,如果该方法返回一个假值,它总是会被执行。 任何不明白为什么这是最佳答案的人都没有使用 ruby​​ 足够长的时间。布拉沃巴勃罗! 以下是优化,因此您只需在模板顶部调用一次,而不是在每次使用变量时都使用“获取保护”。 foo ||= local_assigns[:foo] = local_assigns.fetch(:foo, default_value) 我想知道为什么它不起作用,我认为它也创建了变量,但我们仍然必须使用返回值:foo = local_assigns.fetch :foo, true【参考方案8】:

如果您不想在每次调用时都将局部变量传递给局部变量,请执行以下操作:

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

这样可以避免undefined variable 错误。这将允许您调用带/不带局部变量的局部变量。

【讨论】:

OR local_param = local_param if defined?(local_param)【参考方案9】:

这是 Pablo 答案的派生词。这允许我设置一个默认值('full'),最后,在 local_assigns 和一个实际的局部变量中都设置了 'mode'。

haml/苗条:

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

erb:

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

【讨论】:

【参考方案10】:

我知道这是一个旧线程,但这是我的小贡献:我会在部分内部的 条件 中使用local_assigns[:foo].presence。 然后我只在渲染调用中需要时设置foo

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

看看official Rails guide here。从 RoR 3.1.0 开始有效。

【讨论】:

我看不出local_assigns[:foo]local_assigns[:foo].presence 之间有什么真正的区别。如果哈希中不存在键,则任何一个都将返回nil,如果存在则返回值。【参考方案11】:

就我而言,我使用:

<% variable ||= "" %>

在我的部分。 我不知道这是否好,但对我来说还可以

【讨论】:

这实际上效果很好。适用于未定义以及在部分调用中将nil 作为本地传递时。 啊,往下看,如果variable 是一个布尔值并且你需要将它设置为false,这将失败。它将使用默认值而不是使用false 值。使用风险自负。 这是最干净的方法,没有真正的混淆。【参考方案12】:

红宝石 2.5

Erb

这是可能的,但你必须在范围内声明你的默认值。

VARIABLE 替换词。

# index.html.erb
...
<%= render 'some_content', VARIABLE: false %>
...

# _some_content.html.erb
...
<% VARIABLE = true if local_assigns[:VARIABLE].nil? %>
<% if VARIABLE %>
    <h1>Do you see me?</h1>
<% end %>
...

【讨论】:

以上是关于rails 部分模板中的可选局部变量:我如何摆脱(定义的?foo)混乱?的主要内容,如果未能解决你的问题,请参考以下文章

定义? Ruby 和 Rails 中的方法

Rails 3:“未定义的局部变量或方法”如果我将内容放在部分中

在rails mailer上调用deliver_later之前存储当前的可选路由范围

在模板基类中为继承类中的可选覆盖生成虚拟方法

Ansible Playbook 中的可选变量覆盖

忽略表达式中的可选前缀