有没有办法在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行为就是引发异常。 只接受持续时间,你不这么认为吗?
有办法做到这一点吗?
答案 0 :(得分:5)
警告:猴子修补核心Ruby功能可能很危险,尤其是在这种情况下,因为许多开发人员期望在使用时Time
和Date
对象具有哪些行为+
和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