rails部分テンプレートのオプションのローカル変数:(定義された?foo)混乱からどうやって抜け出すのですか?


225

私はひどい子供であり、部分テンプレートをレンダリングするときに値が:localsハッシュで明示的に定義されていない場合、部分テンプレートで次の構文を使用してローカル変数のデフォルト値を設定しました-

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

これは最近まで正常に機能していたようで、(理由がわからない)渡されなかった変数が、(未定義ではなく)nilに定義されているかのように動作し始めました。

SOのさまざまな役立つ人々によって指摘されているように、http://api.rubyonrails.org/classes/ActionView/Base.htmlは使用しないように言ってます

defined? foo

代わりに使用する

local_assigns.has_key? :foo

私は自分のやり方を修正しようとしていますが、それは多くのテンプレートを変更することを意味します。

前払いして、すべてのテンプレートでこの変更を行うことはできますか?注意が必要な注意点はありますか?それぞれをどれくらい熱心にテストする必要がありますか?

回答:


324

私はこれをします:

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

1
私はhgimenezの提案(上記)のコンパクトな構文が本当に好きですが、このアプローチには、何が起こっているのかが非常に明確になるという利点があります。(それでも、ローカルの値としてnilを渡させないという欠点があります)
brahn

1
nilを渡したい場合の使用例は何ですか?
jonnii 2010年

ああ、具体的なケースは考えていません。完全な影響を理解しようとしています。これは私にこの答えを受け入れることを思い出させました:-)
brahn

4
nilの問題を解決するために、OPのリンク(api.rubyonrails.org/classes/ActionView/Base.html)からコードをコピーしています<%if local_assigns.has_key?:headline%> Headline:<%= headline%> <%end%>-has_keyはnil / falseの状況を回避し、おそらくここの答えのように1行に短縮できます
Phil

5
Pabloの答えを確認してください:local_assigns.fetchnil値のキーでさえ完全に処理します。キーがまったく設定されていない場合にのみ、デフォルトを返します。
ケツァルコアトル2013

158

local_assignsはハッシュなので、オプションのでフェッチを使用することもできますdefault_value

local_assigns.fetch :foo, default_value

設定されていないdefault_value場合fooは、これが返されます。

警告:

local_assigns.fetch :foo, default_valuewhen default_valueがメソッドに結果を渡すためにとにかく呼び出されるため、when はメソッドに注意してくださいfetch

default_valueメソッドの場合は、それをブロックにラップして、local_assigns.fetch(:foo) { default_value }不要なときに呼び出されないようにすることができます。


1
明示的に言っておくnil価値があります。値はここに保持されます。ハッシュが含まれている場合:fooにマッピングされnil、その後、fetchそれが返されますnil。つまり、少なくとも私のv1.9.3では。1.8の動作を覚えていません。
ケツァルコアトル2013

その通りです。それは私がの問題を覚えていlocal_assigns[:foo] || default_valueます、fooが偽の値を返すとき、default_value代わりにが使用されます。@some_value ||= expensive_methodメソッドが偽の値を返す場合、それは通常メモ化の問題であり、常に実行されます。
Pablo Cantero 2013

1
これが最良の答えである理由を理解していない人は、ルビーを十分に長く使用していません。ブラボー・パブロ!
mastaBlasta 2014年

2
最適化は次のとおりです。そのため、変数を使用するたびに「フェッチガード」を使用するのではなく、テンプレートの上部で一度呼び出すだけで済みます。 foo ||= local_assigns[:foo] = local_assigns.fetch(:foo, default_value)
sethcall 2014年

それが機能しなかった理由は、私が思っていた、私はそれがあまりにも変数を作成したと仮定、我々はまだ返された値を使用する必要があります:foo = local_assigns.fetch :foo, true
Vadorequest

84

いかがですか

<% foo ||= default_value %>

これは、「foonilまたはtrueでない場合に使用します。それ以外の場合はdefault_valuefooに割り当てます」


2
ローカルハッシュを介して渡されない限り、fooが定義されていないため、これが機能するかどうかはわかりません。
jonnii

1
これは機能しますが、このようなデフォルト値がある場合、おそらくヘルパーを使用する必要があるという兆候ですか?
psyho 2010年

2
ここには魔法はありません。テーマに関するより多くの資源:groups.google.com/group/comp.lang.ruby/browse_thread/thread/...
hgmnz

37
構文がとてもコンパクトなので、私はこのバージョンが本当に好きです。大きな欠点は、ローカルの値としてnilまたはfalseを渡すことができないことですが、デフォルトで上書きされるためです。
ブラン、

16
@brahn、それは良い点です。実際、これがfooブール値である場合は、これを回避する必要があります。それは当然の値を持ち、誤ってfalse上書きされる可能性がありますdefault_value
hgmnz、2010年

10

私はこれをここで繰り返す必要があると思います(http://api.rubyonrails.org/classes/ActionView/Base.htmlから):

特定のローカル変数に特定のレンダーコールで値が割り当てられているかどうかを確認する必要がある場合は、次のパターンを使用する必要があります。

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

定義済みを使用したテスト?見出しは機能しません。これは実装上の制限です。


6

私の場合、私は以下を使用します:

<% variable ||= "" %>

私の部分で。
それが良いかどうかはわかりませんが、私にとっては大丈夫です


これは実際にはかなりうまくいきます。未定義の場合、およびnil部分呼び出しでローカルとして渡す場合にも機能します。
Joshua Pinter、

3
ああ、以下を読んで、これがvariableブール値であり、に設定する必要がある場合、これは失敗しfalseます。値を使用する代わりにデフォルト値を使用しfalseます。自己責任。
ジョシュア・ピンター

5

私はそれが古いスレッドであることを知っていますが、これが私の小さな貢献です:パーシャル内の条件で使用local_assigns[:foo].presenceします。次に、レンダーコールで必要な場合にのみ設定します。foo

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

TEを見て公式Railsがここに案内します。RoR 3.1.0以降で有効です。


私は間のいずれかの本当の違いを見ることができないlocal_assigns[:foo]としますlocal_assigns[:foo].presencenilハッシュにキーが存在しない場合はどちらかが返され、存在する場合は値が返されます。
jamesmarkcook 2017

1

私は複数のデフォルト変数を許可するより良いオプションを考えます:

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

1

これはパブロの答えの派生物です。これにより、デフォルト(「フル」)を設定でき、最後に、「モード」がlocal_assignsと実際のローカル変数の両方に設定されます。

haml / slim:

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

erb:

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

0

より直感的でコンパクト:

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


3
パーシャルでwithを呼び出すと、これは失敗すると思います:locals => {:some_local => false}
brahn

0

呼び出すたびにローカル変数をパーシャルに渡したくない場合は、次のようにします。

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

これにより、undefined variableエラーを回避できます。これにより、ローカル変数の有無にかかわらず部分コードを呼び出すことができます。


またはlocal_param = local_param if defined?(local_param)
Kinaan Khan Sherwani

0

Ruby 2.5

Erb

可能ですが、デフォルト値をスコープで宣言する必要があります。

置換する単語を可変にします。

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

-6

ヘルパーは次のように作成できます。

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を値として渡すことができることに注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.