timecop 0.9.1 → 0.9.11

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
- SHA1:
3
- metadata.gz: 73784bb387b1ff9addf9f0ea04a14e28d679a577
4
- data.tar.gz: 18fbfbd85ceae5b1a23111267b7b39287911b080
2
+ SHA256:
3
+ metadata.gz: '079905b60d7144434c07560dadc7a1dbd36d19ad27f25ecaf52e0143e0656d23'
4
+ data.tar.gz: f34eb8593ccdd28571a4ccbdd710f518e628da6113eeb0f904e2f04d91dc5bf1
5
5
  SHA512:
6
- metadata.gz: 39552bdc1d0278558f91802906da230656323690b3a4663c6c054e2f8ad379df9f593e65265faa6d0ee6769423b0d73d3ab3a1e16c8ac0ee80896cd88e5661ba
7
- data.tar.gz: 669823d9cd754f2fa5ec195012aa43f43a070e61e3be41dda3ada13eceaffa06733cb6302bbdf88b0764da28d6a3860d74d4505ba3fa4a75be88ef96c03d0ad1
6
+ metadata.gz: bd112c3657079e9c927d266725ba6d231a23658da5f385f1dca760011eff2021a30a1607cc187b26737b6d20244a0e9d54130a5a4c46b93e04c0ddebd3ba4fad
7
+ data.tar.gz: fa6dc270173399ff8c112624913703f612ea0d25720f13b01be67ee41781b07bd6661a502b0db1e1fdbc34fc91b4acd2966526f2ee1f1acd7463d8db26f40b8e
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2012 — Travis Jeffery, John Trupiano
3
+ Copyright (c) 2019 — Travis Jeffery, John Trupiano
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
data/README.markdown CHANGED
@@ -1,14 +1,15 @@
1
1
  # timecop
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/travisjeffery/timecop.svg)](http://travis-ci.org/travisjeffery/timecop)
3
+ [![Gem Version](https://badge.fury.io/rb/timecop.svg)](https://rubygems.org/gems/timecop)
4
+ [![Build Status](https://github.com/travisjeffery/timecop/workflows/CI/badge.svg)](https://github.com/travisjeffery/timecop/actions?query=workflow%3ACI)
4
5
 
5
6
  ## DESCRIPTION
6
7
 
7
- A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.
8
+ A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock `Time.now`, `Date.today`, `DateTime.now`, and `Process.clock_gettime` in a single call.
8
9
 
9
10
  ## INSTALL
10
11
 
11
- `gem install timecop`
12
+ `bundle add timecop`
12
13
 
13
14
  ## FEATURES
14
15
 
@@ -16,13 +17,13 @@ A gem providing "time travel" and "time freezing" capabilities, making it dead s
16
17
  - Travel back to a specific point in time, but allow time to continue moving forward from there.
17
18
  - Scale time by a given scaling factor that will cause time to move at an accelerated pace.
18
19
  - No dependencies, can be used with _any_ ruby project
19
- - Timecop api allows arguments to be passed into #freeze and #travel as one of the following:
20
+ - Timecop api allows arguments to be passed into `#freeze` and `#travel` as one of the following:
20
21
  - Time instance
21
22
  - DateTime instance
22
23
  - Date instance
23
24
  - individual arguments (year, month, day, hour, minute, second)
24
- - a single integer argument that is interpreted as an offset in seconds from Time.now
25
- - Nested calls to Timecop#travel and Timecop#freeze are supported -- each block will maintain its interpretation of now.
25
+ - a single integer argument that is interpreted as an offset in seconds from `Time.now`
26
+ - Nested calls to `Timecop#travel` and `Timecop#freeze` are supported -- each block will maintain its interpretation of now.
26
27
  - Works with regular Ruby projects, and Ruby on Rails projects
27
28
 
28
29
  ## USAGE
@@ -61,7 +62,7 @@ helpful if your whole application is time-sensitive. It allows you to build
61
62
  your test data at a single point in time, and to move in/out of that time as
62
63
  appropriate (within your tests)
63
64
 
64
- in config/environments/test.rb
65
+ in `config/environments/test.rb`
65
66
 
66
67
  ```ruby
67
68
  config.after_initialize do
@@ -73,10 +74,10 @@ end
73
74
 
74
75
  ### The difference between Timecop.freeze and Timecop.travel
75
76
 
76
- freeze is used to statically mock the concept of now. As your program executes,
77
- Time.now will not change unless you make subsequent calls into the Timecop API.
78
- travel, on the other hand, computes an offset between what we currently think
79
- Time.now is (recall that we support nested traveling) and the time passed in.
77
+ `freeze` is used to statically mock the concept of now. As your program executes,
78
+ `Time.now` will not change unless you make subsequent calls into the Timecop API.
79
+ `travel`, on the other hand, computes an offset between what we currently think
80
+ `Time.now` is (recall that we support nested traveling) and the time passed in.
80
81
  It uses this offset to simulate the passage of time. To demonstrate, consider
81
82
  the following code snippets:
82
83
 
@@ -128,6 +129,15 @@ Timecop.freeze
128
129
  # => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.
129
130
  ```
130
131
 
132
+ ### Configuring Mocking Process.clock_gettime
133
+
134
+ By default Timecop does not mock Process.clock_gettime. You must enable it like this:
135
+
136
+ ``` ruby
137
+ # turn on
138
+ Timecop.mock_process_clock = true
139
+ ```
140
+
131
141
  ### Rails v Ruby Date/Time libraries
132
142
 
133
143
  Sometimes [Rails Date/Time methods don't play nicely with Ruby Date/Time methods.](https://rails.lighthouseapp.com/projects/8994/tickets/6410-dateyesterday-datetoday)
@@ -18,8 +18,14 @@ class Time #:nodoc:
18
18
 
19
19
  alias_method :new_without_mock_time, :new
20
20
 
21
- def new_with_mock_time(*args)
22
- args.size <= 0 ? now : new_without_mock_time(*args)
21
+ def new_with_mock_time(*args, **kwargs)
22
+ if args.empty? && kwargs.empty?
23
+ now
24
+ elsif kwargs.any?
25
+ new_without_mock_time(*args, **kwargs)
26
+ else
27
+ new_without_mock_time(*args)
28
+ end
23
29
  end
24
30
 
25
31
  alias_method :new, :new_with_mock_time
@@ -43,12 +49,45 @@ class Date #:nodoc:
43
49
  alias_method :strptime_without_mock_date, :strptime
44
50
 
45
51
  def strptime_with_mock_date(str = '-4712-01-01', fmt = '%F', start = Date::ITALY)
46
- unless start == Date::ITALY
47
- raise ArgumentError, "Timecop's #{self}::#{__method__} only " +
48
- "supports Date::ITALY for the start argument."
52
+ #If date is not valid the following line raises
53
+ Date.strptime_without_mock_date(str, fmt, start)
54
+
55
+ d = Date._strptime(str, fmt)
56
+ now = Time.now.to_date
57
+
58
+ # If "current" time falls near a year boundary, with a year-ambiguous str, we need to explicitly handle it
59
+ cwday = d[:cwday] && (now + (d[:cwday] - now.wday))
60
+ wday = d[:wday] && (now + (d[:wday] - now.wday))
61
+
62
+ year = d[:year] || d[:cwyear] || (cwday || wday || now).year
63
+ mon = d[:mon] || now.mon
64
+ if d.keys == [:year]
65
+ Date.new(year, 1, 1, start)
66
+ elsif d[:mday]
67
+ Date.new(year, mon, d[:mday], start)
68
+ elsif d[:yday]
69
+ Date.new(year, 1, 1, start).next_day(d[:yday] - 1)
70
+ elsif d[:cwyear] || d[:cweek] || d[:wnum0] || d[:wnum1] || d[:wday] || d[:cwday]
71
+ # When only a day (wday/cwday) is present, derive the week from the resolved date; otherwise fall back to now's week
72
+ week = d[:cweek] || d[:wnum1] || d[:wnum0] || (cwday || wday || now).strftime('%W').to_i
73
+ if d[:wnum0] #Week of year where week starts on sunday
74
+ if d[:cwday] #monday based day of week
75
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:cwday]}", '%Y %U %u', start)
76
+ else
77
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:wday] || 0}", '%Y %U %w', start)
78
+ end
79
+ else #Week of year where week starts on monday
80
+ if d[:wday] #sunday based day of week
81
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:wday]}", '%Y %W %w', start)
82
+ else
83
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:cwday] || 1}", '%Y %W %u', start)
84
+ end
85
+ end
86
+ elsif d[:seconds]
87
+ Time.at(d[:seconds]).to_date
88
+ else
89
+ Date.new(year, mon, 1, start)
49
90
  end
