2014-08-29 27 views
1

我正在寫一個簡單的類來將字符串解析爲相對日期。rspec中的時差

module RelativeDate 
    class InvalidString < StandardError; end 
    class Parser 
    REGEX = /([0-9]+)_(day|week|month|year)_(ago|from_now)/ 

    def self.to_time(value) 
     if captures = REGEX.match(value) 
     captures[1].to_i.send(captures[2]).send(captures[3]) 
     else 
     raise InvalidString, "#{value} could not be parsed" 
     end 
    end 

    end 
end 

該代碼似乎工作正常。

現在,當我嘗試我的規格,我只在年份和月份獲得時間差

require 'spec_helper' 
describe RelativeDate::Parser do 
    describe "#to_time" do 

    before do 
     Timecop.freeze 
    end 

    ['day','week','month','year'].each do |type| 
     it "should parse #{type} correctly" do 
     RelativeDate::Parser.to_time("2_#{type}_ago").should == 2.send(type).ago 
     RelativeDate::Parser.to_time("2_#{type}_from_now").should == 2.send(type).from_now 
     end 
    end 

    after do 
     Timecop.return 
    end 

    end 
end 

輸出

..FF 

Failures: 

    1) RelativeDate::Parser#to_time should parse year correctly 
    Failure/Error: RelativeDate::Parser.to_time("2_#{type}_ago").should == 2.send(type).ago 
     expected: Wed, 29 Aug 2012 22:40:14 UTC +00:00 
      got: Wed, 29 Aug 2012 10:40:14 UTC +00:00 (using ==) 
     Diff: 
     @@ -1,2 +1,2 @@ 
     -Wed, 29 Aug 2012 22:40:14 UTC +00:00 
     +Wed, 29 Aug 2012 10:40:14 UTC +00:00 

    # ./spec/lib/relative_date/parser_spec.rb:11:in `(root)' 

    2) RelativeDate::Parser#to_time should parse month correctly 
    Failure/Error: RelativeDate::Parser.to_time("2_#{type}_ago").should == 2.send(type).ago 
     expected: Sun, 29 Jun 2014 22:40:14 UTC +00:00 
      got: Mon, 30 Jun 2014 22:40:14 UTC +00:00 (using ==) 
     Diff: 
     @@ -1,2 +1,2 @@ 
     -Sun, 29 Jun 2014 22:40:14 UTC +00:00 
     +Mon, 30 Jun 2014 22:40:14 UTC +00:00 

    # ./spec/lib/relative_date/parser_spec.rb:11:in `(root)' 

Finished in 0.146 seconds 
4 examples, 2 failures 

Failed examples: 

rspec ./spec/lib/relative_date/parser_spec.rb:10 # RelativeDate::Parser#to_time should parse year correctly 
rspec ./spec/lib/relative_date/parser_spec.rb:10 # RelativeDate::Parser#to_time should parse month correctly 

第一個似乎是一個時區的問題,但另外一個是連相隔一天?這件事我真的很無能。

回答

2

這是一個引人入勝的問題!

首先,這與Timecop或RSpec無關。這個問題可以在Rails的控制檯轉載如下:

2.0.0-p247 :001 > 2.months.ago 
=> Mon, 30 Jun 2014 20:46:19 UTC +00:00 
2.0.0-p247 :002 > 2.months.send(:ago) 
DEPRECATION WARNING: Calling #ago or #until on a number (e.g. 5.ago) is deprecated and will be removed in the future, use 5.seconds.ago instead. (called from irb_binding at (irb):2) 
=> Wed, 02 Jul 2014 20:46:27 UTC +00:00 

[注:這個答案使用的months的例子,但同樣適用於別名month以及yearyears]

滑軌添加month方法將Integer類,返回一個ActiveSupport::Duration對象,它是一個「代理對象的」含有method_missing方法,其重定向到「值」是用作代理的method_missing方法的所有呼叫。

當您直接致電ago時,它在Duration類本身中由ago方法處理。當你嘗試調用通過sendago,然而,send沒有在Duration定義,而不是在所有的代理對象從繼承BasicObject定義,所以Rails的Durationmethod_missing方法被調用,這反過來又要求send的整數「值「,導致在Numeric中調用ago。在你的情況下,這導致日期的變化等於2*30天。

您必須使用的唯一方法是由Duration本身定義的方法和由BasicObject定義的方法。後者如下:

2.0.0-p247 :023 > BasicObject.instance_methods 
=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__] 

除了你發現instance_eval,您可以使用__send__

下面是method_missingduration.rb

def method_missing(method, *args, &block) #:nodoc: 
    value.send(method, *args, &block) 
    end 

value在這種情況下的定義是指秒在Duration對象的數量。如果您將method_missing重新定義爲特例ago,則可以通過測試。或者你可以別名send__send__如下:

class ActiveSupport::Duration 
    alias send __send__ 
end 

這裏有一個如何從Duration作品這個method_missing方法的另一個例子:

macbookair1:so1 palfvin$ rails c 
Loading development environment (Rails 4.1.1) 
irb: warn: can't alias context from irb_context. 
2.0.0-p247 :001 > class ActiveSupport::Duration 
2.0.0-p247 :002?> def foo 
2.0.0-p247 :003?>  'foobar' 
2.0.0-p247 :004?>  end 
2.0.0-p247 :005?> end 
=> nil 
2.0.0-p247 :006 > 2.months.foo 
=> "foobar" 
2.0.0-p247 :007 > 2.months.respond_to?(:foo) 
=> false 
2.0.0-p247 :008 > 

可以直接調用新定義foo,但由於BasicObject沒有按」 t實施respond_to?,您不能「測試」該方法在那裏定義。出於同樣的原因,Duration對象上的method(:ago)返回#<Method: Fixnum(Numeric)#ago>,因爲這是在value上定義的ago方法。

+0

確實很有意思。我將不得不充分理解你在說什麼。 – 2014-09-01 02:18:35

+0

2.months.instance_eval(&:前)這似乎工作。 – 2014-09-01 02:34:09

+0

我已經添加了一些更多的解釋。請讓我知道你正在尋找什麼其他信息。 – 2014-09-01 03:33:47