Rails 3.2 升级到 4.0:FalseClass 上未定义 to_datetime 方法

9
我正在升级一个我从3.2继承而来的Rails应用程序到4.0.1。我遵循并完成了这里的指南:http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#upgrading-from-rails-3-2-to-rails-4-0。我已经解决了所有问题,除了一个我找不到根本原因的错误。当我尝试保存一个用户模型对象时,我遇到了以下错误:
[1] pry(main)> User.create(name: "test user", email: "testuser@frobnitz.com", password: "testPassword123", password_confirmation: "testPassword123")                                                                                                                               

(0.6ms)  BEGIN
(0.9ms)  ROLLBACK
NoMethodError: undefined method `to_datetime' for false:FalseClass
from /home/cmhobbs/src/serve2perform/.gem/ruby/2.3.0/gems/activesupport-4.0.1/lib/active_support/core_ext/date_time/calculations.rb:161:in `<=>'

已安装 activesupport 4.0.1 和 rals 4.0.1。我使用 chgems,并在重新绑定之前清除了我的 .gem/ 目录和 Gemfile.lock

这是用户模型的Gist。

以及我能从 pry 得到的完整回溯输出。

这是用户表架构的链接。


这是因为 created_atupdated_at 造成的。 - uzaif
@uzaif,您能否进一步解释一下?谢谢! - cmhobbs
你能展示一下用户表的模式吗?另外,你重启了服务器吗?可以尝试在控制台创建一个用户吗? - coderVishal
Gist中的回溯输出链接出现404错误。 - sixty4bit
@cmhobbs,你能发布一下用户表的架构吗? - 7urkm3n
显示剩余13条评论
2个回答

9

一旦你找到了有问题的回调函数,就是这个:

  before_create :activate_license

  def activate_license
    self.active_license = true
    self.licensed_date = Time.now
  end

事情开始变得更加清晰了。 activate_licence 是一个之前的回调函数。 之前的回调函数可以通过返回false(或引发异常)来中止整个回调链

如果我们仔细查看您通过手动添加一些puts行到Rails回调代码中提供的调试输出,我们确实可以找到此回调结果与false进行比较(此处 - 我删除了代码的一些不重要的部分):

result = activate_license
halted = (result == false)
if halted
  halted_callback_hook(":activate_license")
end 

因为通过返回 false 来支持在回调之前停止 (即上面显示的 Rails 代码) 的功能从 Rails 3.2Rails 4.0.1 实际上没有改变,所以问题必须出在比较本身上。
回调函数返回一个 DateTime 对象 (它是该方法中的最后一项赋值,也会被返回)。确实,在这两个 Rails 版本之间,DateTime 对象的比较发生了显著变化 (还要注意 == 运算符通常使用 <=> 操作符进行评估)。
  • in Rails 3.2 it was this:

    def <=>(other)
      if other.kind_of?(Infinity)
        super
      elsif other.respond_to? :to_datetime
       super other.to_datetime
      else
        nil
      end
    end
    

    notice especially the respond_to? check if the other object is also a date or time object while otherwise returning nil.

  • whereas in Rails 4.0.1 this changed to the bare code below:

    def <=>(other)
      super other.to_datetime
    end
    

    → all sanity checks are gone!

现在一切都清楚了:回调的结果(一个DateTime对象)使用<=>运算符与false进行比较,在Rails 4.0下,比较试图将false对象转换为DateTime而没有任何检查,这当然会失败并抛出异常。
要解决此问题,只需确保您的回调返回Rails可以轻松与false进行比较的内容,例如true,因为您的回调永远不应该停止链式操作。
  def activate_license
    self.active_license = true
    self.licensed_date = Time.now
    true
  end

现在一切应该正常工作了。

3

即使在核心类中也可以绑定,请像这样做,并检查other是什么,它来自哪里。

/home/cmhobbs/src/serve2perform/.gem/ruby/2.3.0/gems/activesupport-4.0.1/lib/active_support/core_ext/date_time/calculations.rb

def <=>(other)
  binding.pry
  if other.kind_of?(Infinity)
    super
  elsif other.respond_to? :to_datetime
    super other.to_datetime rescue nil
  else
    nil
  end
end

在 gem 文件中覆盖它,它就可以工作 -> 我们有没有不使用这个的解决方案? - Vishwas Nahar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接