50
-
51
- Time.strptime(str, fmt).to_date
52
91
  end
53
92
 
54
93
  alias_method :strptime, :strptime_with_mock_date
@@ -63,6 +102,8 @@ class Date #:nodoc:
63
102
  parsed_date
64
103
  when date_hash[:mon] && date_hash[:mday]
65
104
  Date.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
105
+ when date_hash[:mday]
106
+ Date.new(mocked_time_stack_item.year, mocked_time_stack_item.month, date_hash[:mday])
66
107
  when date_hash[:wday]
67
108
  closest_wday(date_hash[:wday])
68
109
  else
@@ -101,7 +142,6 @@ class DateTime #:nodoc:
101
142
  alias_method :now, :now_with_mock_time
102
143
 
103
144
  def parse_with_mock_date(*args)
104
- date_hash = Date._parse(*args)
105
145
  parsed_date = parse_without_mock_date(*args)
106
146
  return parsed_date unless mocked_time_stack_item
107
147
  date_hash = DateTime._parse(*args)
@@ -111,8 +151,21 @@ class DateTime #:nodoc:
111
151
  parsed_date
112
152
  when date_hash[:mon] && date_hash[:mday]
113
153
  DateTime.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
154
+ when date_hash[:mday]
155
+ DateTime.new(mocked_time_stack_item.year, mocked_time_stack_item.month, date_hash[:mday])
156
+ when date_hash[:wday] && date_hash[:hour] && date_hash[:min]
157
+ closest_date = Date.closest_wday(date_hash[:wday]).to_datetime
158
+
159
+ DateTime.new(
160
+ closest_date.year, closest_date.month, closest_date.day,
161
+ date_hash[:hour], date_hash[:min]
162
+ )
114
163
  when date_hash[:wday]
