我可以使用downcase!而不是在before_save挂钩中使用downcase来更改值?

时间:2015-10-10 19:21:08

标签: ruby-on-rails ruby string hook before-save

我是Ruby新手。我正在做Michael Hartl的 Rails Tutorial ,并且在用户的模型中使用了以下代码:

before_save { self.email = email.downcase }

在这种情况下,可以接受写:

before_save { self.email.downcase! }

或者由于某种原因这是否存在缺陷?如果是的话,你能给我一个快速解释原因吗?

3 个答案:

答案 0 :(得分:5)

TL; DR

  

在这种情况下,可以接受写

before_save { self.email.downcase! }
     

或者由于某种原因,这是否存在缺陷?

除非爆炸方法在方法链的末尾,或者除非您确定肯定您不关心返回值,否则不要这样做。 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!会将自己的电子邮件属性放在一边,但返回值可能为零。例如:

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

"".downcase!
#=> nil

"z".downcase!
#=> nil

更糟糕的是,如果电子邮件为零,则无论您使用downcase还是downcase!,都会引发异常。例如:

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

为了简单地确保电子邮件属性是小写的,那么您可能会在strong params或其他因素确保电子邮件<的狭窄环境中侥幸成功/ em>不是nil,并且您没有使用方法或钩子的返回值。但更广泛地说,火车失事如:

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

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

总结一下,您当前的示例似乎在功能上等效,但处理返回值的方式完全不同。因此,您的里程可能会有所不同。

答案 1 :(得分:0)

只要您可以保证设置downcase!,就可以使用email来修改此处的属性。在emailnil的情况下,如果您尝试在其上调用NoMethodError,则会遇到downcase!。这也适用于您的第一个示例。

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

before_save { email.downcase! if email }

答案 2 :(得分:0)

原始方法调用属性setter方法email=,因此是可行的方法:

self.email= email.downcase
然后,ActiveRecord可以执行其他操作。例如,它可能会跟踪更改的属性以优化数据库更新。

当你用downcase!更改它时,你首先调用getter,ActiveRecord返回字符串,然后你就改变了字符串,而ActiveRecord并没有直接意识到这一点。也许它在这个例子中起作用,但不习惯这种习惯更安全。