~ubuntu-branches/ubuntu/trusty/ruby-timecop/trusty

« back to all changes in this revision

Viewing changes to lib/timecop/timecop.rb

  • Committer: Package Import Robot
  • Author(s): Nandaja Varma
  • Date: 2012-11-01 23:33:19 UTC
  • Revision ID: package-import@ubuntu.com-20121101233319-ryp7jkbqoob0gncs
Tags: upstream-0.5.9
ImportĀ upstreamĀ versionĀ 0.5.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'singleton'
 
2
require File.join(File.dirname(__FILE__), "time_extensions")
 
3
require File.join(File.dirname(__FILE__), "time_stack_item")
 
4
 
 
5
# Timecop
 
6
# * Wrapper class for manipulating the extensions to the Time, Date, and DateTime objects
 
7
# * Allows us to "freeze" time in our Ruby applications.
 
8
# * Optionally allows time travel to simulate a running clock, such time is not technically frozen.
 
9
#
 
10
# This is very useful when your app's functionality is dependent on time (e.g.
 
11
# anything that might expire).  This will allow us to alter the return value of
 
12
# Date.today, Time.now, and DateTime.now, such that our application code _never_ has to change.
 
13
class Timecop
 
14
  include Singleton
 
15
 
 
16
  class << self
 
17
    attr_accessor :active_support
 
18
 
 
19
    # Allows you to run a block of code and "fake" a time throughout the execution of that block.
 
20
    # This is particularly useful for writing test methods where the passage of time is critical to the business
 
21
    # logic being tested.  For example:
 
22
    #
 
23
    #   joe = User.find(1)
 
24
    #   joe.purchase_home()
 
25
    #   assert !joe.mortgage_due?
 
26
    #   Timecop.freeze(2008, 10, 5) do
 
27
    #     assert joe.mortgage_due?
 
28
    #   end
 
29
    #
 
30
    # freeze and travel will respond to several different arguments:
 
31
    # 1. Timecop.freeze(time_inst)
 
32
    # 2. Timecop.freeze(datetime_inst)
 
33
    # 3. Timecop.freeze(date_inst)
 
34
    # 4. Timecop.freeze(offset_in_seconds)
 
35
    # 5. Timecop.freeze(year, month, day, hour=0, minute=0, second=0)
 
36
    #
 
37
    # When a block is also passed, Time.now, DateTime.now and Date.today are all reset to their
 
38
    # previous values after the block has finished executing.  This allows us to nest multiple
 
39
    # calls to Timecop.travel and have each block maintain it's concept of "now."
 
40
    #
 
41
    # * Note: Timecop.freeze will actually freeze time.  This can cause unanticipated problems if
 
42
    #   benchmark or other timing calls are executed, which implicitly expect Time to actually move
 
43
    #   forward.
 
44
    #
 
45
    # * Rails Users: Be especially careful when setting this in your development environment in a
 
46
    #   rails project.  Generators will load your environment, including the migration generator,
 
47
    #   which will lead to files being generated with the timestamp set by the Timecop.freeze call
 
48
    #   in your dev environment
 
49
    #
 
50
    # Returns the value of the block if one is given, or the mocked time.
 
51
    def freeze(*args, &block)
 
52
      send_travel(:freeze, *args, &block)
 
53
    end
 
54
 
 
55
    # Allows you to run a block of code and "fake" a time throughout the execution of that block.
 
56
    # See Timecop#freeze for a sample of how to use (same exact usage syntax)
 
57
    #
 
58
    # * Note: Timecop.travel will not freeze time (as opposed to Timecop.freeze).  This is a particularly
 
59
    #   good candidate for use in environment files in rails projects.
 
60
    #
 
61
    # Returns the value of the block if one is given, or the mocked time.
 
62
    def travel(*args, &block)
 
63
      send_travel(:travel, *args, &block)
 
64
    end
 
65
 
 
66
    # Allows you to run a block of code and "scale" a time throughout the execution of that block.
 
67
    # The first argument is a scaling factor, for example:
 
68
    #   Timecop.scale(2) do
 
69
    #     ... time will 'go' twice as fast here
 
70
    #   end
 
71
    # See Timecop#freeze for exact usage of the other arguments
 
72
    #
 
73
    # Returns the value of the block if one is given, or the mocked time.
 
74
    def scale(*args, &block)
 
75
      send_travel(:scale, *args, &block)
 
76
    end
 
77
 
 
78
    def baseline
 
79
      instance.send(:baseline)
 
80
    end
 
81
 
 
82
    def baseline=(baseline)
 
83
      instance.send(:baseline=, baseline)
 
84
    end
 
85
 
 
86
    # Reverts back to system's Time.now, Date.today and DateTime.now (if it exists) permamently when
 
87
    # no block argument is given, or temporarily reverts back to the system's time temporarily for
 
88
    # the given block.
 
89
    def return(&block)
 
90
      if block_given?
 
91
        instance.send(:return, &block)
 
92
      else
 
93
        instance.send(:unmock!)
 
94
        nil
 
95
      end
 
96
    end
 
97
 
 
98
    def return_to_baseline
 
99
      instance.send(:return_to_baseline)
 
100
      Time.now
 
101
    end
 
102
 
 
103
    def top_stack_item #:nodoc:
 
104
      instance.instance_variable_get(:@_stack).last
 
105
    end
 
106
 
 
107
    private
 
108
    def send_travel(mock_type, *args, &block)
 
109
      val = instance.send(:travel, mock_type, *args, &block)
 
110
      block_given? ? val : Time.now  
 
111
    end
 
112
  end
 
113
 
 
114
  private
 
115
 
 
116
  def baseline=(baseline)
 
117
    @baseline = baseline
 
118
    @_stack << TimeStackItem.new(:travel, baseline)
 
119
  end
 
120
 
 
121
  def initialize #:nodoc:
 
122
    @_stack = []
 
123
  end
 
124
 
 
125
  def travel(mock_type, *args, &block) #:nodoc:
 
126
    stack_item = TimeStackItem.new(mock_type, *args)
 
127
 
 
128
    @_stack << stack_item
 
129
 
 
130
    if block_given?
 
131
      begin
 
132
        yield stack_item.time
 
133
      ensure
 
134
        @_stack.pop
 
135
      end
 
136
    end
 
137
  end
 
138
 
 
139
  def return(&block)
 
140
    current_stack = @_stack
 
141
    current_baseline = @baseline
 
142
    unmock!
 
143
    yield
 
144
    @_stack = current_stack
 
145
    @baseline = current_baseline
 
146
  end
 
147
 
 
148
  def unmock! #:nodoc:
 
149
    @baseline = nil
 
150
    @_stack = []
 
151
  end
 
152
 
 
153
  def return_to_baseline
 
154
    if @baseline
 
155
      @_stack = [@_stack.shift]
 
156
    else
 
157
      unmock!
 
158
    end
 
159
  end
 
160
end