115
164
  Date.closest_wday(date_hash[:wday]).to_datetime
165
+ when date_hash[:hour] && date_hash[:min] && date_hash[:sec]
166
+ DateTime.new(mocked_time_stack_item.year, mocked_time_stack_item.month, mocked_time_stack_item.day, date_hash[:hour], date_hash[:min], date_hash[:sec])
167
+ when date_hash[:hour] && date_hash[:min]
168
+ DateTime.new(mocked_time_stack_item.year, mocked_time_stack_item.month, mocked_time_stack_item.day, date_hash[:hour], date_hash[:min], 0)
116
169
  else
117
170
  parsed_date + mocked_time_stack_item.travel_offset_days
118
171
  end
@@ -126,3 +179,57 @@ class DateTime #:nodoc:
126
179
  end
127
180
  end
128
181
  end
182
+
183
+ module Process #:nodoc:
184
+ class << self
185
+ alias_method :clock_gettime_without_mock, :clock_gettime
186
+
187
+ def clock_gettime_mock_time(clock_id, unit = :float_second)
188
+ mock_time = case clock_id
189
+ when Process::CLOCK_MONOTONIC
190
+ mock_time_monotonic
191
+ when Process::CLOCK_REALTIME
192
+ mock_time_realtime
193
+ end
194
+
195
+ return clock_gettime_without_mock(clock_id, unit) unless Timecop.mock_process_clock? && mock_time
196
+
197
+ divisor = case unit
198
+ when :float_second
199
+ 1_000_000_000.0
200
+ when :second
201
+ 1_000_000_000
202
+ when :float_millisecond
203
+ 1_000_000.0
204
+ when :millisecond
205
+ 1_000_000
206
+ when :float_microsecond
207
+ 1000.0
208
+ when :microsecond
209
+ 1000
210
+ when :nanosecond
211
+ 1
212
+ end
213
+
214
+ (mock_time / divisor)
215
+ end
216
+
217
+ alias_method :clock_gettime, :clock_gettime_mock_time
218
+
219
+ private
220
+
221
+ def mock_time_monotonic
222
+ mocked_time_stack_item = Timecop.top_stack_item
223
+ mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.monotonic
224
+ end
225
+
226
+ def mock_time_realtime
227
+ mocked_time_stack_item = Timecop.top_stack_item
228
+
229
+ return nil if mocked_time_stack_item.nil?
230
+
231
+ t = mocked_time_stack_item.time
232
+ t.to_i * 1_000_000_000 + t.nsec
233
+ end
234
+ end
235
+ end
@@ -1,140 +1,169 @@
1
1
  class Timecop
