我是红宝石的新手。我正在做 Michael Hartl 的Ruby on Rails 教程,用户模型中使用了以下代码:
before_save { self.email = email.downcase }
在这种情况下,是否可以改为写:
before_save { self.email.downcase! }
还是由于某种原因这种方法有缺陷?如果是,你能快速解释一下为什么吗?
我是红宝石的新手。我正在做 Michael Hartl 的Ruby on Rails 教程,用户模型中使用了以下代码:
before_save { self.email = email.downcase }
在这种情况下,是否可以改为写:
before_save { self.email.downcase! }
还是由于某种原因这种方法有缺陷?如果是,你能快速解释一下为什么吗?
在这种情况下,是否可以改为写
before_save { self.email.downcase! }
还是由于某种原因这种方法有缺陷?
不要这样做,除非 bang 方法位于方法链的末尾,或者除非您确定您不关心返回值。否则,Bad Things™ 可能会发生。
相反,您应该使用处理极端情况的东西,例如以下之一:
before_save { self.email.downcase! unless self.email.blank? }
before_save { self.email = self.email.to_s.downcase }
一些爆炸方法的问题,如String#downcase!是他们没有提供您认为他们提供的返回值。虽然self.email.downcase!
会将 self 的email属性小写,但返回值可能为 nil。例如:
"A".downcase!
#=> "a"
"".downcase!
#=> nil
"z".downcase!
#=> nil
更糟糕的是,如果email为 nil,无论您使用downcase
还是downcase!
. 例如:
nil.downcase
# NoMethodError: undefined method `downcase' for nil:NilClass
为了简单地确保电子邮件属性是小写的,那么您可能会在强参数或其他因素确保电子邮件不为零并且您不使用方法的返回值的狭窄情况下逃脱或钩。不过,更广泛地说,火车失事像:
before_save { self.email.downcase!.gsub(?@, ' AT ') }
可能会在运行时以令人惊讶且难以调试的方式爆炸。
回顾一下,您当前的示例在功能上看起来是等效的,但处理返回值的方式却大不相同。因此,您的里程可能会有所不同。
原始方法调用属性设置器方法email=
,因此是要走的路:
self.email= email.downcase
ActiveRecord 然后可以做其他事情。例如,它可能会跟踪更改的属性以优化数据库更新。
当你用 改变它时downcase!
,你首先调用 getter,ActiveRecord 返回字符串,然后你就地改变字符串,而 ActiveRecord 并不直接意识到这一点。也许它在这个例子中有效,但不要养成这个习惯会更安全。
在这里使用downcase!
来修改属性是完全可以接受的,只要你能保证email
已经设置好了。在 where email
is的情况下,如果您尝试调用它nil
,您将遇到 a 。这也适用于您的第一个示例。NoMethodError
downcase!
顺便说一句,实际上不需要指定self
访问模型上的属性。以下是完全足够的。(添加检查是否存在电子邮件。)
before_save { email.downcase! if email }