timecop 0.8.1 → 0.9.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfc685225a22109f897b68e3174608a4b6a16def
4
- data.tar.gz: 66aee6ccc9dfd424502c3f3f149c175af3fa3fbc
3
+ metadata.gz: 580525a47a6026f3d83689807d96949dd5269fa5
4
+ data.tar.gz: ffb9cc8cf58489f231f3348656f99597b3c79989
5
5
  SHA512:
6
- metadata.gz: ac084a1428c7b528faa70aa321b1cb9db1f9f3f8eb45e4a94777f15b915662fc60ba9208091837903348348a1c902c109649aa7a471819cbec7ea1e0d8c1a377
7
- data.tar.gz: 14082a5cba36b7f5238455d884025c5f9ce01ca0b81073f9b9860220286d13db10cce76b3da38465fb585976fd29782a1e0d81913d699814e4505790446e03b7
6
+ metadata.gz: dc3c024856bf2bbaab09a0734aa1d87712e26c8b1e47774edf2d65153ceb22ae6bbe3b04499a4f12c68acf5f9352fb26e614c8c4996c80e14353f75049899598
7
+ data.tar.gz: c759c9cc704c61d2bd1cf13b4d44a6ad8a1b36a03fc398b4796f1dd1bc0d055a6ddda86aa0a31addc753eb6accc57049648d3fd5334477ccb0cc8cbc594076b7
@@ -1,6 +1,6 @@
1
1
  # timecop
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/travisjeffery/timecop.png)](http://travis-ci.org/travisjeffery/timecop)
3
+ [![Build Status](https://secure.travis-ci.org/travisjeffery/timecop.svg)](http://travis-ci.org/travisjeffery/timecop)
4
4
 
5
5
  ## DESCRIPTION
6
6
 
@@ -104,7 +104,7 @@ being able to simulate activity via subsequent calls to your application.
104
104
  Timecop.scale(3600)
105
105
  Time.now
106
106
  # => 2012-09-20 21:23:25 -0500
107
- # seconds later, hours have past it's gone from 9pm at night to 6am in the morning
107
+ # seconds later, hours have passed and it's gone from 9pm at night to 6am in the morning
108
108
  Time.now
109
109
  # => 2012-09-21 06:22:59 -0500
110
110
  ```
@@ -128,6 +128,12 @@ Timecop.freeze
128
128
  # => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.
129
129
  ```
130
130
 
131
+ ### Rails v Ruby Date/Time libraries
132
+
133
+ Sometimes [Rails Date/Time methods don't play nicely with Ruby Date/Time methods.](https://rails.lighthouseapp.com/projects/8994/tickets/6410-dateyesterday-datetoday)
134
+
135
+ Be careful mixing Ruby `Date.today` with Rails `Date.tomorrow` / `Date.yesterday` as things might break.
136
+
131
137
  ## Contribute
132
138
 
133
139
  timecop is maintained by [travisjeffery](http://github.com/travisjeffery), and
@@ -27,19 +27,8 @@ class Time #:nodoc:
27
27
  end
28
28
 
29
29
  class Date #:nodoc:
30
- WEEKDAYS = {
31
- "sunday" => 0,
32
- "monday" => 1,
33
- "tuesday" => 2,
34
- "wednesday" => 3,
35
- "thursday" => 4,
36
- "friday" => 5,
37
- "saturday" => 6
38
- }
39
-
40
30
  class << self
41
31
  def mock_date
42
- mocked_time_stack_item = Timecop.top_stack_item
43
32
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date(self)
44
33
  end
45
34
 
@@ -65,26 +54,41 @@ class Date #:nodoc:
65
54
  alias_method :strptime, :strptime_with_mock_date
66
55
 
67
56
  def parse_with_mock_date(*args)
68
- str = args.first
69
- if str && WEEKDAYS.keys.include?(str.downcase)
70
- offset = WEEKDAYS[str.downcase] - Date.today.wday
71
-
72
- Date.today + offset
57
+ parsed_date = parse_without_mock_date(*args)
58
+ return parsed_date unless mocked_time_stack_item
59
+ date_hash = Date._parse(*args)
60
+
61
+ case
62
+ when date_hash[:year] && date_hash[:mon] && date_hash[:mday]
63
+ parsed_date
64
+ when date_hash[:mon] && date_hash[:mday]
65
+ Date.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
66
+ when date_hash[:wday]
67
+ closest_wday(date_hash[:wday])
73
68
  else
74
- parse_without_mock_date(*args)
69
+ parsed_date + mocked_time_stack_item.travel_offset_days
75
70
  end
76
71
  end
77
72
 
78
73
  alias_method :parse_without_mock_date, :parse
79
74
  alias_method :parse, :parse_with_mock_date
80
75
 
76
+ def mocked_time_stack_item
77
+ Timecop.top_stack_item
78
+ end
79
+
80
+ def closest_wday(wday)
81
+ today = Date.today
82
+ result = today - today.wday
83
+ result += 1 until wday == result.wday
84
+ result
85
+ end
81
86
  end
82
87
  end
83
88
 
84
89
  class DateTime #:nodoc:
85
90
  class << self
86
91
  def mock_time
87
- mocked_time_stack_item = Timecop.top_stack_item
88
92
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime(self)
89
93
  end
90
94
 
@@ -97,19 +101,28 @@ class DateTime #:nodoc:
97
101
  alias_method :now, :now_with_mock_time
98
102
 
99
103
  def parse_with_mock_date(*args)
100
- str = args.first
101
- if str && Date::WEEKDAYS.keys.include?(str.downcase)
102
- offset = Date::WEEKDAYS[str.downcase] - DateTime.now.wday
103
-
104
- parsed_weekday =(DateTime.now + offset)
105
-
106
- DateTime.new(parsed_weekday.year, parsed_weekday.month, parsed_weekday.day, 0, 0, 0, 0)
104
+ date_hash = Date._parse(*args)
105
+ parsed_date = parse_without_mock_date(*args)
106
+ return parsed_date unless mocked_time_stack_item
107
+ date_hash = DateTime._parse(*args)
108
+
109
+ case
110
+ when date_hash[:year] && date_hash[:mon] && date_hash[:mday]
111
+ parsed_date
112
+ when date_hash[:mon] && date_hash[:mday]
113
+ DateTime.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
114
+ when date_hash[:wday]
115
+ Date.closest_wday(date_hash[:wday]).to_datetime
107
116
  else
108
- parse_without_mock_date(*args)
117
+ parsed_date + mocked_time_stack_item.travel_offset_days
109
118
  end
110
119
  end
111
120
 
112
121
  alias_method :parse_without_mock_date, :parse
113
122
  alias_method :parse, :parse_with_mock_date
123
+
124
+ def mocked_time_stack_item
125
+ Timecop.top_stack_item
126
+ end
114
127
  end
115
128
  end
@@ -43,7 +43,11 @@ class Timecop
43
43
  end
44
44
 
45
45
  def travel_offset
46
- @travel_offset
46
+ @travel_offset unless mock_type == :freeze
47
+ end
48
+
49
+ def travel_offset_days
50
+ (@travel_offset / 60 / 60 / 24).round
47
51
  end
48
52
 
49
53
  def scaling_factor
@@ -52,7 +56,7 @@ class Timecop
52
56
 
53
57
  def time(time_klass = Time) #:nodoc:
54
58
  if @time.respond_to?(:in_time_zone)
55
- time = time_klass.at(@time.dup.utc.to_r)
59
+ time = time_klass.at(@time.dup.localtime)
56
60
  else
57
61
  time = time_klass.at(@time)
58
62
  end
@@ -60,7 +64,7 @@ class Timecop
60
64
  if travel_offset.nil?
61
65
  time
62
66
  elsif scaling_factor.nil?
63
- time_klass.at((Time.now_without_mock_time + travel_offset).to_f)
67
+ time_klass.at(Time.now_without_mock_time + travel_offset)
64
68
  else
65
69
  time_klass.at(scaled_time)
66
70
  end
@@ -102,12 +106,12 @@ class Timecop
102
106
  elsif Object.const_defined?(:Date) && arg.is_a?(Date)
103
107
  time_klass.local(arg.year, arg.month, arg.day, 0, 0, 0)
104
108
  elsif args.empty? && (arg.kind_of?(Integer) || arg.kind_of?(Float))
105
- Time.now + arg
109
+ time_klass.now + arg
106
110
  elsif arg.nil?
107
- Time.now
111
+ time_klass.now
108
112
  else
109
113
  if arg.is_a?(String) && Time.respond_to?(:parse)
110
- Time.parse(arg)
114
+ time_klass.parse(arg)
111
115
  else
112
116
  # we'll just assume it's a list of y/m/d/h/m/s
113
117
  year = arg || 2000
@@ -122,7 +126,6 @@ class Timecop
122
126
  end
123
127
 
124
128
  def compute_travel_offset
125
- return nil if mock_type == :freeze
126
129
  time - Time.now_without_mock_time
127
130
  end
128
131
 
@@ -100,7 +100,7 @@ class Timecop
100
100
  end
101
101
 
102
102
  def top_stack_item #:nodoc:
103
- instance.instance_variable_get(:@_stack).last
103
+ instance.send(:stack).last
104
104
  end
105
105
 
106
106
  def safe_mode=(safe)
@@ -111,9 +111,17 @@ class Timecop
111
111
  @safe_mode ||= false
112
112
  end
113
113
 
114
+ def thread_safe=(t)
115
+ instance.send(:thread_safe=, t)
116
+ end
117
+
118
+ def thread_safe
119
+ instance.send(:thread_safe)
120
+ end
121
+
114
122
  # Returns whether or not Timecop is currently frozen/travelled
115
123
  def frozen?
116
- !instance.instance_variable_get(:@_stack).empty?
124
+ !instance.send(:stack).empty?
117
125
  end
118
126
 
119
127
  private
@@ -125,50 +133,97 @@ class Timecop
125
133
 
126
134
  private
127
135
 
128
- def baseline=(baseline)
129
- @baseline = baseline
130
- @_stack << TimeStackItem.new(:travel, baseline)
136
+ def baseline=(b)
137
+ set_baseline(b)
138
+ stack << TimeStackItem.new(:travel, b)
139
+ end
140
+
141
+ def baseline
142
+ if @thread_safe
143
+ Thread.current[:timecop_baseline]
144
+ else
145
+ @baseline
146
+ end
147
+ end
148
+
149
+ def set_baseline(b)
150
+ if @thread_safe
151
+ Thread.current[:timecop_baseline] = b
152
+ else
153
+ @baseline = b
154
+ end
155
+ end
156
+
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
165
+
166
+ def set_stack(s)
167
+ if @thread_safe
168
+ Thread.current[:timecop_stack] = s
169
+ else
170
+ @stack = s
171
+ end
131
172
  end
132
173
 
133
174
  def initialize #:nodoc:
134
- @_stack = []
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
135
187
  end
136
188
 
137
189
  def travel(mock_type, *args, &block) #:nodoc:
138
- raise SafeModeException if Timecop.safe_mode? && !block_given?
190
+ raise SafeModeException if Timecop.safe_mode? && !block_given? && !@safe
139
191
 
140
192
  stack_item = TimeStackItem.new(mock_type, *args)
141
193
 
142
- stack_backup = @_stack.dup
143
- @_stack << stack_item
194
+ stack_backup = stack.dup
195
+ stack << stack_item
144
196
 
145
197
  if block_given?
198
+ safe_backup = @safe
199
+ @safe = true
146
200
  begin
147
201
  yield stack_item.time
148
202
  ensure
149
- @_stack.replace stack_backup
203
+ @stack.replace stack_backup
204
+ @safe = safe_backup
150
205
  end
151
206
  end
152
207
  end
153
208
 
154
209
  def return(&block)
155
- current_stack = @_stack
156
- current_baseline = @baseline
210
+ current_stack = stack
211
+ current_baseline = baseline
157
212
  unmock!
158
213
  yield
159
214
  ensure
160
- @_stack = current_stack
161
- @baseline = current_baseline
215
+ set_stack current_stack
216
+ set_baseline current_baseline
162
217
  end
163
218
 
164
219
  def unmock! #:nodoc:
165
- @baseline = nil
166
- @_stack = []
220
+ set_baseline nil
221
+ set_stack []
167
222
  end
168
223
 
169
224
  def return_to_baseline
170
- if @baseline
171
- @_stack = [@_stack.shift]
225
+ if baseline
226
+ set_stack [stack.shift]
172
227
  else
173
228
  unmock!
174
229
  end
@@ -1,3 +1,3 @@
1
1
  class Timecop
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -51,4 +51,8 @@ class Minitest::Test
51
51
  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}"
52
52
  end
53
53
 
54
+ def jruby?
55
+ RUBY_PLATFORM == "java"
56
+ end
57
+
54
58
  end
@@ -1,6 +1,7 @@
1
1
  require 'date'
2
2
  require_relative "test_helper"
3
3
  require 'timecop'
4
+
4
5
  require 'active_support/all'
5
6
 
6
7
  class TestTimeStackItem < Minitest::Test
@@ -191,6 +192,15 @@ class TestTimeStackItem < Minitest::Test
191
192
  end
192
193
  end
193
194
 
195
+ def test_timezones_with_parsed_string
196
+ Time.zone = "Europe/Zurich"
197
+ time_string = "2012-12-27 12:12"
198
+ expected_time = Time.zone.parse(time_string)
199
+ Timecop.freeze(time_string) do |frozen_time|
200
+ assert_equal expected_time, frozen_time
201
+ end
202
+ end
203
+
194
204
  def test_timezones_apply_dates
195
205
  Time.zone = "Central Time (US & Canada)"
196
206
  time = Time.zone.local(2013,1,3)
@@ -251,7 +251,7 @@ class TestTimecop < Minitest::Test
251
251
  new_now = Time.now
252
252
  assert_times_effectively_equal(new_now, t, 1, "Looks like we failed to actually travel time")
253
253
  sleep(0.25)
254
- assert_times_effectively_not_equal new_now, Time.now, 0.25, "Looks like time is not moving"
254
+ assert_times_effectively_not_equal new_now, Time.now, 0.24, "Looks like time is not moving"
255
255
  end
256
256
  end
257
257
 
@@ -495,6 +495,18 @@ class TestTimecop < Minitest::Test
495
495
  end
496
496
  end
497
497
 
498
+ def test_raises_when_safe_mode_and_no_block_though_previously_block_given
499
+ Timecop.freeze do
500
+ Timecop.freeze
501
+ end
502
+
503
+ with_safe_mode do
504
+ assert_raises Timecop::SafeModeException do
505
+ Timecop.freeze
506
+ end
507
+ end
508
+ end
509
+
498
510
  def test_no_raise_when_safe_mode_and_block_used
499
511
  with_safe_mode do
500
512
  Timecop.freeze {}
@@ -507,6 +519,14 @@ class TestTimecop < Minitest::Test
507
519
  end
508
520
  end
509
521
 
522
+ def test_no_raise_when_safe_mode_and_no_block_and_in_block_context
523
+ with_safe_mode do
524
+ Timecop.freeze do
525
+ Timecop.freeze
526
+ end
527
+ end
528
+ end
529
+
510
530
  def test_date_strptime_without_year
511
531
  Timecop.freeze(Time.new(1984,2,28)) do
512
532
  assert_equal Date.strptime('04-14', '%m-%d'), Date.new(1984, 4, 14)
@@ -536,6 +556,23 @@ class TestTimecop < Minitest::Test
536
556
  assert !Timecop.frozen?
537
557
  end
538
558
 
559
+ def test_thread_safe_timecop
560
+ Timecop.thread_safe = true
561
+ date = Time.local(2011, 01, 02)
562
+ thread = Thread.new do
563
+ Timecop.freeze(date) do
564
+ sleep 1 #give main thread time to run
565
+ assert_equal date, Time.now
566
+ end
567
+ end
568
+
569
+ sleep 0.25
570
+ assert Time.now != date
571
+ thread.join
572
+ ensure
573
+ Timecop.thread_safe = false
574
+ end
575
+
539
576
  private
540
577
 
541
578
  def with_safe_mode(enabled=true)
@@ -70,7 +70,7 @@ class TestTimecopWithoutDate < Minitest::Test
70
70
  new_now = Time.now
71
71
  assert_times_effectively_equal new_now, t, 1, "Looks like we failed to actually travel time" # 0.1 seconds
72
72
  sleep(0.25)
73
- assert_times_effectively_not_equal new_now, Time.now, 0.25, "Looks like time is not moving"
73
+ assert_times_effectively_not_equal new_now, Time.now, 0.24, "Looks like time is not moving"
74
74
  end
75
75
  end
76
76
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timecop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis Jeffery
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-04-01 00:00:00.000000000 Z
12
+ date: 2017-06-22 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A gem providing "time travel" and "time freezing" capabilities, making
15
15
  it dead simple to test time-dependent code. It provides a unified method to mock