删除/停用Ruby / Rails隐式转换

时间:2015-11-25 16:04:32

标签: ruby-on-rails ruby implicit-conversion

有没有办法在Ruby / Rails中删除/停用/猴子补丁隐式转换?

我已经厌倦了像这样的代码生成的错误:

t = Time.now
t + 3600 == t + 3600.seconds

但是

dt = DateTime.now 
dt + 3600 == dt + 3600.days #(why it's days here and not seconds as with Time ?)

根据添加(或减法)中的日期类型,结果是不同的,因为在时间的情况下,Integer被隐式转换为几秒,而在DateTime的情况下则是天。

修改

确定。我在这里有一些很好的答案 也许更好的方法来纠正"如果有人试图将一个Integer / Fixnum添加到日期/时间,那么这种非常不一致的Ruby行为就是引发异常。 只接受持续时间,你不这么认为吗?

有办法做到这一点吗?

2 个答案:

答案 0 :(得分:5)

警告:猴子修补核心Ruby功能可能很危险,尤其是在这种情况下,因为许多开发人员期望在使用时TimeDate对象具有哪些行为+Fixnum。如果您使用依赖于此预期行为的没有已知库来推出自己的解决方案,则可以使用此答案。否则,你会因为不准确的对象或随机的不必要的异常导致未知边缘情况进入一个受伤的世界。

这实际上是Date对象与核心Ruby库的Time对象行为的行为。 DateTime对象是Date的扩展名,而Rails只是extends it a bit further

以下是method reference for Date#+,其中声明:

  

返回指向self后其他日期的日期对象。另一个应该是一个数值。如果另一个是flonum,则假定其精度至多为纳秒。

method reference for Time#+表现不同:

  

向时间添加一些秒数(可能是小数)并将该值作为新的Time对象返回。

这两种行为都是用C语言编写的Ruby核心和库方法,但是可以在Ruby中修补这种核心行为。例如,在向DateTime添加Fixnum时,使补丁class DateTime def +(num) num = num.to_f / (3600*24) if num.class == Fixnum super(num) end end 表现为秒数:

vagrant@ubuntu-14:/vagrant$ irb
2.1.2 :001 > require 'date'
 => true 
2.1.2 :002 > class DateTime
2.1.2 :003?>   def +(num)
2.1.2 :004?>     num = num.to_f / (3600*24) if num.class == Fixnum
2.1.2 :005?>     super(num)
2.1.2 :006?>     end
2.1.2 :007?>   end
 => :+ 
2.1.2 :008 > test = DateTime.now
 => #<DateTime: 2015-11-25T12:09:18-05:00 ((2457352j,61758s,869355861n),-18000s,2299161j)> 
2.1.2 :009 > test + 1
 => #<DateTime: 2015-11-25T12:09:19-05:00 ((2457352j,61759s,869355861n),-18000s,2299161j)> 

演示:

Fixnum

要解决您的编辑问题,您可能会采用相同的方法。如果检测到的参数是class Time def +(num) raise TypeError.new("No implicit conversion of Fixnum into Time") if num.class == Fixnum super(num) end end class Date def +(num) raise TypeError.new("No implicit conversion of Fixnum into Date") if num.class == Fixnum super(num) end end class DateTime def +(num) raise TypeError.new("No implicit conversion of Fixnum into DateTime") if num.class == Fixnum super(num) end end

,只需覆盖类并引发异常
Int32.MaxValue

答案 1 :(得分:1)

要使Date / DateTime类在rails中添加秒而不是几天:

class Date
  def plus_with_duration other
    if ActiveSupport::Duration === other
      other.since self
    else
      plus_without_duration(other.to_f / (24 * 60 * 60).to_f)
    end
  end
  alias_method :+, :plus_with_duration
end

找到monkeypatch的方法:

DateTime.instance_method(:+).source_location