Ruby on Rails: elimine varias claves hash

148

A menudo me encuentro escribiendo esto:

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

El rastro de eliminaciones no se siente bien y tampoco:

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

¿Hay algo más simple y más limpio?

Mark Westling
fuente
Cuando escribí que el segundo enfoque no se sentía bien, quise decir que dada la riqueza de la API Hash, sospechaba que ya había algún método o modismo para esto y que no sería necesario un parche de mono. Quizás no, sin embargo. Muchas gracias a todos los que respondieron!
Mark Westling
3
Hash # excepto que era exactamente lo que estaba buscando. No recordaba que se trata de una extensión principal de Rails, así que me quedé perplejo cuando no pude encontrarlo en la API de Hash.
Mark Westling
1
Tenga en cuenta que estrictamente la respuesta es Hash#except!pero Hash#exceptes el camino a seguir (¡no se meta con params!). Como regla general, no se meta con ningún objeto en su lugar a menos que sea absolutamente necesario, los efectos secundarios pueden tener resultados inesperados.
tokland

Respuestas:

219

Supongo que no conoce el Hash # excepto el método que ActiveSupport agrega a Hash.

Permitiría simplificar su código para:

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

Además, no tendrías que usar el parche de mono, ya que el equipo de Rails lo hizo por ti

Ben Crouse
fuente
1
Ahhh, sabía que había visto esto antes, ¡pero no podía recordar dónde! (De ahí mi comentario de "esto no se siente bien".) ¡Gracias!
Mark Westling
3
Uno de esos métodos menos documentados. Fui a buscar algo como esto mientras proponía una respuesta, pero no la vi.
tadman el
1
Por alguna razón, excepto que no funcionó. Pero lo except!hizo. Rails 3.0
Viaje el
44
Rails 3.2 en los atributos ActiveRecord, ¿tenía que usar cadenas para las claves? es decir, los User.attributes.except("id", "created_at", "updated_at")símbolos no funcionaron
house9
1
Además de lo que @ house9 mencionó, el attributesmétodo ActiveRecord devuelve un Hashcon claves que son String. Entonces, tendría que usar nombres de cadena de clave .except(). Sin embargo, evito esto usando Hash.symbolize_keysa la @user.attributes.symbolize_keys.except(:password, :notes)- el uso lo symbolize_keyshace funcionar como uno esperaría
FireDragon
44

Mientras usa Hash#exceptmaneja su problema, tenga en cuenta que presenta posibles problemas de seguridad . Una buena regla general para manejar cualquier información de los visitantes es utilizar un enfoque de lista blanca. En este caso, usando en su Hash#slicelugar.

params.slice!(:param_to_remove_1, :param_to_remove_2)
redirect_to my_path(params)
jsa
fuente
1
Gracias por mencionar los problemas de seguridad que rodean la redirección.
David J.
12
Solo un aviso: ActiveSupport, no el propio Ruby, proporciona Hash # slice y #slice. as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/…
David J.
1
No pude conseguir que el enlace de David James funcionara, pero este parece estar bien: api.rubyonrails.org/classes/Hash.html#method-i-slice
Dominic Sayers
método indefinido 'rebanada' para{:b=>2, :c=>3}:Hash
Khurram Raza
25

Estaría completamente satisfecho con el código que publicó originalmente en su pregunta.

[:controller, :action, :other_key].each { |k| params.delete(k) }
Bob Aman
fuente
sin modificar Hashesta es la mejor respuesta: +1:
Dan Bradbury
¡He usado este método pero reemplacé los parámetros con el nombre del hash y luego funcionó! El hash se muta.
Pablo
13

Otra forma de expresar la respuesta de dmathieu podría ser

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

¿Encender un parche de mono?

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
fuente
55
Los parches de mono son una herramienta de último recurso.
Bob Aman el
15
Los parches de mono que reemplazan las funciones existentes son una herramienta de último recurso. Los parches de mono que agregan nuevas funciones son Ruby 101.
David Seiler
44
Debería ser en delete(k)lugar dedelete(key)
Vincent
Para el mantenimiento del código, la implementación de lo no destructivo delete_keysdebería ser sencilladup.delete_keys!(*keys)
Phrogz el
@Phrogz Definir uno en términos del otro no siempre es una mala idea, pero se deja aquí desenrollado para mayor claridad.
tadman
2

No sé qué crees que está mal con tu solución propuesta. ¿Supongo que quieres un delete_allmétodo en Hash o algo así? Si es así, la respuesta de tadman proporciona la solución. Pero, francamente, para una sola vez, creo que su solución es extremadamente fácil de seguir. Si usa esto con frecuencia, es posible que desee envolverlo en un método auxiliar.

pesto
fuente