Sono interessato a ottenere il parametro ‘nome’ nidificato di un hash params. Chiamando qualcosa come
params[:subject][:name]
genera un errore quando params [: subject] è vuoto. Per evitare questo errore, di solito scrivo qualcosa del genere:
if params[:subject] && params[:subject][:name]
C’è un modo più pulito per implementare questo?
Forse è il caso di Ick . Non è necessario ridigitare in modo significativo il codice, basta interspassare forse i proxy quando necessario:
params[:subject].maybe[:name]
Lo stesso autore ( raganwald ) scrisse anche e, con la stessa idea.
Puoi usare #try
, ma non penso che sia molto meglio:
params[:subject].try(:[], :name)
Oppure usa #fetch
con il parametro predefinito:
params.fetch(:subject, {}).fetch(:name, nil)
Oppure puoi impostare #default=
con un nuovo hash vuoto, ma poi non provare a modificare i valori restituiti da questo:
params.default = {} params[:subject][:name]
Rompe anche tutti i test semplici per l’esistenza, quindi non puoi scrivere:
if params[:subject]
perché restituirà l’hash vuoto, ora devi aggiungere #present?
chiama ad ogni test.
Inoltre, restituisce sempre hash quando non c’è alcun valore per la chiave, anche quando ci si aspetta una stringa.
Ma da quello che vedo, si tenta di estrarre il parametro nidificato, invece di assegnarlo al modello e di mettere la propria logica. Se hai il modello Subject
, allora semplicemente assegna:
@subject = Subject.new(params[:subject])
shuld estrae tutti i parametri compilati dall’utente. Quindi provi a salvarli, per vedere se l’utente ha passato valori validi.
Se ti preoccupi dell’accesso ai campi che l’utente non deve impostare, aggiungi la attr_accessible
bianca attr_accessible
per i campi a cui dovrebbe essere consentito impostare l’assegnazione di massa (come nel mio esempio, con @subject.attributes = params[:subject]
per l’aggiornamento)
Ruby 2.3.0 rende questo molto facile da fare con #dig
h = {foo: {bar: {baz: 1}}} h.dig(:foo, :bar, :baz) #=> 1 h.dig(:foo, :zot, :baz) #=> nil
params[:subject].try(:[], :name)
è il modo più pulito
Quando ho lo stesso problema nella codifica, a volte uso `rescue ‘.
name = params[:subject][:name] rescue "" # => ""
Non sono buone maniere, ma penso che sia un modo semplice.
EDIT: non uso più spesso in questo modo. Consiglio di try
o fetch
.
Non proprio. Puoi provare a fetch
o try
(da ActiveSupport) ma non è molto più pulito di quello che hai già.
Maggiori informazioni qui:
AGGIORNAMENTO: dimenticati di andand
:
andand
ti permette di fare:
params[:user].andand[:name] # nil guard is built-in
Allo stesso modo, puoi usare maybe
dalla libreria Ick per la risposta sopra .
Oppure aggiungi []
ad esso.
class NilClass; def [](*); nil end end params[:subject][:name]
class Hash def fetch2(*keys) keys.inject(self) do |hash, key| hash.fetch(key, Hash.new) end end end
per esempio
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
Ho incrociato questo post dalla mia risposta qui:
Come verificare se params [: some] [: field] è nullo?
Ho cercato anche una soluzione migliore.
Quindi ho pensato di try
un modo diverso per testare l’impostazione di una chiave annidata:
params[:some].try(:has_key?, :field)
Non è male. Ottieni nil
vs. false
se non è impostato. Si ottiene anche true
se il parametro è impostato su nil
.
Ho scritto Dottie solo per questo caso d’uso, raggiungendo in profondità un hash senza prima sapere se esiste l’intero albero atteso. La syntax è più sintetica rispetto all’utilizzo di try
(Rails) o maybe
(Ick). Per esempio:
# 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
Guarda i documenti se vuoi vedere di più: https://github.com/nickpearson/dottie
Ero solito:
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])
dà:
=> "Jack" => "use internet" => nil
Puoi evitare il doppio accesso ad hash con un incarico in linea:
my_param = subj_params = params[:subject] && subj_params[:name]