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

Jul*_*nic 1 ruby ruby-on-rails implicit-conversion

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

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

t = Time.now
t + 3600 == t + 3600.seconds
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:

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

有没有办法做到这一点 ?

sja*_*agr 5

警告:猴子修补核心Ruby功能可能很危险,尤其是在这种情况下,因为许多开发人员期望在使用和使用时行为TimeDate对象具有什么.如果您正在使用没有已知库依赖于此预期行为的自己的解决方案,则可以使用此答案.否则,您将进入一个由不准确的对象或随机不需要的异常引起的未知边缘情况的伤害世界.+Fixnum

这实际上是Date对象的行为Time与核心Ruby库的对象的行为.甲DateTime目的是的延伸Date,和Rails只是其扩展远一点.

以下是方法参考Date#+,其中指出:

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

虽然方法参考的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
Run Code Online (Sandbox Code Playgroud)

演示:

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)> 
Run Code Online (Sandbox Code Playgroud)

为了解决您的编辑问题,您可能会采用相同的方法.如果检测到的参数是Fixnum:1,只需覆盖类并引发异常:

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
Run Code Online (Sandbox Code Playgroud)