我正在寻找一种简洁的方法来评估object
并在条件得到验证时返回object
,否则nil
以便我可以使用默认值。类似的东西:
result = object.verify?{ |object| object.test? } || default_value
我可以看到实现这个的几种方法,但我希望有一种内置的方法来实现这一点。例如:
转到Array
级别
def verify?(&block)
Array(self).filter(block).first
end
使用instance_eval
def verify?(&block)
self.instance_eval{ |object| yield(object) ? object : nil}
end
修改
这是我的实际例子(虽然这个问题没有被提到):
class User < ActiveRecord::Base
def currency
self.billing_information.try(:address).try(:country).try(:currency_code).instance_eval{ |currency| Finance::CURRENCIES.include?(currency) ? currency : nil} || 'EUR'
end
end
我知道这很丑陋,但我确实喜欢它的逻辑:如果我正在寻找的对象存在,那就去找下一个。第一个条件是存在(使用try
),然后是包含。
答案 0 :(得分:4)
关于您的实际问题 - Rails提供presence_in
:
如果接收器包含在参数中,则返回接收器,否则返回
nil
。
'EUR'.presence_in %w(EUR USD) #=> "EUR"
'JPY'.presence_in %w(EUR USD) #=> nil
我可能会将实际货币与经过验证的货币分开(因此您仍然可以访问前一种货币):
class User < ActiveRecord::Base
def currency
billing_information.try(:address).try(:country).try(:currency_code)
end
def verified_currency
Finance::CURRENCIES.include?(currency) ? currency : 'EUR'
end
end
并移动检查货币的逻辑并将默认货币提供给Finance
:
class User < ActiveRecord::Base
def currency
billing_information.try(:address).try(:country).try(:currency_code)
end
def verified_currency
Finance.verified_currency(currency)
end
end
module Finance
CURRENCIES = %w(EUR USD)
DEFAULT_CURRENCY = 'EUR'
def self.verified_currency(currency)
CURRENCIES.include?(currency) ? currency : DEFAULT_CURRENCY
end
end
这也避免了必须两次评估User#currency
。
try
- 链可以替换为delegate
:
class User < ActiveRecord::Base
delegate :currency, to: billing_information, allow_nil: true
def verified_currency
Finance.verified_currency(currency)
end
end
class BillingInformation < ActiveRecord::Base
delegate :currency, to: address, allow_nil: true
end
class Address < ActiveRecord::Base
delegate :currency, to: country, allow_nil: true
end
答案 1 :(得分:3)
如果在条件满足时需要接收器,则假设test?
在满足条件时返回真值,否则返回假值:
result = object.tap{|object| break unless object.test?} || default_value
或者,按照Stefan的建议:
result = object.tap{|object| break default_value unless object.test?}
答案 2 :(得分:2)
我认为@Stefan的建议是最好的,但是如果你坚持要迟钝,你可以写:
(object.test? && object) || default
答案 3 :(得分:0)
如果您认为需要可以处理默认值的Object#try
版本,则可以对其进行修补。
require "rails"
class Object
alias_method :old_try, :try
def try(params, default = nil, &block)
old_try(params, &block) || default
end
end
object = {}
default = "I'm default"
p result = object.try(:test, default)
#=> I'm default
答案 4 :(得分:-1)
我认为您正在寻找Enumerable#detect
:
detect(ifnone = nil) { |obj| block } → obj or nil
将enum
中的每个条目传递给block
。返回block
不是false
的第一个。 如果没有匹配的对象,则调用ifnone并在指定时返回其结果,否则返回nil
。(1..10).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> nil (1..100).find { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
所以你可以按照自己的要求做result = object.detect { |object| object.test? } || default_value
,或者更加惯用result = object.detect(default_value) { |object| object.test? }
。