Ruby: All? Method Caused BUG

问题描述

有一张记录关注公众号用户的表,要实现定时给关注非注册用户发送微信图文消息,如果用户取关则不执行发送程序,如下是实现代码。

accounts = WechatOfficialAccount.wondercv.subscribes.where(openid: openid)

if accounts.all? { |account| account.user.blank? }
  Onboarding::WechatCustomNews.new(openid).send
end

但今天检查后台任务发现很多的发送任务都在retry,具体原因是发送的用户已经取关了。检查了记录取关的代码是正常工作的,仔细看上面的代码发现如果accounts为空时会不会返回的是true,一试果然,本来的直觉认为是false

all?

Passes each element of the collection to the given block. The method returns true if the block never returns false or nil. If the block is not given, Ruby adds an implicit block of {|obj| obj} (that is all? will return true only if none of the collection members are false or nil.)

如果调用对象为空时,block是不会被执行的,不会返回nil或者falseall?方法就会返回true

解决办法

if accounts.any? && accounts.all? { |account| account.user.blank? }
  Onboarding::WechatCustomNews.new(openid).send
end

any?

Passes each element of the collection to the given block. The method returns true if the block ever returns a value other than false or nil. If the block is not given, Ruby adds an implicit block of {|obj| obj} (that is any? will return true if at least one of the collection members is not false or nil.

accounts.any?没有给定执行的blockruby会添加一个隐形的blockany?返回true需要block返回非nil或者false,由于accounts为空,block没有被执行,any?返回false

Reference:

Why does .all? return true on an empty array?

Enumerable Doc