Optionale lokale Variablen in Schienen-Teilvorlagen: Wie komme ich aus dem (definierten? foo) Chaos heraus?

224

Ich war ein böses Kind und habe in meinen Teilvorlagen die folgende Syntax verwendet, um Standardwerte für lokale Variablen festzulegen, wenn beim Rendern des Teil-Hashs kein expliziter Wert im: local-Hash definiert wurde.

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

Dies schien bis vor kurzem gut zu funktionieren, als (ohne Grund, den ich erkennen konnte) nicht übergebene Variablen sich so verhielten, als wären sie auf Null (anstatt auf undefiniert) definiert worden.

Wie von verschiedenen hilfreichen Personen auf SO erwähnt, sagt http://api.rubyonrails.org/classes/ActionView/Base.html , nicht zu verwenden

defined? foo

und stattdessen zu verwenden

local_assigns.has_key? :foo

Ich versuche, meine Methoden zu ändern, aber das bedeutet, viele Vorlagen zu ändern.

Kann / sollte ich einfach im Voraus aufladen und diese Änderung in allen Vorlagen vornehmen? Gibt es irgendwelche Schwierigkeiten, auf die ich achten muss? Wie sorgfältig muss ich jeden einzelnen testen?

Brahn
quelle

Antworten:

323

Ich mache das:

<% some_local = default_value if local_assigns[:some_local].nil? %>
jonnii
quelle
1
Obwohl mir die kompakte Syntax von hgimenez 'Vorschlag (oben) sehr gut gefällt, hat dieser Ansatz den Vorteil, dass er sehr klar ist, was los ist. (
Hat
1
Was ist Ihr Anwendungsfall für den Wunsch, Null zu bestehen?
Jonnii
Oh, ich habe keinen bestimmten Fall im Sinn. Ich versuche nur, die vollständigen Auswirkungen zu verstehen. Dies erinnerte mich daran, diese Antwort zu akzeptieren :-)
Brahn
4
Um das Null-Problem zu lösen, kopiere ich den Code vom OP-Link ( api.rubyonrails.org/classes/ActionView/Base.html ) <% if local_assigns.has_key? : Überschrift%> Überschrift: <% = Überschrift%> <% end%> - has_key vermeidet die Null / Falsch-Situation und kann wahrscheinlich wie die Antwort hier auf eine Zeile gekürzt werden
Phil
5
Bitte überprüfen Sie die Antwort von Pablo: local_assigns.fetchBehandelt gerade Schlüssel mit Nullwert perfekt. Es wird nur dann eine Standardeinstellung zurückgegeben, wenn der Schlüssel überhaupt nicht festgelegt ist.
Quetzalcoatl
157

Da local_assignses sich um einen Hash handelt, können Sie auch fetch mit dem optionalen verwenden default_value.

local_assigns.fetch :foo, default_value

Dies wird zurückgegeben, default_valuewenn fooes nicht festgelegt wurde.

WARNUNG:

Seien Sie vorsichtig mit local_assigns.fetch :foo, default_valuewann default_valueist eine Methode, da sie sowieso aufgerufen wird, um ihr Ergebnis an zu übergeben fetch.

Wenn es sich bei Ihrer default_valueMethode um eine Methode handelt, können Sie sie in einen Block einschließen local_assigns.fetch(:foo) { default_value }, um den Aufruf zu verhindern, wenn sie nicht benötigt wird.

Pablo Cantero
quelle
1
Es lohnt sich, es ausdrücklich zu sagen: nilWerte bleiben hier erhalten. Wenn der Hash :foozugeordnet ist nil, fetchwird er zurückgegeben nil. Das heißt, zumindest auf meiner v1.9.3. Ich kann mich nicht erinnern, wie sich 1.8 verhalten hat.
Quetzalcoatl
Das ist völlig richtig. Es erinnert mich an das Problem, denn local_assigns[:foo] || default_valuewenn foo einen falschen Wert zurückgibt, default_valuewird stattdessen der verwendet. Es ist normalerweise ein Problem für Memoization, @some_value ||= expensive_methodwenn die Methode einen falschen Wert zurückgibt. Sie wird immer ausgeführt.
Pablo Cantero
1
Wer nicht versteht, warum dies die beste Antwort ist, hat Ruby nicht lange genug verwendet. Bravo Pablo!
MastaBlasta
2
Eine Optimierung ist die folgende, sodass Sie diese nur einmal oben in Ihrer Vorlage aufrufen müssen, anstatt bei jeder Verwendung der Variablen den 'Fetch Guard' zu verwenden. foo ||= local_assigns[:foo] = local_assigns.fetch(:foo, default_value)
Sethcall
Ich habe mich gefragt, warum es nicht funktioniert hat. Ich habe angenommen, dass es auch die Variable erstellt hat, aber wir müssen immer noch den zurückgegebenen Wert verwenden:foo = local_assigns.fetch :foo, true
Vadorequest
84

