Seb*_* N. 11 ruby design-patterns smalltalk block
在Smalltalk中有方法ifNotNilDo:
它使用如下:
database getUser ifNotNilDo: [:user | Mail sendTo: user ]
Run Code Online (Sandbox Code Playgroud)
在不nil执行块的对象上执行,将对象本身作为参数传递.类中的实现UndefinedObject(Smalltalk相当于Ruby的NilClass)实际上什么都不做.这样,如果让用户得到一个nil对象,就什么都不会发生.
我不知道Ruby有类似的东西,所以我推出了自己的解决方案.它是这样的:
class Object
def not_nil
yield(self)
end
end
class NilClass
def not_nil
# do nothing
end
end
Run Code Online (Sandbox Code Playgroud)
它可以像这样使用:
users = {:peter => "peter@peter.com", :roger => "roger@roger.com" }
users[:roger].not_nil {|u| Mail.send(u) }
Run Code Online (Sandbox Code Playgroud)
这使我们无法访问哈希两次
Mail.send(users[:roger]) if users[:roger]
Run Code Online (Sandbox Code Playgroud)
...或使用临时变量:
u = users[:roger]
Mail.send(u) if u
Run Code Online (Sandbox Code Playgroud)
人们开始建议基于哈希操作的解决方案,并且还要两次访问哈希.我的问题不是直接与哈希相关.
想象一下,第一个操作不是哈希访问,也很昂贵.喜欢:
RemoteUserRepo.find_user(:roger).not_nil {|u| Mail.send(u) }
Run Code Online (Sandbox Code Playgroud)
(结束更新)
我的问题是:
ndn*_*kov 14
在Ruby 2.3.0+中,您可以将安全导航运算符(&.)与Object#tap以下内容结合使用:
users[:roger]&.tap { |u| Mail.send(u) }
Run Code Online (Sandbox Code Playgroud)
在 ActiveSupport 中有try方法。
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/try.rb
data = { a: [1,2,3] }
data[:a].try(:first)
#=> 1
data[:b].try(:first)
#=> nil
data[:b].first
#=> Exception
Run Code Online (Sandbox Code Playgroud)
在引擎盖下,它是在您的解决方案附近实施的。对于除 nil 之外的任何对象,它将“发送消息”(就 Smalltalk 而言)具有属性。
# object.rb
def try(*a, &b)
if a.empty? && block_given?
yield self
else
public_send(*a, &b) if respond_to?(a.first)
end
end
# nilclass
def try(*args)
nil
end
Run Code Online (Sandbox Code Playgroud)
关于您的问题
重新发明这个习语有错吗?
Rails 家伙做了类似的事情
Ruby 开箱即用是否支持这样(或更好)的东西?
不,Ruby 不支持开箱即用
或者有另一种更短、更优雅的方式来做到这一点吗?
在我看来它有一个问题:程序员应该控制数据。应该知道他拥有什么样的数据并处理每种类型和每种情况,否则会引发错误。在您的情况下,它对除 NilClass 之外的所有数据类型都有效。什么会导致很难调试的错误。
我更喜欢使用老式的
Mail.send(users[:roger]) if users[:roger]
# or
users[:roger] && Mail.send(users[:roger])
# or use caching if needed
Run Code Online (Sandbox Code Playgroud)