Remove/deactivate Ruby/Rails 隐式转换
Remove/deactivate Ruby/Rails implicit conversions
有没有办法 remove/deactivate/monkey 修补 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 在 Time 的情况下隐式转换为几秒,在 DateTime 的情况下隐式转换为天数。
编辑:
好的。我在这里有一些很好的答案。
也许 "correct" 这种不太一致的 Ruby 行为的更好方法是在有人尝试将 Integer/Fixnum 添加到 Date/Time 时引发异常。
只应接受持续时间,您不这么认为吗?
有办法吗?
警告: Monkey 修补核心 Ruby 功能可能很危险,尤其是在这种情况下,因为许多开发人员都期望 Time
和 Date
对象在使用 +
和 Fixnum
时有。如果您使用 没有依赖于此预期行为的已知库 来推出自己的解决方案,则可以使用此答案。否则,您将进入一个由不准确的对象或随机不需要的异常引起的未知边缘情况造成伤害的世界。
这实际上是 Date
对象的行为与核心 Ruby 库的 Time
对象的行为。 A DateTime
object is an extension of Date
, and Rails just extends it a bit further.
这里是 method reference for Date#+
,其中指出:
Returns a date object pointing other days after self. The other should be a numeric value. If the other is flonum, assumes its precision is at most nanosecond.
而 method reference for Time#+
的行为不同:
Adds some number of seconds (possibly fractional) to time and returns that value as a new Time
object.
这两个行为都是 Ruby 用 C 编写的核心和库方法,但是可以在 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
要使 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
寻找猴子补丁的方法:
DateTime.instance_method(:+).source_location
有没有办法 remove/deactivate/monkey 修补 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 在 Time 的情况下隐式转换为几秒,在 DateTime 的情况下隐式转换为天数。
编辑:
好的。我在这里有一些很好的答案。
也许 "correct" 这种不太一致的 Ruby 行为的更好方法是在有人尝试将 Integer/Fixnum 添加到 Date/Time 时引发异常。
只应接受持续时间,您不这么认为吗?
有办法吗?
警告: Monkey 修补核心 Ruby 功能可能很危险,尤其是在这种情况下,因为许多开发人员都期望 Time
和 Date
对象在使用 +
和 Fixnum
时有。如果您使用 没有依赖于此预期行为的已知库 来推出自己的解决方案,则可以使用此答案。否则,您将进入一个由不准确的对象或随机不需要的异常引起的未知边缘情况造成伤害的世界。
这实际上是 Date
对象的行为与核心 Ruby 库的 Time
对象的行为。 A DateTime
object is an extension of Date
, and Rails just extends it a bit further.
这里是 method reference for Date#+
,其中指出:
Returns a date object pointing other days after self. The other should be a numeric value. If the other is flonum, assumes its precision is at most nanosecond.
而 method reference for Time#+
的行为不同:
Adds some number of seconds (possibly fractional) to time and returns that value as a new
Time
object.
这两个行为都是 Ruby 用 C 编写的核心和库方法,但是可以在 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
要使 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
寻找猴子补丁的方法:
DateTime.instance_method(:+).source_location