Wie wäre es mit

<% foo ||= default_value %>

Hier steht "benutze, foowenn es nicht null oder wahr ist. Sonst default_valuefoo zuweisen "

hgmnz
quelle
2
Ich bin nicht sicher, ob dies funktioniert, da foo nicht definiert ist, es sei denn, es wird über den lokalen Hash übergeben.
Jonnii
1
Dies funktioniert, aber wenn Sie solche Standardwerte haben, ist dies möglicherweise ein Zeichen dafür, dass Sie einen Helfer verwenden sollten?
Psyho
2
Keine Magie hier. Weitere Ressourcen zu diesem Thema: groups.google.com/group/comp.lang.ruby/browse_thread/thread/…
hgmnz
37
Ich mag diese Version wirklich, da die Syntax so kompakt ist. Ich nehme an, der große Nachteil ist, dass Sie nicht null oder falsch als Wert für den lokalen Wert übergeben können, da dieser standardmäßig überschrieben wird.
Brahn
16
@ Brahn, das ist ein guter Punkt. In der Tat sollte dies vermieden werden, wenn fooes sich um einen Booleschen Wert handelt. Es kann zu Recht den Wert von falsehaben und versehentlich überschrieben werden default_value.
hgmnz
10

Ich denke, dies sollte hier wiederholt werden (von http://api.rubyonrails.org/classes/ActionView/Base.html ):

Wenn Sie herausfinden möchten, ob einer bestimmten lokalen Variablen in einem bestimmten Renderaufruf ein Wert zugewiesen wurde, müssen Sie das folgende Muster verwenden:

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

Testen mit definiert? Überschrift wird nicht funktionieren. Dies ist eine Implementierungsbeschränkung.

Gamov
quelle
6

In meinem Fall verwende ich:

<% variable ||= "" %>

in meinem Teil.
Ich habe keine Ahnung, ob das gut ist, aber für mich ist es in Ordnung

Moises Portillo
quelle
Das funktioniert eigentlich ganz gut. Funktioniert für undefiniert und beim Übergeben nilals lokal im Teilaufruf.
Joshua Pinter
3
Ah, lesen Sie weiter unten, dies wird fehlschlagen, wenn variablees sich um einen Booleschen Wert handelt und Sie ihn einstellen müssen false. Es wird der Standardwert anstelle des falseWerts verwendet. BENUTZUNG AUF EIGENE GEFAHR.
Joshua Pinter
5

Ich weiß, dass es ein alter Thread ist, aber hier ist mein kleiner Beitrag: Ich würde local_assigns[:foo].presencein einer Bedingung innerhalb des Teils verwenden. Dann setze ich foonur bei Bedarf im Renderaufruf:

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

Schauen Sie sich hier den offiziellen Rails-Guide an . Gültig ab RoR 3.1.0.

Mikrospino
quelle
Ich kann keinen wirklichen Unterschied zwischen local_assigns[:foo]und sehen local_assigns[:foo].presence. Entweder wird zurückgegeben, nilwenn der Schlüssel nicht im Hash vorhanden ist, und der Wert, wenn er vorhanden ist.
Jamesmarkcook
1

Ich denke, eine bessere Option, die mehrere Standardvariablen zulässt:

<% 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] %>
Daniel OCallaghan
quelle
1

Dies ist eine Ableitung von Pablos Antwort. Auf diese Weise kann ich einen Standardwert ('full') festlegen, und am Ende wird 'mode' sowohl in local_assigns als auch in einer tatsächlichen lokalen Variablen festgelegt.

haml / schlank:

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

erb:

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

Intuitiver und kompakter:

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

Muirbot
quelle
3
Ich denke, das wird scheitern, wenn Sie den Teil mit:locals => {:some_local => false}
brahn
0

Wenn Sie die lokale Variable nicht bei jedem Aufruf an partiell übergeben möchten, gehen Sie wie folgt vor:

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

Auf diese Weise vermeiden Sie undefined variableFehler. Auf diese Weise können Sie Ihren Teil mit / ohne lokale Variablen aufrufen.

Haris Krajina
quelle
ODER local_param = local_param falls definiert? (Local_param)
Kinaan Khan Sherwani
0

Ruby 2.5

Erb

Es ist möglich, aber Sie müssen Ihre Standardwerte im Bereich deklarieren.

VARIABLE das Wort für Ersatz.

# 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 %>
...
Dimpiax
quelle
-6

Ein Helfer kann so erstellt werden:

somearg = opt(:somearg) { :defaultvalue }

Implementiert wie:

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

In meinem Blog finden Sie Details dazu, wie und warum.

Beachten Sie, dass Sie mit dieser Lösung nil oder false als Wert übergeben können, ohne dass dieser überschrieben wird.

Jaime Cham
quelle