如果值不是nil,有条件地在Ruby中执行块?(又名Smalltalk的ifNotNilDo :)

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)

(结束更新)

我的问题是:

  • 重新发明这个成语我错了吗?
  • 在开箱即用的Ruby中是否支持这样(或更好)的东西?
  • 或者还有另一种更短,更优雅的方式吗?

ndn*_*kov 14

在Ruby 2.3.0+中,您可以将安全导航运算符(&.)与Object#tap以下内容结合使用:

users[:roger]&.tap { |u| Mail.send(u) }
Run Code Online (Sandbox Code Playgroud)


fl0*_*00r 5

在 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)