2
- # A data class for carrying around "time movement" objects. Makes it easy to keep track of the time
3
- # movements on a simple stack.
4
- class TimeStackItem #:nodoc:
5
- attr_reader :mock_type
6
-
7
- def initialize(mock_type, *args)
8
- raise "Unknown mock_type #{mock_type}" unless [:freeze, :travel, :scale].include?(mock_type)
9
- @travel_offset = @scaling_factor = nil
10
- @scaling_factor = args.shift if mock_type == :scale
11
- @mock_type = mock_type
12
- @time = parse_time(*args)
13
- @time_was = Time.now_without_mock_time
14
- @travel_offset = compute_travel_offset
15
- end
2
+ # A data class for carrying around "time movement" objects. Makes it easy to keep track of the time
3
+ # movements on a simple stack.
4
+ class TimeStackItem #:nodoc:
5
+ attr_reader :mock_type
6
+
7
+ def initialize(mock_type, *args)
8
+ raise "Unknown mock_type #{mock_type}" unless [:freeze, :travel, :scale].include?(mock_type)
9
+ @travel_offset = @scaling_factor = nil
10
+ @scaling_factor = args.shift if mock_type == :scale
11
+ @mock_type = mock_type
12
+ @monotonic = parse_monotonic_time(*args)
13
+ @time = parse_time(*args)
14
+ @time_was = Time.now_without_mock_time
15
+ @travel_offset = compute_travel_offset
16
+ end
16
17
 
17
- def year
18
- time.year
19
- end
18
+ def year
19
+ time.year
20
+ end
20
21
 
21
- def month
22
- time.month
23
- end
22
+ def month
23
+ time.month
24
+ end
24
25
 
25
- def day
26
- time.day
27
- end
26
+ def day
27
+ time.day
28
+ end
28
29
 
29
- def hour
30
- time.hour
31
- end
30
+ def hour
31
+ time.hour
32
+ end
32
33
 
33
- def min
34
- time.min
35
- end
34
+ def min
35
+ time.min
36
+ end
36
37
 
37
- def sec
38
- time.sec
39
- end
38
+ def sec
39
+ time.sec
40
+ end
40
41
 
41
- def utc_offset
42
- time.utc_offset
43
- end
42
+ def utc_offset
43
+ time.utc_offset
44
+ end
44
45
 
45
- def travel_offset
46
- @travel_offset unless mock_type == :freeze
47
- end
46
+ def travel_offset
47
+ @travel_offset unless mock_type == :freeze
48
+ end
48
49
 
49
- def travel_offset_days
50
- (@travel_offset / 60 / 60 / 24).round
51
- end
50
+ def travel_offset_days
51
+ (@travel_offset / 60 / 60 / 24).round
52
+ end
53
+
54
+ def scaling_factor
55
+ @scaling_factor
56
+ end
52
57
 
53
- def scaling_factor
54
- @scaling_factor
58
+ def monotonic
59
+ if travel_offset.nil?
60
+ @monotonic
61
+ elsif scaling_factor.nil?
62
+ current_monotonic + travel_offset * (10 ** 9)
63
+ else
64
+ (@monotonic + (current_monotonic - @monotonic) * scaling_factor).to_i
55
65
  end
