Ruby on Rails: supprimer plusieurs clés de hachage

148

Je me retrouve souvent à écrire ceci:

params.delete(:controller)  
params.delete(:action)  
params.delete(:other_key)  
redirect_to my_path(params)  

La traînée de suppressions ne semble pas juste et non plus:

[:controller, :action, :other_key].each do |k|
  params.delete(k)
end

Y a-t-il quelque chose de plus simple et de plus propre?

Mark Westling
la source
Quand j'ai écrit que la deuxième approche ne me semblait pas juste, je voulais dire qu'étant donné la richesse de l'API Hash, je soupçonnais qu'il y avait déjà une méthode ou un idiome pour cela et qu'un patch de singe ne serait pas nécessaire. Peut-être pas, cependant. Un grand merci à tous ceux qui ont répondu!
Mark Westling
3
Hash # sauf que c'était exactement ce que je cherchais. Je ne me souvenais pas que c'était une extension principale de Rails, donc j'étais perplexe de ne pas la trouver dans l'API Hash.
Mark Westling
1
Notez que la réponse est strictement, Hash#except!mais Hash#exceptc'est la voie à suivre (ne vous embêtez pas params!). En règle générale, ne gâchez aucun objet en place à moins que cela ne soit absolument nécessaire, les effets secondaires peuvent avoir des résultats inattendus.
tokland

Réponses:

219

Je suppose que vous n'êtes pas au courant du Hash # sauf la méthode qu'ActiveSupport ajoute à Hash.

Cela permettrait à votre code d'être simplifié pour:

redirect_to my_path(params.except(:controller, :action, :other_key))

De plus, vous n'auriez pas besoin de patch monkey, puisque l'équipe Rails l'a fait pour vous!

Ben Crouse
la source
1
Ahhh, je savais que j'avais déjà vu ça mais je ne me souvenais pas où! (D'où ma remarque "ça ne va pas".) Merci!
Mark Westling
3
Une de ces méthodes moins documentées. J'ai cherché quelque chose comme ça en proposant une réponse mais je ne l'ai pas vue.
tadman
1
Pour une raison quelconque, sauf cela n'a pas fonctionné. Mais except!fait. Rails 3.0
Voyage du
4
Rails 3.2 sur les attributs ActiveRecord, a dû utiliser des chaînes pour les clés? ie les User.attributes.except("id", "created_at", "updated_at")symboles ne fonctionnaient pas
maison9
1
En plus de ce que @ house9 a mentionné, la attributesméthode ActiveRecord retourne un Hashavec des clés String. Vous devrez donc utiliser des noms de clé de chaîne dans .except(). Cependant, je contourne cela en utilisant le Hash.symbolize_keysa la @user.attributes.symbolize_keys.except(:password, :notes)- en utilisant le symbolize_keysfait que cela fonctionne comme on pouvait s'y attendre
FireDragon
44

Lorsque vous utilisez Hash#exceptgère votre problème, sachez que cela présente des problèmes de sécurité potentiels . Une bonne règle de base pour gérer les données des visiteurs consiste à utiliser une approche de liste blanche. Dans ce cas, utilisez à la Hash#sliceplace.

params.slice!(:param_to_remove_1, :param_to_remove_2)
redirect_to my_path(params)
jsa
la source
1
Merci d'avoir mentionné les problèmes de sécurité liés à la redirection.
David J.
12
Juste un avertissement: ActiveSupport, pas Ruby lui-même, fournit Hash # slice et #slice! as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/…
David J.
1
Je n'ai pas pu faire fonctionner le lien de David James mais celui-ci semble être correct: api.rubyonrails.org/classes/Hash.html#method-i-slice
Dominic Sayers
méthode non définie 'slice!' pour{:b=>2, :c=>3}:Hash
Khurram Raza
25

Je serais entièrement satisfait du code que vous avez initialement publié dans votre question.

[:controller, :action, :other_key].each { |k| params.delete(k) }
Bob Aman
la source
sans modifier Hashc'est la meilleure réponse: +1:
Dan Bradbury
J'ai utilisé cette méthode mais j'ai remplacé les paramètres par le nom du hachage, puis cela a fonctionné !! Le hachage est muté.
Pablo
13

Une autre façon de formuler la réponse de dmathieu pourrait être

params.delete_if { |k,v| [:controller, :action, :other_key].include? k }
Mike Seplowitz
la source
8

Allumez un patch de singe?

class Hash
  def delete_keys!(*keys)
    keys.flatten.each do |k|
      delete(k)
    end

    self
  end

  def delete_keys(*keys)
    _dup = dup
    keys.flatten.each do |k|
      _dup.delete(k)
    end

    _dup
  end
end
tadman
la source
5
Les patchs Monkey sont un outil de dernier recours.
Bob Aman
15
Les correctifs Monkey qui remplacent les fonctions existantes sont un outil de dernier recours. Les correctifs de singe qui ajoutent de nouvelles fonctions sont Ruby 101.
David Seiler
4
Devrait être à la delete(k)place dedelete(key)
Vincent
Pour la maintenance du code, la mise en œuvre de la non-destructive delete_keysdevrait être simplementdup.delete_keys!(*keys)
Phrogz
@Phrogz Définir l'un par rapport à l'autre n'est pas toujours une mauvaise idée, mais c'est juste ici déroulé pour plus de clarté.
tadman
2

Je ne sais pas ce qui ne va pas avec la solution que vous proposez. Je suppose que vous voulez une delete_allméthode sur Hash ou quelque chose? Si tel est le cas, la réponse de Tadman fournit la solution. Mais franchement, pour une seule fois, je pense que votre solution est extrêmement facile à suivre. Si vous l'utilisez fréquemment, vous voudrez peut-être l'envelopper dans une méthode d'assistance.

Pesto
la source