Variables locales facultatives dans les modèles partiels de rails: comment puis-je sortir du désordre (défini? foo)?

225

J'ai été un mauvais gamin et j'ai utilisé la syntaxe suivante dans mes modèles partiels pour définir des valeurs par défaut pour les variables locales si une valeur n'était pas explicitement définie dans le: hachage local lors du rendu du partiel -

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

Cela a semblé fonctionner correctement jusqu'à récemment, quand (pour aucune raison je ne pouvais discerner) les variables non passées ont commencé à se comporter comme si elles avaient été définies à zéro (plutôt qu'indéfinies).

Comme il a été souligné par diverses personnes utiles sur SO, http://api.rubyonrails.org/classes/ActionView/Base.html dit pas à l' utilisation

defined? foo

et au lieu d'utiliser

local_assigns.has_key? :foo

J'essaie de modifier mes manières, mais cela signifie changer beaucoup de modèles.

Puis-je / devrais-je simplement charger à l'avance et effectuer cette modification dans tous les modèles? Y a-t-il une astuce que je dois surveiller? Avec quelle diligence dois-je tester chacun d'eux?

brahn
la source

Réponses:

324

Je fais ça:

<% some_local = default_value if local_assigns[:some_local].nil? %>
jonnii
la source
1
Bien que j'aime vraiment la syntaxe compacte de la suggestion de hgimenez (ci-dessus), cette approche a l'avantage d'être très claire sur ce qui se passe. (A toujours l'inconvénient de ne pas vous laisser passer à zéro comme valeur pour le local)
brahn
1
Quel est votre cas d'utilisation pour vouloir passer à zéro?
jonnii
Oh, je n'ai pas de cas spécifique en tête. J'essaie juste de comprendre toutes les implications. Cela m'a rappelé d'accepter cette réponse :-)
brahn
4
Pour surmonter le problème nul, je copie le code à partir du lien OP ( api.rubyonrails.org/classes/ActionView/Base.html ) <% if local_assigns.has_key? : headline%> Headline: <% = headline%> <% end%> - has_key évite la situation nulle / fausse, et peut probablement être raccourci à une ligne comme la réponse ici
Phil
5
Veuillez vérifier la réponse de Pablo: local_assigns.fetchgère parfaitement les touches paires avec une valeur nulle. Il renvoie une valeur par défaut uniquement si la clé n'est pas définie du tout.
quetzalcoatl
158

Étant donné qu'il local_assignss'agit d'un hachage, vous pouvez également utiliser la récupération avec l'option default_value.

local_assigns.fetch :foo, default_value

Cela reviendra default_values'il foon'a pas été défini.

AVERTISSEMENT:

Faites attention à local_assigns.fetch :foo, default_valuequand default_valueest une méthode, car elle sera appelée de toute façon afin de transmettre son résultat à fetch.

Si votre default_valueest une méthode, vous pouvez l'encapsuler dans un bloc: local_assigns.fetch(:foo) { default_value }pour empêcher son appel quand il n'est pas nécessaire.

Pablo Cantero
la source
1
Cela vaut la peine de le dire explicitement: les nilvaleurs sont conservées ici. Si le hachage contient :foomappé sur nil, fetchil reviendra nil. C'est, au moins sur ma v1.9.3. Je ne me souviens pas comment 1.8 s'est comporté.
quetzalcoatl
C'est tout à fait ça. Il me rappelle le problème car local_assigns[:foo] || default_value, lorsque foo renvoie une valeur falsifiée, le default_valuesera utilisé à la place. C'est généralement un problème pour la mémorisation @some_value ||= expensive_methodsi la méthode retourne une valeur fausse, elle sera toujours exécutée.
Pablo Cantero
1
Quiconque ne comprend pas pourquoi c'est la meilleure réponse n'a pas utilisé suffisamment de rubis. Bravo Pablo!
mastaBlasta
2
Une optimisation est la suivante, de sorte que vous n'avez à l'appeler qu'une seule fois en haut de votre modèle, au lieu d'utiliser le 'fetch guard' à chaque utilisation de la variable. foo ||= local_assigns[:foo] = local_assigns.fetch(:foo, default_value)
sethcall
Je me demandais pourquoi cela ne fonctionnait pas, j'ai supposé qu'il créait aussi la variable, mais nous devons encore utiliser la valeur retournée:foo = local_assigns.fetch :foo, true
Vadorequest
84