66
+ end
56
67
 
57
- def time(time_klass = Time) #:nodoc:
58
- if @time.respond_to?(:in_time_zone)
59
- time = time_klass.at(@time.dup.localtime)
60
- else
61
- time = time_klass.at(@time)
62
- end
68
+ def current_monotonic
69
+ Process.clock_gettime_without_mock(Process::CLOCK_MONOTONIC, :nanosecond)
70
+ end
63
71
 
64
- if travel_offset.nil?
65
- time
66
- elsif scaling_factor.nil?
67
- time_klass.at(Time.now_without_mock_time + travel_offset)
68
- else
69
- time_klass.at(scaled_time)
70
- end
71
- end
72
+ def current_monotonic_with_mock
73
+ Process.clock_gettime_mock_time(Process::CLOCK_MONOTONIC, :nanosecond)
74
+ end
72
75
 
73
- def scaled_time
74
- (@time + (Time.now_without_mock_time - @time_was) * scaling_factor).to_f
76
+ def time(time_klass = Time) #:nodoc:
77
+ if @time.respond_to?(:in_time_zone)
78
+ time = time_klass.at(@time.dup.localtime)
79
+ else
80
+ time = time_klass.at(@time)
75
81
  end
76
82
 
77
- def date(date_klass = Date)
78
- date_klass.jd(time.__send__(:to_date).jd)
83
+ if travel_offset.nil?
84
+ time
85
+ elsif scaling_factor.nil?
86
+ time_klass.at(Time.now_without_mock_time + travel_offset)
87
+ else
88
+ time_klass.at(scaled_time)
79
89
  end
90
+ end
80
91
 
81
- def datetime(datetime_klass = DateTime)
82
- if Float.method_defined?(:to_r)
83
- fractions_of_a_second = time.to_f % 1
84
- datetime_klass.new(year, month, day, hour, min, (fractions_of_a_second + sec), utc_offset_to_rational(utc_offset))
85
- else
86
- datetime_klass.new(year, month, day, hour, min, sec, utc_offset_to_rational(utc_offset))
87
- end
88
- end
92
+ def scaled_time
93
+ (@time + (Time.now_without_mock_time - @time_was) * scaling_factor).to_f
94
+ end
89
95
 
90
- private
96
+ def date(date_klass = Date)
97
+ date_klass.jd(time.__send__(:to_date).jd)
98
+ end
91
99
 
92
- def rational_to_utc_offset(rational)
93
- ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
100
+ def datetime(datetime_klass = DateTime)
101
+ if Float.method_defined?(:to_r)
102
+ fractions_of_a_second = time.to_f % 1
103
+ datetime_klass.new(year, month, day, hour, min, (fractions_of_a_second + sec), utc_offset_to_rational(utc_offset))
104
+ else
105
+ datetime_klass.new(year, month, day, hour, min, sec, utc_offset_to_rational(utc_offset))
94
106
  end
107
+ end
95
108
 
96
- def utc_offset_to_rational(utc_offset)
97
- Rational(utc_offset, 24 * 60 * 60)
109
+ private
110
+
111
+ def rational_to_utc_offset(rational)
112
+ ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
113
+ end
114
+
115
+ def utc_offset_to_rational(utc_offset)
116
+ Rational(utc_offset, 24 * 60 * 60)
117
+ end
118
+
119
+ def parse_monotonic_time(*args)
120
+ arg = args.shift
121
+ offset_in_nanoseconds = if args.empty? && (arg.kind_of?(Integer) || arg.kind_of?(Float))
122
+ arg * 1_000_000_000
123
+ else
124
+ 0
98
125
  end
126
+ current_monotonic_with_mock + offset_in_nanoseconds
127
+ end
99
128
 
