Ответ 1
Проверьте Ick. Вам не нужно значительно реорганизовывать свой код, просто перефразируйте, возможно, прокси, когда это необходимо:
params[:subject].maybe[:name]
Тот же автор (raganwald) также написал andand, с той же идеей.
Мне интересно получить вложенный параметр 'name' хэша params. Вызов чего-то типа
params[:subject][:name]
выдает ошибку, когда параметры [: subject] пусты. Чтобы избежать этой ошибки, я обычно пишу что-то вроде этого:
if params[:subject] && params[:subject][:name]
Есть ли более чистый способ реализовать это?
Проверьте Ick. Вам не нужно значительно реорганизовывать свой код, просто перефразируйте, возможно, прокси, когда это необходимо:
params[:subject].maybe[:name]
Тот же автор (raganwald) также написал andand, с той же идеей.
Вы можете использовать #try
, но я не думаю, что это намного лучше:
params[:subject].try(:[], :name)
Или используйте #fetch
с параметром по умолчанию:
params.fetch(:subject, {}).fetch(:name, nil)
Или вы можете установить #default=
на новый пустой хэш, но не пытайтесь изменить значения, возвращаемые из этого:
params.default = {}
params[:subject][:name]
Он также разбивает все простые тесты на существование, поэтому вы не можете писать:
if params[:subject]
потому что он вернет пустой хэш, теперь вам нужно добавить вызов #present?
для каждого теста.
Также это всегда возвращает хеш, когда нет значения для ключа, даже если вы ожидаете строку.
Но из того, что я вижу, вы пытаетесь извлечь вложенный параметр, вместо того, чтобы назначать его модели и там размещать свою логику. Если у вас есть модель Subject
, просто присвойте:
@subject = Subject.new(params[:subject])
shuld извлекает все ваши параметры, заполненные пользователем. Затем вы попытаетесь сохранить их, чтобы проверить, не прошли ли пользователь допустимые значения.
Если вы беспокоитесь о доступе к полям, которые пользователь не должен устанавливать, добавьте attr_accessible
белый список для полей whoich, которым должно быть разрешено устанавливать с массовым назначением (как в моем примере, с @subject.attributes = params[:subject]
для обновления)
Ruby 2.3.0 делает это очень легко сделать с # dig
h = {foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) #=> 1
h.dig(:foo, :zot, :baz) #=> nil
params[:subject].try(:[], :name)
- самый чистый способ
Когда у меня такая же проблема при кодировании, я иногда использую `rescue '.
name = params[:subject][:name] rescue ""
# => ""
Это не хорошие манеры, но я думаю, что это простой способ.
EDIT: Я больше не использую этот способ. Я рекомендую try
или fetch
.
Не совсем. Вы можете попробовать fetch
или try
(из ActiveSupport), но он не намного чище, чем у вас уже есть.
Подробнее здесь:
UPDATE: Забыл andand
:
andand
позволяет:
params[:user].andand[:name] # nil guard is built-in
Аналогично, вы можете использовать maybe
из Ick library за ответ выше.
Или добавьте к нему []
.
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
например.
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
Я перекрестился с этим ответом здесь:
Как проверить, нет ли параметров [: some] [: field] nil?
Я искал лучшее решение.
Итак, я решил позволить использовать try
другой способ проверить наличие вложенного ключа:
params[:some].try(:has_key?, :field)
Это не плохо. Вы получите nil
vs. false
, если он не установлен. Вы также получаете true
, если параметр имеет значение nil
.
Я написал Dottie только для этого случая использования - доходит до хэша, не зная, существует ли все ожидаемое дерево. Синтаксис более краткий, чем использование try
(Rails) или maybe
(Ick). Например:
# 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 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
Проверьте документы, если вы хотите увидеть больше: https://github.com/nickpearson/dottie
Я использовал:
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])
дает:
=> "Jack"
=> "use internet"
=> nil
Вы можете избежать двойного хеш-доступа с помощью встроенного назначения:
my_param = subj_params = params[:subject] && subj_params[:name]