Que diriez-vous

<% foo ||= default_value %>

Cela dit "utiliser foosi ce n'est pas nul ou vrai. Sinon, attribuer default_valueà foo"

hgmnz
la source
2
Je ne suis pas sûr que cela fonctionne car foo n'est pas défini à moins qu'il ne soit transmis via le hachage local.
jonnii
1
Cela fonctionne, mais si vous avez des valeurs par défaut comme celle-ci, c'est peut-être un signe que vous devez utiliser un assistant?
psyho
2
Pas de magie ici. Plus de ressources sur le sujet: groups.google.com/group/comp.lang.ruby/browse_thread/thread/…
hgmnz
37
J'aime vraiment cette version car la syntaxe est tellement compacte. Je suppose que le gros inconvénient est que cela signifie que vous ne pouvez pas passer nil ou faux comme valeur pour le local, car il sera écrasé par défaut.
brahn
16
@brahn, c'est un bon point. En fait, cela devrait être évité s'il foos'agit d'un booléen. Il peut légitimement avoir la valeur de falseet être remplacé par default_valueaccident.
hgmnz
10

Je pense que cela devrait être répété ici (à partir de http://api.rubyonrails.org/classes/ActionView/Base.html ):

Si vous devez savoir si une certaine variable locale a reçu une valeur dans un appel de rendu particulier, vous devez utiliser le modèle suivant:

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

Test en utilisant défini? le titre ne fonctionnera pas. Il s'agit d'une restriction d'implémentation.

gamov
la source
6

Dans mon cas, j'utilise:

<% variable ||= "" %>

dans mon partiel.
Je ne sais pas si c'est bien mais pour moi ça va

Moises Portillo
la source
Cela fonctionne plutôt bien. Fonctionne pour non défini et lors du passage en niltant que local dans l'appel partiel également.
Joshua Pinter
3
Ah, en lisant ci-dessous, cela échouera s'il variables'agit d'un booléen et vous devez le régler sur false. Il utilisera la valeur par défaut au lieu d'utiliser la falsevaleur. À UTILISER À VOS RISQUES ET PÉRILS.
Joshua Pinter
5

Je sais que c'est un vieux fil mais voici ma petite contribution: je l'utiliserais local_assigns[:foo].presenceau conditionnel à l'intérieur du partiel. Ensuite, je foone définis que lorsque cela est nécessaire dans l'appel de rendu:

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

Consultez le guide officiel de Rails ici . Valable à partir de RoR 3.1.0.

microspino
la source
Je ne vois aucune différence réelle entre local_assigns[:foo]et local_assigns[:foo].presence. Soit on retournera nilsi la clé n'existe pas dans le hachage et la valeur si elle existe.
jamesmarkcook
1

Je pense qu'une meilleure option qui permet de multiples variables par défaut:

<% 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
la source
1

Ceci est un dérivé de la réponse de Pablo. Cela me permet de définir une valeur par défaut («complet»), et à la fin, «mode» est défini à la fois dans local_assigns et dans une variable locale réelle.

haml / slim:

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

erb:

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

Plus intuitif et compact:

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

muirbot
la source
3
Je pense que cela échouera si vous appelez le partiel avec:locals => {:some_local => false}
brahn
0

Si vous ne voulez pas passer de variable locale à partielle à chaque appel, procédez comme suit:

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

De cette façon, vous évitez les undefined variableerreurs. Cela vous permettra d'appeler votre partiel avec / sans variables locales.

Haris Krajina
la source
OU local_param = local_param si défini? (Local_param)
Kinaan Khan Sherwani
0

Ruby 2.5

Erb

C'est possible, mais vous devez déclarer vos valeurs par défaut dans la portée.

VARIABLE le mot à remplacer.

# 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
la source
-6

Un assistant peut être créé pour ressembler à ceci:

somearg = opt(:somearg) { :defaultvalue }

Mis en œuvre comme:

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

Voir mon blog pour plus de détails sur comment et pourquoi.

Notez que cette solution vous permet de passer nil ou faux comme valeur sans qu'elle soit remplacée.

Jaime Cham
la source