是否有一种干净的方法可以避免在嵌套的params哈希中调用nil上的方法?

时间:2011-03-25 07:42:39

标签: ruby-on-rails ruby hashmap null

我有兴趣获取params哈希的嵌套'name'参数。调用类似

的内容
params[:subject][:name]
当params [:subject]为空时,

会抛出错误。为了避免这个错误,我通常写这样的东西:

if params[:subject] && params[:subject][:name]

有更简洁的方法来实现这个吗?

12 个答案:

答案 0 :(得分:19)

检查Ick可能是。您不需要显着重构代码,只需在必要时插入可能代理:

params[:subject].maybe[:name]

同一位作者(raganwald)也写了andand,也有同样的想法。

答案 1 :(得分:17)

  1. 您可以使用#try,但我认为这不会更好:

    params[:subject].try(:[], :name)
    
  2. 或者使用#fetch默认参数:

    params.fetch(:subject, {}).fetch(:name, nil)
    
  3. 或者您可以将#default=设置为新的空哈希,但不要尝试修改从此返回的值:

    params.default = {}
    params[:subject][:name]
    

    它也打破了所有存在的简单测试,所以你不能写:

    if params[:subject]
    

    因为它将返回空哈希,现在你必须为每个测试添加#present?调用。

    此外,当key没有值时,它总会返回哈希,即使你期望字符串也是如此。

  4. 但是从我看到的,你尝试提取嵌套参数,而不是将其分配给模型并放置你的逻辑。如果你有Subject模型,那么只需指定:

    @subject = Subject.new(params[:subject])
    

    shuld提取用户填写的所有参数。然后,您尝试保存它们,以查看用户是否传递了有效值。

    如果您担心访问用户不应设置的字段,请为允许使用质量分配设置的字段添加attr_accessible白名单(如我的示例所示,使用@subject.attributes = params[:subject]更新)

答案 2 :(得分:9)

Ruby 2.3.0使#dig

更容易实现
h = {foo: {bar: {baz: 1}}}

h.dig(:foo, :bar, :baz)           #=> 1
h.dig(:foo, :zot, :baz)           #=> nil

答案 3 :(得分:5)

params[:subject].try(:[], :name)最干净的方式

答案 4 :(得分:4)

当我在编码时遇到同样的问题时,我有时会使用`rescue'。

name = params[:subject][:name] rescue ""
# => ""

这不礼貌,但我觉得这很简单。

编辑:我不再经常使用这种方式了。我建议tryfetch

答案 5 :(得分:2)

不是真的。您可以尝试fetchtry(来自ActiveSupport),但它并不比您现有的更干净。

更多信息:

更新:忘记了andand

andand可以让您:

params[:user].andand[:name] # nil guard is built-in

同样,您可以使用Ick library per the answer above中的maybe

答案 6 :(得分:2)

或者,向其添加[]

class NilClass; def [](*); nil end end
params[:subject][:name]

答案 7 :(得分:1)

class Hash
  def fetch2(*keys)
    keys.inject(self) do |hash, key|
      hash.fetch(key, Hash.new)
    end
  end
end

e.g。

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

答案 8 :(得分:1)

我从这里的答案中透露了这个:

How to check if params[:some][:field] is nil?

我一直在寻找更好的解决方案。

所以我想让我们用try以不同的方式测试所设置的嵌套键:

params[:some].try(:has_key?, :field)

这还不错。如果未设置,则会nilfalse进行比较。如果参数设置为true,您也会获得nil

答案 9 :(得分:0)

我为这个用例写了Dottie - 深入到哈希,而不先知道整个预期的树是否存在。语法比使用try(Rails)或maybe(Ick)更简洁。例如:

# in a Rails request, assuming `params` contains:
{ 'person' => { 'email' => 'jon@example.com' } } # there is no 'subject'

# standard hash access (symbols will work here
# because params is a HashWithIndifferentAccess)
params[:person][:email] # => 'jon@example.com'
params[:subject][:name] # undefined method `[]' for nil:NilClass

# with Dottie
Dottie(params)['person.email'] # => 'jon@example.com'
Dottie(params)['subject.name'] # => nil

# with Dottie's optional class extensions loaded, this is even easier
dp = params.dottie
dp['person.email'] # => 'jon@example.com'
dp['subject.name'] # => nil
dp['some.other.deeply.nested.key'] # => nil

如果您想了解更多信息,请查看文档:{​​{3}}

答案 10 :(得分:0)

我用过:

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

答案 11 :(得分:0)

您可以使用内联分配来避免双重哈希访问:

my_param = subj_params = params[:subject] && subj_params[:name]