为什么单个 `=` 在 `if` 语句中起作用?

Why does single `=` work in `if` statement?

此代码作为与 devise 和 OmniAuth 一起使用的示例提供,它适用于 my project

class User < ActiveRecord::Base
  def self.new_with_session(params, session)
    super.tap do |user|
      if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
        user.email = data["email"] if user.email.blank?
      end
    end
  end
end

我不知道为什么它是一个单等号而不是双等号,我认为这是 if 语句所必需的。我的 IDE "intelliJ IDEA" 同意我的担忧。

在Ruby中,一个等号用于赋值。表达式

data = session["devise.facebook_data"]

将计算 session["devise.facebook_data"] 的结果分配给名为 data.

的局部变量

如果 session 哈希没有 "devise.facebook_data" 键,它将 return nil 并且 data 将被分配 nil.赋值计算为被赋值的值,因此赋值也将计算为 nilnil 在布尔上下文中被认为是错误的,因此不会评估 && 的右操作数。这样,您就不会得到 NoMethodError 试图调用 nil["extra"]["raw_info"].

如果 session 散列 确实 有一个 "devise.facebook_data" 键,data 将被设置为与之关联的值。 nilfalse 以外的任何值都被认为是真实的,因此将评估 && 运算符的右侧操作数。

如果条件为真,将评估 then 子句,它使用条件中分配的 data 变量。


注意:我相信也可以在 && 运算符的右侧使用 data 变量,即条件可以这样读:

if data = session["devise.facebook_data"] && data["extra"]["raw_info"]

但我必须检查一下。

赋值运算符 (=) returns 赋值,然后由 if 求值。在ruby中,只有falsenil被认为是false。在布尔上下文中,其他所有内容的计算结果为 true(如 if)。

Ruby 不关心条件中的类型,不像 Java。只要该值既不是 nil 也不是 false,它就会通过。

在您的示例中,您实际上歧视了 nil:if 条件确保数据确实存在并且不是 nil,因此我们可以使用它,假设它是一个散列。这是 Ruby.

中的常见模式

if 语句有效的唯一必要条件是布尔表达式。在这种情况下,由于 = returns 赋值的结果,实际测试的是 session["devise.facebook_data"].

的虚假性

IntelliJ 对这样的代码提出投诉是有道理的,因为如果不了解 Ruby 的一两件事就很难阅读。建议将其移至显式赋值语句。这有一个额外的好处,就是将对它的引用干燥两次。

class User < ActiveRecord::Base
  def self.new_with_session(params, session)
    super.tap do |user|
      data = session["devise.facebook_data"]
      if data && data["extra"]["raw_info"]
        user.email = data["email"] if user.email.blank?
      end
    end
  end
end