Rails 3.2.21和Ruby 2.2.0打破了Time.zone.parse

时间:2015-01-24 23:29:35

标签: ruby-on-rails ruby

使用Rails 3.2.21和Ruby 2.2.0p0时区解析器被破坏。使用Ruby 2.1.2,这工作得很好。

[1] pry(main)> Time.zone.parse("2015-01-12")
NoMethodError: undefined method `year' for nil:NilClass
from /Users/user/.rvm/gems/ruby-2.2.0/gems/activesupport-3.2.21/lib/active_support/values/time_zone.rb:275:in `parse'

现在我知道您可以将其替换为Time.parse("2015-01-12").localtime,但这会破坏我的应用中的功能。是否有任何已知的修复方法?

3 个答案:

答案 0 :(得分:6)

TLDR:已修复的rails bug,3.2分支上的第一个固定版本是3.2.22

Ruby 2.2更改了在存在名称歧义时如何解析默认参数:

def now
  ...
end

def foo(now = now)
end

在旧版本的ruby中调用foo而没有参数会导致参数now被设置为now()方法调用的任何内容。在ruby 2.2中,它将设置为nil(并且您会收到有关循环引用的警告)

您可以通过执行

来解决歧义
def foo(now = now())
end

或者

def foo(something = now)
end

(显然改变了对那个论点的用法)

显然它以前的工作方式一直都是一个错误。 Rails有一些地方依赖这种不良行为,包括在AS :: Timezone.parse中。该修正是backported到3-2稳定分支,并最终作为3.2.22的一部分发布。

修复此问题的rails master的提交链接指向有关此内容的ruby错误

答案 1 :(得分:2)

所以,这是你最初的情况:

class Dog

  def do_stuff(x, y=2)
     puts x + y
  end

end


d = Dog.new
d.do_stuff(1)

--output:--
3

但是,do_stuff()的代码已经改变,现在你面临类似的事情:

class Dog

  def do_stuff(x, y=nil)
     puts x + y
  end

end


d = Dog.new
d.do_stuff(1)


--output:--
1.rb:4:in `+': nil can't be coerced into Fixnum (TypeError)
    from 1.rb:4:in `do_stuff'
    from 1.rb:10:in `<main>'

抨击!@#$!@#$!@#$!@#$!@#@#developers !!!

在ruby中,您可以使用alias_method()为方法创建其他名称:

class Dog  #Reopen the previously defined Dog class.
  alias_method :orig_do_stuff, :do_stuff  #Create additional name for do_stuff()

  #Now redefine do_stuff():
  def do_stuff(x, y=2)  #Use a better default value for y.
    orig_do_stuff(x, y) #Call the original method.
  end
end

d.do_stuff(1)


--output:--
3

根据rails文档,Time.zone()返回一个TimeZone对象,因此这是定义parse()的类,这是您想要别名的方法。所以,代码看起来像这样:

class Timezone  #Re-open the Timezone class.
  alias_method :orig_parse, :parse  #Create an additional name for parse().

  def parse(str, now=now())  #Now, redefine parse().
    orig_parse(str, now)     #Call the original parse() method.
  end
end

然后,您可以像往常一样调用parse():

Time.zone.parse("2015-01-12")

我猜你应该把代码放在app/helpers/application_helper.rb里面。看看是否有效。

我认为上述内容将被视为Adapter pattern。虽然,因为 plug 已经适合 - 它只是不能产生你想要的结果 - 它可能被认为是Decorator pattern。所以,现在您可以在简历中使用rails代码中的自定义Adapter和/或Decorator模式。 :)

答案 2 :(得分:1)

扩展7stud's答案,因为在ActiveSupport :: TimeZone类中定义了parse方法,您只需打开它并相应地进行轻微更改。

您可以将其放在config/initializers/timezone.rb下,或者参考this question以获取替代名称。

class ActiveSupport::TimeZone
  alias_method :orig_parse, :parse  #Create an additional name for parse().

  def parse(str, now=now())  #Now, redefine parse().
    orig_parse(str, now)     #Call the original parse() method.
  end
end