使用默认命名参数将`nil`传递给方法

时间:2016-03-01 18:51:13

标签: ruby named-parameters

在Rails项目中,我收集了一个包含10-15个键值对的哈希,并将其传递给类(服务对象)进行实例化。应该从哈希值中设置对象属性,除非没有值(或nil)。在这种情况下,该属性最好设置为默认值。

在创建对象之前,我不想检查散列中的每个值是否都是nil,而是希望找到一种更有效的方法。

我尝试使用默认值的命名参数。我不知道这是否有意义,但我希望在使用nil调用参数时使用默认值。我为此功能创建了一个测试:

class Taco
  def initialize(meat: "steak", cheese: true, salsa: "spicy")
    @meat = meat
    @cheese = cheese
    @salsa = salsa
  end
  def assemble
    "taco with: #@meat + #@cheese + #@salsa"
  end
end

options1 = {:meat => "chicken", :cheese => false, :salsa => "mild"}
chickenTaco = Taco.new(options1)
puts chickenTaco.assemble
# => taco with: chicken + false + mild

options2 = {}
defaultTaco = Taco.new(options2)
puts defaultTaco.assemble
# => taco with: steak + true + spicy

options3 = {:meat => "pork", :cheese => nil, :salsa => nil}
invalidTaco = Taco.new(options3)
puts invalidTaco.assemble
# expected => taco with: pork + true + spicy
# actual => taco with: pork +  +

3 个答案:

答案 0 :(得分:1)

使用命名参数传递值后,对该方法调用访问该参数的默认值。

您必须(i)不在方法配置文件中分配默认值,而是在方法体中分配sagarpandya82的答案,或者(ii)在传递参数之前删除nil值这样的方法使用Rails' Hash#compact

options3 = {:meat => "pork", :cheese => nil, :salsa => nil}
invalidTaco = Taco.new(options3.compact)

答案 1 :(得分:1)

如果您想遵循面向对象的方法,可以使用单独的方法隔离默认值,然后使用Hash#merge

class Taco
  def initialize (args)
    args = defaults.merge(args)
    @meat   = args[:meat]
    @cheese = args[:cheese]
    @salsa  = args[:salsa]
  end

  def assemble
     "taco with: #{@meat} + #{@cheese} + #{@salsa}"
  end

  def defaults
   {meat: 'steak', cheese: true, salsa: 'spicy'}
  end  
end

然后按照@sawa的建议(谢谢),使用Rails'Hash#compact作为明确定义nil值的输入哈希值,您将得到以下输出:

taco with: chicken + false + mild
taco with: steak + true + spicy
taco with: pork + true + spicy

修改

如果您不想使用Rails的精彩Hash#compact方法,则可以使用Ruby的Array#compact方法。将initialize方法中的第一行替换为:

args = defaults.merge(args.map{|k, v| [k,v] if v != nil }.compact.to_h)

答案 2 :(得分:0)

我不认为关键字参数适合您的情况。似乎Hash更适合。

class Taco
    attr_accessor :ingredients

    def initialize(ingredients = {})
        @ingredients = ingredients
    end

    def assemble
        "taco with: #{ingredients[:meat]} + #{ingredients[:cheese]} + #{ingredients[:salsa]}"
    end
end

您甚至可以缩短assemble方法以列出所有成分

def assemble
    string = "taco with: " + ingredients.values.join(" + ")
end

它会像你期待的那样起作用

options1 = {:meat => "chicken", :cheese => false, :salsa => "mild"}
chicken_taco = Taco.new(options1)
puts chicken_taco.assemble() # output: taco with: chicken + false + mild

值得一提的是,Ruby更喜欢chicken_tacos而不是chickenTacos