2015-11-25 24 views
1

有沒有辦法在Ruby/Rails中刪除/停用/禁用補丁隱式轉換?刪除/停用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 ?) 

根據在加入(或減法)日期類型的,其結果是不同的,因爲整數在時間的情況下隱式轉換爲幾秒,在DateTime的情況下整數轉換爲幾天。

編輯:

確定。我在這裏有一些很好的答案。
也許更好的方法來「糾正」這種相當不一致的Ruby行爲是,如果有人試圖將Integer/Fixnum添加到日期/時間,則會引發異常。 只有持續時間應該被接受,你不這麼認爲嗎?

有沒有辦法做到這一點?

+1

什麼?這些語句都不會是「真」的,因爲每個生成的'now'都會以毫秒爲單位關閉。 – sjagr

+0

是的,你說得對。我糾正了這個問題 –

+1

**使用猴子補丁修改任何語言的核心庫的功能是非常危險的**你正在尋求複雜的道路,無論是你自己還是新的開發人員在'DateTime'不'工作應該如何。 –

回答

5

警告:猴子修補核心Ruby的功能可能是危險的,尤其是在這種情況下,因爲許多開發商有一種期待什麼樣的行爲的TimeDate對象與+Fixnum s工作時有。如果您使用來滾動您自己的解決方案,則沒有已知的文件庫依賴於此預期行爲,您可以利用此答案。否則,由於不準確的對象或隨機不需要的異常導致未知邊緣情況,你正在進入一個受到傷害的世界。

這實際上是Date對象的行爲與核心Ruby庫的行爲Time對象的行爲。 A DateTime對象是Date的擴展,而Rails只是extends it a bit further

這裏是method reference for Date#+,其中規定:

返回後,自我指向其他天的日期對象。另一個應該是一個數值。如果另一個是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 

演示:

[email protected]:/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 
1

爲了Date/DateTime類來添加秒而不是天在軌:

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