timecop 0.5.2 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +1 -1
- data/README.markdown +48 -13
- data/Rakefile +10 -4
- data/lib/timecop/time_extensions.rb +128 -54
- data/lib/timecop/time_stack_item.rb +78 -83
- data/lib/timecop/timecop.rb +142 -49
- data/lib/timecop/version.rb +2 -2
- data/lib/timecop.rb +2 -2
- data/test/test_helper.rb +26 -17
- data/test/time_stack_item_test.rb +153 -73
- data/test/timecop_test.rb +247 -41
- data/test/timecop_without_date_but_with_time_test.rb +4 -8
- data/test/timecop_without_date_test.rb +22 -23
- metadata +16 -23
- data/History.rdoc +0 -137
- data/test/run_tests.sh +0 -6
data/lib/timecop/timecop.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'singleton'
|
2
|
-
require
|
3
|
-
require 'timecop/time_stack_item'
|
2
|
+
require File.join(File.dirname(__FILE__), "time_stack_item")
|
4
3
|
|
5
4
|
# Timecop
|
6
5
|
# * Wrapper class for manipulating the extensions to the Time, Date, and DateTime objects
|
@@ -14,7 +13,7 @@ class Timecop
|
|
14
13
|
include Singleton
|
15
14
|
|
16
15
|
class << self
|
17
|
-
|
16
|
+
private :instance
|
18
17
|
|
19
18
|
# Allows you to run a block of code and "fake" a time throughout the execution of that block.
|
20
19
|
# This is particularly useful for writing test methods where the passage of time is critical to the business
|
@@ -33,6 +32,7 @@ class Timecop
|
|
33
32
|
# 3. Timecop.freeze(date_inst)
|
34
33
|
# 4. Timecop.freeze(offset_in_seconds)
|
35
34
|
# 5. Timecop.freeze(year, month, day, hour=0, minute=0, second=0)
|
35
|
+
# 6. Timecop.freeze() # Defaults to Time.now
|
36
36
|
#
|
37
37
|
# When a block is also passed, Time.now, DateTime.now and Date.today are all reset to their
|
38
38
|
# previous values after the block has finished executing. This allows us to nest multiple
|
@@ -47,11 +47,9 @@ class Timecop
|
|
47
47
|
# which will lead to files being generated with the timestamp set by the Timecop.freeze call
|
48
48
|
# in your dev environment
|
49
49
|
#
|
50
|
-
# Returns the value of the block or
|
50
|
+
# Returns the value of the block if one is given, or the mocked time.
|
51
51
|
def freeze(*args, &block)
|
52
|
-
|
53
|
-
|
54
|
-
block_given? ? val : nil
|
52
|
+
send_travel(:freeze, *args, &block)
|
55
53
|
end
|
56
54
|
|
57
55
|
# Allows you to run a block of code and "fake" a time throughout the execution of that block.
|
@@ -60,11 +58,9 @@ class Timecop
|
|
60
58
|
# * Note: Timecop.travel will not freeze time (as opposed to Timecop.freeze). This is a particularly
|
61
59
|
# good candidate for use in environment files in rails projects.
|
62
60
|
#
|
63
|
-
# Returns the value of the block or
|
61
|
+
# Returns the value of the block if one is given, or the mocked time.
|
64
62
|
def travel(*args, &block)
|
65
|
-
|
66
|
-
|
67
|
-
block_given? ? val : nil
|
63
|
+
send_travel(:travel, *args, &block)
|
68
64
|
end
|
69
65
|
|
70
66
|
# Allows you to run a block of code and "scale" a time throughout the execution of that block.
|
@@ -74,74 +70,171 @@ class Timecop
|
|
74
70
|
# end
|
75
71
|
# See Timecop#freeze for exact usage of the other arguments
|
76
72
|
#
|
77
|
-
# Returns the value of the block or
|
73
|
+
# Returns the value of the block if one is given, or the mocked time.
|
78
74
|
def scale(*args, &block)
|
79
|
-
|
80
|
-
|
81
|
-
block_given? ? val : nil
|
75
|
+
send_travel(:scale, *args, &block)
|
82
76
|
end
|
83
77
|
|
84
78
|
def baseline
|
85
|
-
instance
|
79
|
+
instance.baseline
|
86
80
|
end
|
87
81
|
|
88
82
|
def baseline=(baseline)
|
89
|
-
instance
|
83
|
+
instance.baseline = baseline
|
90
84
|
end
|
91
85
|
|
92
|
-
# Reverts back to system's Time.now, Date.today and DateTime.now (if it exists)
|
93
|
-
|
94
|
-
|
95
|
-
|
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.return(&block)
|
92
|
+
else
|
93
|
+
instance.unmock!
|
94
|
+
nil
|
95
|
+
end
|
96
96
|
end
|
97
|
+
alias :unfreeze :return
|
97
98
|
|
98
99
|
def return_to_baseline
|
99
|
-
instance
|
100
|
+
instance.return_to_baseline
|
100
101
|
Time.now
|
101
102
|
end
|
102
103
|
|
103
104
|
def top_stack_item #:nodoc:
|
104
|
-
instance
|
105
|
+
instance.stack.last
|
106
|
+
end
|
107
|
+
|
108
|
+
def safe_mode=(safe)
|
109
|
+
@safe_mode = safe
|
110
|
+
end
|
111
|
+
|
112
|
+
def safe_mode?
|
113
|
+
@safe_mode ||= false
|
114
|
+
end
|
115
|
+
|
116
|
+
def thread_safe=(t)
|
117
|
+
instance.thread_safe = t
|
118
|
+
end
|
119
|
+
|
120
|
+
def thread_safe
|
121
|
+
instance.thread_safe
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns whether or not Timecop is currently frozen/travelled
|
125
|
+
def frozen?
|
126
|
+
!instance.stack.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
def send_travel(mock_type, *args, &block)
|
131
|
+
val = instance.travel(mock_type, *args, &block)
|
132
|
+
block_given? ? val : Time.now
|
105
133
|
end
|
106
134
|
end
|
107
135
|
|
108
|
-
|
136
|
+
def baseline=(b)
|
137
|
+
set_baseline(b)
|
138
|
+
stack << TimeStackItem.new(:travel, b)
|
139
|
+
end
|
109
140
|
|
110
|
-
|
111
|
-
|
112
|
-
|
141
|
+
def baseline
|
142
|
+
if @thread_safe
|
143
|
+
Thread.current[:timecop_baseline]
|
144
|
+
else
|
145
|
+
@baseline
|
113
146
|
end
|
147
|
+
end
|
114
148
|
|
115
|
-
|
116
|
-
|
149
|
+
def set_baseline(b)
|
150
|
+
if @thread_safe
|
151
|
+
Thread.current[:timecop_baseline] = b
|
152
|
+
else
|
153
|
+
@baseline = b
|
117
154
|
end
|
155
|
+
end
|
118
156
|
|
119
|
-
|
120
|
-
|
157
|
+
def stack
|
158
|
+
if @thread_safe
|
159
|
+
Thread.current[:timecop_stack] ||= []
|
160
|
+
Thread.current[:timecop_stack]
|
161
|
+
else
|
162
|
+
@stack
|
163
|
+
end
|
164
|
+
end
|
121
165
|
|
122
|
-
|
123
|
-
|
166
|
+
def set_stack(s)
|
167
|
+
if @thread_safe
|
168
|
+
Thread.current[:timecop_stack] = s
|
169
|
+
else
|
170
|
+
@stack = s
|
171
|
+
end
|
172
|
+
end
|
124
173
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
174
|
+
def initialize #:nodoc:
|
175
|
+
@stack = []
|
176
|
+
@safe = nil
|
177
|
+
@thread_safe = false
|
178
|
+
end
|
179
|
+
|
180
|
+
def thread_safe=(t)
|
181
|
+
initialize
|
182
|
+
@thread_safe = t
|
183
|
+
end
|
184
|
+
|
185
|
+
def thread_safe
|
186
|
+
@thread_safe
|
187
|
+
end
|
188
|
+
|
189
|
+
def travel(mock_type, *args, &block) #:nodoc:
|
190
|
+
raise SafeModeException if Timecop.safe_mode? && !block_given? && !@safe
|
191
|
+
|
192
|
+
stack_item = TimeStackItem.new(mock_type, *args)
|
193
|
+
|
194
|
+
stack_backup = stack.dup
|
195
|
+
stack << stack_item
|
196
|
+
|
197
|
+
if block_given?
|
198
|
+
safe_backup = @safe
|
199
|
+
@safe = true
|
200
|
+
begin
|
201
|
+
yield stack_item.time
|
202
|
+
ensure
|
203
|
+
stack.replace stack_backup
|
204
|
+
@safe = safe_backup
|
132
205
|
end
|
133
206
|
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def return(&block)
|
210
|
+
current_stack = stack
|
211
|
+
current_baseline = baseline
|
212
|
+
unmock!
|
213
|
+
yield
|
214
|
+
ensure
|
215
|
+
set_stack current_stack
|
216
|
+
set_baseline current_baseline
|
217
|
+
end
|
134
218
|
|
135
|
-
|
136
|
-
|
137
|
-
|
219
|
+
def unmock! #:nodoc:
|
220
|
+
set_baseline nil
|
221
|
+
set_stack []
|
222
|
+
end
|
223
|
+
|
224
|
+
def return_to_baseline
|
225
|
+
if baseline
|
226
|
+
set_stack [stack.shift]
|
227
|
+
else
|
228
|
+
unmock!
|
138
229
|
end
|
230
|
+
end
|
139
231
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
else
|
144
|
-
unmock!
|
145
|
-
end
|
232
|
+
class SafeModeException < StandardError
|
233
|
+
def initialize
|
234
|
+
super "Safe mode is enabled, only calls passing a block are allowed."
|
146
235
|
end
|
236
|
+
end
|
147
237
|
end
|
238
|
+
|
239
|
+
# This must be done after TimeCop is available
|
240
|
+
require File.join(File.dirname(__FILE__), "time_extensions")
|
data/lib/timecop/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
class Timecop
|
2
|
-
VERSION = "0.5
|
3
|
-
end
|
2
|
+
VERSION = "0.9.5"
|
3
|
+
end
|
data/lib/timecop.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require File.join(File.dirname(__FILE__), "timecop", "timecop")
|
2
|
+
require File.join(File.dirname(__FILE__), "timecop", "version")
|
data/test/test_helper.rb
CHANGED
@@ -1,34 +1,39 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'bundler/setup'
|
3
|
-
require '
|
4
|
-
require '
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/rg'
|
4
|
+
require 'pry'
|
5
5
|
|
6
|
-
|
6
|
+
$VERBOSE = true # enable ruby warnings
|
7
7
|
|
8
|
+
require 'mocha/minitest'
|
9
|
+
|
10
|
+
class Minitest::Test
|
8
11
|
private
|
9
12
|
# Tests to see that two times are within the given distance,
|
10
13
|
# in seconds, from each other.
|
11
14
|
def times_effectively_equal(time1, time2, seconds_interval = 1)
|
12
15
|
(time1 - time2).abs <= seconds_interval
|
13
16
|
end
|
14
|
-
|
17
|
+
|
15
18
|
def assert_times_effectively_equal(time1, time2, seconds_interval = 1, msg = nil)
|
16
19
|
assert times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
|
17
20
|
end
|
18
|
-
|
21
|
+
|
19
22
|
def assert_times_effectively_not_equal(time1, time2, seconds_interval = 1, msg = nil)
|
20
23
|
assert !times_effectively_equal(time1, time2, seconds_interval), "#{msg}: time1 = #{time1.to_s}, time2 = #{time2.to_s}"
|
21
24
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
|
26
|
+
# Gets the local offset (supplied by ENV['TZ'] or your computer's clock)
|
27
|
+
# At the given timestamp, or Time.now if not time is given.
|
28
|
+
def local_offset(time = Time.now)
|
29
|
+
Time.at(time.to_i).to_datetime.offset
|
25
30
|
end
|
26
|
-
|
27
|
-
TIMEZONES = ["Europe/Paris", "UTC", "
|
28
|
-
|
31
|
+
|
32
|
+
TIMEZONES = ["Pacific/Midway", "Europe/Paris", "UTC", "America/Chicago"]
|
33
|
+
|
29
34
|
def each_timezone
|
30
35
|
old_tz = ENV["TZ"]
|
31
|
-
|
36
|
+
|
32
37
|
begin
|
33
38
|
TIMEZONES.each do |timezone|
|
34
39
|
ENV["TZ"] = timezone
|
@@ -38,13 +43,17 @@ class Test::Unit::TestCase
|
|
38
43
|
ENV["TZ"] = old_tz
|
39
44
|
end
|
40
45
|
end
|
41
|
-
|
46
|
+
|
42
47
|
def a_time_stack_item
|
43
48
|
Timecop::TimeStackItem.new(:freeze, 2008, 1, 1, 0, 0, 0)
|
44
49
|
end
|
45
|
-
|
50
|
+
|
46
51
|
def assert_date_times_equal(dt1, dt2)
|
47
|
-
|
52
|
+
assert_in_delta dt1.to_time.to_f, dt2.to_time.to_f, 0.01, "Failed for timezone: #{ENV['TZ']}: #{dt1.to_s} not equal to #{dt2.to_s}"
|
48
53
|
end
|
49
|
-
|
54
|
+
|
55
|
+
def jruby?
|
56
|
+
RUBY_PLATFORM == "java"
|
57
|
+
end
|
58
|
+
|
50
59
|
end
|