100
- def parse_time(*args)
101
- arg = args.shift
102
- if arg.is_a?(Time)
103
- arg
104
- elsif Object.const_defined?(:DateTime) && arg.is_a?(DateTime)
105
- time_klass.at(arg.to_time.to_f).getlocal
106
- elsif Object.const_defined?(:Date) && arg.is_a?(Date)
107
- time_klass.local(arg.year, arg.month, arg.day, 0, 0, 0)
108
- elsif args.empty? && (arg.kind_of?(Integer) || arg.kind_of?(Float))
109
- time_klass.now + arg
110
- elsif arg.nil?
111
- time_klass.now
129
+ def parse_time(*args)
130
+ arg = args.shift
131
+ if arg.is_a?(Time)
132
+ arg
133
+ elsif Object.const_defined?(:DateTime) && arg.is_a?(DateTime)
134
+ time_klass.at(arg.to_time.to_f).getlocal
135
+ elsif Object.const_defined?(:Date) && arg.is_a?(Date)
136
+ time_klass.local(arg.year, arg.month, arg.day, 0, 0, 0)
137
+ elsif args.empty? && (arg.kind_of?(Integer) || arg.kind_of?(Float))
138
+ time_klass.now + arg
139
+ elsif arg.nil?
140
+ time_klass.now
141
+ else
142
+ if arg.is_a?(String) && Time.respond_to?(:parse)
143
+ time_klass.parse(arg)
112
144
  else
113
- if arg.is_a?(String) && Time.respond_to?(:parse)
114
- time_klass.parse(arg)
115
- else
116
- # we'll just assume it's a list of y/m/d/h/m/s
117
- year = arg || 2000
118
- month = args.shift || 1
119
- day = args.shift || 1
120
- hour = args.shift || 0
121
- minute = args.shift || 0
122
- second = args.shift || 0
123
- time_klass.local(year, month, day, hour, minute, second)
124
- end
145
+ # we'll just assume it's a list of y/m/d/h/m/s
146
+ year = arg || 2000
147
+ month = args.shift || 1
148
+ day = args.shift || 1
149
+ hour = args.shift || 0
150
+ minute = args.shift || 0
151
+ second = args.shift || 0
152
+ time_klass.local(year, month, day, hour, minute, second)
125
153
  end
126
154
  end
155
+ end
127
156
 
128
- def compute_travel_offset
129
- time - Time.now_without_mock_time
130
- end
157
+ def compute_travel_offset
158
+ time - Time.now_without_mock_time
159
+ end
131
160
 
132
- def times_are_equal_within_epsilon t1, t2, epsilon_in_seconds
133
- (t1 - t2).abs < epsilon_in_seconds
134
- end
161
+ def times_are_equal_within_epsilon t1, t2, epsilon_in_seconds
162
+ (t1 - t2).abs < epsilon_in_seconds
163
+ end
135
164
 
136
- def time_klass
137
- Time.respond_to?(:zone) && Time.zone ? Time.zone : Time
138
- end
165
+ def time_klass
166
+ Time.respond_to?(:zone) && Time.zone ? Time.zone : Time
139
167
  end
168
+ end
140
169
  end
@@ -1,5 +1,4 @@
1
1
  require 'singleton'
2
- require File.join(File.dirname(__FILE__), "time_extensions")
3
2
  require File.join(File.dirname(__FILE__), "time_stack_item")
4
3
 
5
4
  # Timecop
@@ -14,6 +13,8 @@ class Timecop
14
13
  include Singleton
15
14
 
16
15
  class << self
16
+ private :instance
17
+
17
18
  # Allows you to run a block of code and "fake" a time throughout the execution of that block.
18
19
  # This is particularly useful for writing test methods where the passage of time is critical to the business
19
20
  # logic being tested. For example:
@@ -37,6 +38,12 @@ class Timecop
37
38
  # previous values after the block has finished executing. This allows us to nest multiple
38
39
  # calls to Timecop.travel and have each block maintain it's concept of "now."
39
40
  #
