我可以使用小写字母吗!而不是在 before_save 挂钩中小写来更改值?

Can I use downcase! instead of downcase in a before_save hook to change a value?

我是 Ruby 的新手。我在 Rails 教程 上做 Michael Hartl 的 Ruby,以下代码用于用户的模型:

before_save { self.email = email.downcase }

在这种情况下,是否可以改写:

before_save { self.email.downcase! }

或者这种方法是否出于某种原因存在缺陷?如果是,您能否快速解释一下原因?

这里用downcase!修改属性是完全可以接受的,只要你能保证设置了email。在 emailnil 的情况下,如果您尝试对其调用 downcase!,则会遇到 NoMethodError。这也适用于您的第一个示例。

顺便说一句,确实没有必要指定 self 来访问模型的属性。以下是完全足够的。 (添加检查是否存在电子邮件。)

before_save { email.downcase! if email }

TL;博士

In this context, would it be acceptable to instead write

before_save { self.email.downcase! }

or is this approached flawed for some reason?

不要这样做,除非 bang 方法位于方法链的末尾,或者除非您确定知道您不关心 return 值。否则 Bad Things™ 可能会发生。

相反,您应该使用处理极端情况的东西,例如以下之一:

  • before_save { self.email.downcase! unless self.email.blank? }
  • before_save { self.email = self.email.to_s.downcase }

说明

一些像 String#downcase! 这样的 bang 方法的问题在于它们不提供您认为它们提供的 return 值。虽然 self.email.downcase! 会将自身的 email 属性小写,但 return 值可能为 nil。例如:

"A".downcase!
#=> "a"

"".downcase!
#=> nil

"z".downcase!
#=> nil

更糟糕的是,如果 email 为 nil,则无论您使用 downcase 还是 downcase! 都会引发异常。例如:

nil.downcase
# NoMethodError: undefined method `downcase' for nil:NilClass

为了简单地确保 email 属性是小写的,那么在 strong params 或其他因素确保email 不是零,并且您没有使用方法或挂钩的 return 值。不过,更广泛地说,火车失事就像:

before_save { self.email.downcase!.gsub(?@, ' AT ') }

可能会在运行时以令人惊讶且难以调试的方式爆炸。

总而言之,您当前的示例 看起来 功能相同,但处理 return 值的方式完全不同。因此,您的里程可能会有所不同。

原来的方法调用属性setter方法email=,因此要走的路:

self.email= email.downcase

A​​ctiveRecord 可以做更多的事情。例如,它可能会跟踪更改的属性以优化数据库更新。

downcase!改的时候,是先调用getter,ActiveRecord returns这个字符串,然后就地改字符串,ActiveRecord不是直接意识到这一点。也许它在这个例子中有效,但不要养成这种习惯更安全。