2016-03-01 46 views
2

在Rails项目上,我收集了一个带有10-15个键值对的散列,并将它传递给一个类(服务对象)进行实例化。除非没有值(或nil),否则应从哈希中的值设置对象属性。在这种情况下,该属性将被理想地设置为默认值。将`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 + + 
+1

感谢格式帮助泽 – phpete

回答

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 

然后下面的S通过@sawa uggestion(感谢),使用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) 
+0

除了'Hash#compact'之外,还使用'Hash#merge'是确保我的对象获得默认值而不是'nil'值的一个好方法。非常感谢你! – phpete

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 

值得一提的是红宝石喜欢chicken_tacos超过chickenTacos

+1

谢谢您的回答!有没有什么办法可以为你的答案中提到的一些'成分'哈希键提供默认值?例如,如果“肉”不是作为配料给出的,它仍然会打印“taco:{default_meat} + {other_ingredient_a} + ...'? – phpete

+1

'成分[:肉] || =“默认肉类”将允许您有条件地为该键分配默认值,如果没有给出。 –

1

一旦您使用命名参数传递值,则该方法调用将无法访问该参数的默认值。

您必须(i)不是在方法配置文件中指定默认值,而是在方法主体中指定默认值,如在sagarpandya82的答案中;或者(ii)在将参数传递给像这样的方法之前,删除nil值, Hash#compact

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

我删除了我的答案,因为我无法获得OP所需的输出。如果你知道如何解决随意融入你的答案。 –

+0

@ sagarpandya82我的答案没有看到任何问题。至少,它比接受的答案要好得多,它甚至没有考虑到默认值(除了对OP评论的事后回应外)。 – sawa

+0

OP想要第一个串有'taco:chicken + false + mild'。我的代码打印出'taco:chicken + true + mild'。这是唯一的原因。你能看到修复吗? –