41
+ # The Process.clock_gettime call mocks both CLOCK::MONOTIC and CLOCK::REALTIME
42
+ #
43
+ # CLOCK::MONOTONIC works slightly differently than other clocks. This clock cannot move to a
44
+ # particular date/time. So the only option that changes this clock is #4 which will move the
45
+ # clock the requested offset. Otherwise the clock is frozen to the current tick.
46
+ #
40
47
  # * Note: Timecop.freeze will actually freeze time. This can cause unanticipated problems if
41
48
  # benchmark or other timing calls are executed, which implicitly expect Time to actually move
42
49
  # forward.
@@ -75,11 +82,11 @@ class Timecop
75
82
  end
76
83
 
77
84
  def baseline
78
- instance.send(:baseline)
85
+ instance.baseline
79
86
  end
80
87
 
81
88
  def baseline=(baseline)
82
- instance.send(:baseline=, baseline)
89
+ instance.baseline = baseline
83
90
  end
84
91
 
85
92
  # Reverts back to system's Time.now, Date.today and DateTime.now (if it exists) permamently when
@@ -87,20 +94,21 @@ class Timecop
87
94
  # the given block.
88
95
  def return(&block)
89
96
  if block_given?
90
- instance.send(:return, &block)
97
+ instance.return(&block)
91
98
  else
92
- instance.send(:unmock!)
99
+ instance.unmock!
93
100
  nil
94
101
  end
95
102
  end
103
+ alias :unfreeze :return
96
104
 
97
105
  def return_to_baseline
98
- instance.send(:return_to_baseline)
106
+ instance.return_to_baseline
99
107
  Time.now
100
108
  end
101
109
 
102
110
  def top_stack_item #:nodoc:
103
- instance.send(:stack).last
111
+ instance.stack.last
104
112
  end
105
113
 
106
114
  def safe_mode=(safe)
@@ -112,27 +120,43 @@ class Timecop
112
120
  end
113
121
 
114
122
  def thread_safe=(t)
115
- instance.send(:thread_safe=, t)
123
+ instance.thread_safe = t
116
124
  end
117
125
 
118
126
  def thread_safe
119
- instance.send(:thread_safe)
127
+ instance.thread_safe
120
128
  end
121
129
 
122
- # Returns whether or not Timecop is currently frozen/travelled
130
+ # Returns whether or not Timecop is currently frozen
123
131
  def frozen?
124
- !instance.send(:stack).empty?
132
+ !instance.stack.empty? && instance.stack.last.mock_type == :freeze
133
+ end
134
+
135
+ # Returns whether or not Timecop is currently travelled
136
+ def travelled?
137
+ !instance.stack.empty? && instance.stack.last.mock_type == :travel
138
+ end
139
+
140
+ # Returns whether or not Timecop is currently scaled
141
+ def scaled?
142
+ !instance.stack.empty? && instance.stack.last.mock_type == :scale
143
+ end
144
+
145
+ def mock_process_clock=(mock)
146
+ @mock_process_clock = mock
147
+ end
148
+
149
+ def mock_process_clock?
150
+ @mock_process_clock ||= false
125
151
  end
126
152
 
127
153
  private
128
154
  def send_travel(mock_type, *args, &block)
129
- val = instance.send(:travel, mock_type, *args, &block)
155
+ val = instance.travel(mock_type, *args, &block)
130
156
  block_given? ? val : Time.now
131
157
  end
132
158
  end
133
159
 
134
- private
135
-
136
160
  def baseline=(b)
137
161
  set_baseline(b)
138
162
  stack << TimeStackItem.new(:travel, b)
@@ -200,7 +224,7 @@ class Timecop
200
224
  begin
201
225
  yield stack_item.time
202
226
  ensure
203
- @stack.replace stack_backup
227
+ stack.replace stack_backup
204
228
  @safe = safe_backup
205
229
  end
206
230
  end
@@ -235,3 +259,6 @@ class Timecop
235
259
  end
236
260
  end
237
261
  end
262
+
263
+ # This must be done after TimeCop is available
264
+ require File.join(File.dirname(__FILE__), "time_extensions")
@@ -1,3 +1,3 @@
1
1
  class Timecop
2
- VERSION = "0.9.1"
2
+ VERSION = "0.9.11"
3
3
  end