ruby-on-rails - query - rails errors
¿Hay alguna manera clara de evitar llamar a un método en nil en un hash de parámetros anidado? (12)
Puedes usar
#try
, pero no creo que sea mucho mejor:params[:subject].try(:[], :name)
O usa
#fetch
con el parámetro predeterminado:params.fetch(:subject, {}).fetch(:name, nil)
O puede configurar
#default=
a hash vacío nuevo, pero luego no intente modificar los valores devueltos por esto:params.default = {} params[:subject][:name]
También rompe todas las pruebas simples de existencia, por lo que no puedes escribir:
if params[:subject]
porque devolverá hash vacío, ahora tienes que agregar
#present?
llamar a cada prueba.También esto siempre devuelve hash cuando no hay ningún valor para la clave, incluso cuando espera una cadena.
Pero por lo que veo, intentas extraer el parámetro anidado, en lugar de asignarlo al modelo y ubicar tu lógica. Si tiene un modelo de Subject
, simplemente asigne:
@subject = Subject.new(params[:subject])
shuld extraerá todos los parámetros que el usuario haya rellenado. Luego intente guardarlos para ver si el usuario pasó valores válidos.
Si está preocupado por acceder a los campos que el usuario no debe configurar, agregue la lista blanca attr_accessible
para los campos a los que se les debe permitir asignarlos en masa (como en mi ejemplo, con @subject.attributes = params[:subject]
para la actualización)
Esta pregunta ya tiene una respuesta aquí:
Estoy interesado en obtener el parámetro "nombre" anidado de hash de params. Llamando algo así como
params[:subject][:name]
arroja un error cuando params [: subject] está vacío. Para evitar este error, suelo escribir algo como esto:
if params[:subject] && params[:subject][:name]
¿Hay una manera más limpia de implementar esto?
Cuando tengo el mismo problema en la codificación, a veces uso `rescue ''.
name = params[:subject][:name] rescue ""
# => ""
Esto no es buenos modales, pero creo que es una manera simple.
EDITAR: Ya no uso esta manera a menudo. Recomiendo try
o fetch
.
Escribí a Dottie solo para este caso de uso: profundizar en un hash sin saber primero si existe todo el árbol esperado. La sintaxis es más sucinta que usar try
(Rails) o maybe
(Ick). Por ejemplo:
# in a Rails request, assuming `params` contains:
{ ''person'' => { ''email'' => ''[email protected]'' } } # there is no ''subject''
# standard hash access (symbols will work here
# because params is a HashWithIndifferentAccess)
params[:person][:email] # => ''[email protected]''
params[:subject][:name] # undefined method `[]'' for nil:NilClass
# with Dottie
Dottie(params)[''person.email''] # => ''[email protected]''
Dottie(params)[''subject.name''] # => nil
# with Dottie''s optional class extensions loaded, this is even easier
dp = params.dottie
dp[''person.email''] # => ''[email protected]''
dp[''subject.name''] # => nil
dp[''some.other.deeply.nested.key''] # => nil
Consulte los documentos si desea ver más: Dottie
Lo crucé por mi respuesta aquí:
¿Cómo verificar si params [: some] [: field] es nil?
He estado buscando una mejor solución también.
Así que pensé que vamos a usar una forma diferente de probar una clave anidada que se está configurando:
params[:some].try(:has_key?, :field)
No está mal. Obtienes nil
vs. false
si no está configurado. También se true
si el parámetro está establecido en nil
.
O bien, agregue []
a ella.
class NilClass; def [](*); nil end end
params[:subject][:name]
Puede evitar el doble acceso hash con una asignación en línea:
my_param = subj_params = params[:subject] && subj_params[:name]
Realmente no. Puedes intentar fetch
o try
(desde ActiveSupport) pero no es mucho más limpio que lo que ya tienes.
Más información aquí:
ACTUALIZACIÓN: se olvidó de andand
:
andand
te deja hacer:
params[:user].andand[:name] # nil guard is built-in
Del mismo modo, puede usar maybe
de la biblioteca Ick por la respuesta anterior .
Ruby 2.3.0 hace que esto sea muy fácil de hacer con #dig
h = {foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) #=> 1
h.dig(:foo, :zot, :baz) #=> nil
Solía:
params = {:subject => {:name => "Jack", :actions => {:peaceful => "use internet"}}}
def extract_params(params, param_chain)
param_chain.inject(params){|r,e| r=((r.class.ancestors.include?(Hash)) ? r[e] : nil)}
end
extract_params(params, [:subject,:name])
extract_params(params, [:subject,:actions,:peaceful])
extract_params(params, [:subject,:actions,:foo,:bar,:baz,:qux])
da:
=> "Jack"
=> "use internet"
=> nil
params[:subject].try(:[], :name)
es la forma más limpia
class Hash
def fetch2(*keys)
keys.inject(self) do |hash, key|
hash.fetch(key, Hash.new)
end
end
end
p.ej
require ''minitest/autorun''
describe Hash do
it "#fetch2" do
{ yo: :lo }.fetch2(:yo).must_equal :lo
{ yo: { lo: :mo } }.fetch2(:yo, :lo).must_equal :mo
end
end