timecop 0.8.0 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6e96be7760b99a2b26e5d68800d2980190fe22c2
4
- data.tar.gz: 9c55cc8abe218ff8dd6035452ffe5e1056d592a4
2
+ SHA256:
3
+ metadata.gz: ad1963ee820d4d3519b4e078879aaa3914b2e9802460480fd9ca3c4a239677e4
4
+ data.tar.gz: 49a827fc02c8580d33d6861f51974dd046996e7281442c33f89c527d4d4c5af0
5
5
  SHA512:
6
- metadata.gz: 6f5b765cd3bc5ead77b8947e18d8230f164a1b4655f26ccc73bc7715a70666bf6e623a8567274fda5b375955f30a7bbeb825921fa24c2bb046de9cd8b26df429
7
- data.tar.gz: 2170ebdcf36371775b6b2abd27faf6ed435ba3d7469c405d8dbda47328622a9375b847de7663e25ff23a6dd8fbddc64012246f4127752a21a143d87458ee0f94
6
+ metadata.gz: d18895efbc69f3768fcf68fa372759e727c30869e5ff1a77c8b7a2901e4a89dd49fe61df38c0a5c57dd9604250e0c49733c990c32757ac9a374a56fb3962d441
7
+ data.tar.gz: 6938b5dfc45c545d8c0e83b757f1b99d9b69b2cfe23d5e1d0f9b4fc4ff56282af201a982bcf52084f05b0de88ea36256ecbe54e2988fc944693940f5179dcb97
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.png)](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`, and `DateTime.now` 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
 
@@ -104,7 +105,7 @@ being able to simulate activity via subsequent calls to your application.
104
105
  Timecop.scale(3600)
105
106
  Time.now
106
107
  # => 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
108
+ # seconds later, hours have passed and it's gone from 9pm at night to 6am in the morning
108
109
  Time.now
109
110
  # => 2012-09-21 06:22:59 -0500
110
111
  ```
@@ -128,6 +129,12 @@ Timecop.freeze
128
129
  # => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.
129
130
  ```
130
131
 
132
+ ### Rails v Ruby Date/Time libraries
133
+
134
+ Sometimes [Rails Date/Time methods don't play nicely with Ruby Date/Time methods.](https://rails.lighthouseapp.com/projects/8994/tickets/6410-dateyesterday-datetoday)
135
+
136
+ Be careful mixing Ruby `Date.today` with Rails `Date.tomorrow` / `Date.yesterday` as things might break.
137
+
131
138
  ## Contribute
132
139
 
133
140
  timecop is maintained by [travisjeffery](http://github.com/travisjeffery), and
data/Rakefile CHANGED
@@ -3,8 +3,6 @@ require 'bundler/gem_tasks'
3
3
  require 'rake/testtask'
4
4
  require 'rdoc/task'
5
5
 
6
- $LOAD_PATH.unshift("lib")
7
-
8
6
  Rake::RDocTask.new do |rdoc|
9
7
  if File.exist?('VERSION')
10
8
  version = File.read('VERSION')
@@ -21,7 +19,15 @@ Rake::RDocTask.new do |rdoc|
21
19
  end
22
20
 
23
21
  task :test do
24
- system "cd test && ./run_tests.sh" or fail
22
+ failed = Dir["test/*_test.rb"].map do |test|
23
+ command = "ruby #{test}"
24
+ puts
25
+ puts command
26
+ command unless system(command)
27
+ end.compact
28
+ if failed.any?
29
+ abort "#{failed.count} Tests failed\n#{failed.join("\n")}"
30
+ end
25
31
  end
26
32
 
27
33
  desc 'Default: run tests'
@@ -22,24 +22,15 @@ class Time #:nodoc:
22
22
  args.size <= 0 ? now : new_without_mock_time(*args)
23
23
  end
24
24
 
25
+ ruby2_keywords :new_with_mock_time if Module.private_method_defined?(:ruby2_keywords)
26
+
25
27
  alias_method :new, :new_with_mock_time
26
28
  end
27
29
  end
28
30
 
29
31
  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
32
  class << self
41
33
  def mock_date
42
- mocked_time_stack_item = Timecop.top_stack_item
43
34
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date(self)
44
35
  end
45
36
 
@@ -54,37 +45,81 @@ class Date #:nodoc:
54
45
  alias_method :strptime_without_mock_date, :strptime
55
46
 
56
47
  def strptime_with_mock_date(str = '-4712-01-01', fmt = '%F', start = Date::ITALY)
57
- unless start == Date::ITALY
58
- raise ArgumentError, "Timecop's #{self}::#{__method__} only " +
59
- "supports Date::ITALY for the start argument."
48
+ #If date is not valid the following line raises
49
+ Date.strptime_without_mock_date(str, fmt, start)
50
+
51
+ d = Date._strptime(str, fmt)
52
+ now = Time.now.to_date
53
+ year = d[:year] || d[:cwyear] || now.year
54
+ mon = d[:mon] || now.mon
55
+ if d.keys == [:year]
56
+ Date.new(year, 1, 1, start)
57
+ elsif d[:mday]
58
+ Date.new(year, mon, d[:mday], start)
59
+ elsif d[:yday]
60
+ Date.new(year, 1, 1, start).next_day(d[:yday] - 1)
61
+ elsif d[:cwyear] || d[:cweek] || d[:wnum0] || d[:wnum1] || d[:wday] || d[:cwday]
62
+ week = d[:cweek] || d[:wnum1] || d[:wnum0] || now.strftime('%W').to_i
63
+ if d[:wnum0] #Week of year where week starts on sunday
64
+ if d[:cwday] #monday based day of week
65
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:cwday]}", '%Y %U %u', start)
66
+ else
67
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:wday] || 0}", '%Y %U %w', start)
68
+ end
69
+ else #Week of year where week starts on monday
70
+ if d[:wday] #sunday based day of week
71
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:wday]}", '%Y %W %w', start)
72
+ else
73
+ Date.strptime_without_mock_date("#{year} #{week} #{d[:cwday] || 1}", '%Y %W %u', start)
74
+ end
75
+ end
76
+ elsif d[:seconds]
77
+ Time.at(d[:seconds]).to_date
78
+ else
79
+ Date.new(year, mon, 1, start)
60
80
  end
61
-
62
- Time.strptime(str, fmt).to_date
63
81
  end
64
82
 
65
83
  alias_method :strptime, :strptime_with_mock_date
66
84
 
67
85
  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
86
+ parsed_date = parse_without_mock_date(*args)
87
+ return parsed_date unless mocked_time_stack_item
88
+ date_hash = Date._parse(*args)
89
+
90
+ case
91
+ when date_hash[:year] && date_hash[:mon]
92
+ parsed_date
93
+ when date_hash[:mon] && date_hash[:mday]
94
+ Date.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
95
+ when date_hash[:mday]
96
+ Date.new(mocked_time_stack_item.year, mocked_time_stack_item.month, date_hash[:mday])
97
+ when date_hash[:wday]
98
+ closest_wday(date_hash[:wday])
73
99
  else
74
- parse_without_mock_date(*args)
100
+ parsed_date + mocked_time_stack_item.travel_offset_days
75
101
  end
76
102
  end
77
103
 
78
104
  alias_method :parse_without_mock_date, :parse
79
105
  alias_method :parse, :parse_with_mock_date
80
106
 
107
+ def mocked_time_stack_item
108
+ Timecop.top_stack_item
109
+ end
110
+
111
+ def closest_wday(wday)
112
+ today = Date.today
113
+ result = today - today.wday
114
+ result += 1 until wday == result.wday
115
+ result
116
+ end
81
117
  end
82
118
  end
83
119
 
84
120
  class DateTime #:nodoc:
85
121
  class << self
86
122
  def mock_time
87
- mocked_time_stack_item = Timecop.top_stack_item
88
123
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime(self)
89
124
  end
90
125
 
@@ -97,19 +132,29 @@ class DateTime #:nodoc:
97
132
  alias_method :now, :now_with_mock_time
98
133
 
99
134
  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)
135
+ parsed_date = parse_without_mock_date(*args)
136
+ return parsed_date unless mocked_time_stack_item
137
+ date_hash = DateTime._parse(*args)
138
+
139
+ case
140
+ when date_hash[:year] && date_hash[:mon]
141
+ parsed_date
142
+ when date_hash[:mon] && date_hash[:mday]
143
+ DateTime.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
144
+ when date_hash[:mday]
145
+ DateTime.new(mocked_time_stack_item.year, mocked_time_stack_item.month, date_hash[:mday])
146
+ when date_hash[:wday]
147
+ Date.closest_wday(date_hash[:wday]).to_datetime
107
148
  else
108
- parse_without_mock_date(*args)
149
+ parsed_date + mocked_time_stack_item.travel_offset_days
109
150
  end
110
151
  end
111
152
 
112
153
  alias_method :parse_without_mock_date, :parse
113
154
  alias_method :parse, :parse_with_mock_date
155
+
156
+ def mocked_time_stack_item
157
+ Timecop.top_stack_item
158
+ end
114
159
  end
115
160
  end
@@ -1,137 +1,140 @@
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
+ @time = parse_time(*args)
13
+ @time_was = Time.now_without_mock_time
14
+ @travel_offset = compute_travel_offset
15
+ end
16
16
 
17
- def year
18
- time.year
19
- end
17
+ def year
18
+ time.year
19
+ end
20
20
 
21
- def month
22
- time.month
23
- end
21
+ def month
22
+ time.month
23
+ end
24
24
 
25
- def day
26
- time.day
27
- end
25
+ def day
26
+ time.day
27
+ end
28
28
 
29
- def hour
30
- time.hour
31
- end
29
+ def hour
30
+ time.hour
31
+ end
32
32
 
33
- def min
34
- time.min
35
- end
33
+ def min
34
+ time.min
35
+ end
36
36
 
37
- def sec
38
- time.sec
39
- end
37
+ def sec
38
+ time.sec
39
+ end
40
40
 
41
- def utc_offset
42
- time.utc_offset
43
- end
41
+ def utc_offset
42
+ time.utc_offset
43
+ end
44
44
 
45
- def travel_offset
46
- @travel_offset
47
- end
45
+ def travel_offset
46
+ @travel_offset unless mock_type == :freeze
47
+ end
48
48
 
49
- def scaling_factor
50
- @scaling_factor
51
- end
49
+ def travel_offset_days
50
+ (@travel_offset / 60 / 60 / 24).round
51
+ end
52
52
 
53
- def time(time_klass = Time) #:nodoc:
54
- if @time.respond_to?(:in_time_zone)
55
- time = time_klass.at(@time.dup.utc.to_r)
56
- else
57
- time = time_klass.at(@time)
58
- end
53
+ def scaling_factor
54
+ @scaling_factor
55
+ end
59
56
 
60
- if travel_offset.nil?
61
- time
62
- elsif scaling_factor.nil?
63
- time_klass.at((Time.now_without_mock_time + travel_offset).to_f)
64
- else
65
- time_klass.at(scaled_time)
66
- end
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)
67
62
  end
68
63
 
69
- def scaled_time
70
- (@time + (Time.now_without_mock_time - @time_was) * scaling_factor).to_f
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)
71
70
  end
71
+ end
72
72
 
73
- def date(date_klass = Date)
74
- date_klass.jd(time.__send__(:to_date).jd)
75
- end
73
+ def scaled_time
74
+ (@time + (Time.now_without_mock_time - @time_was) * scaling_factor).to_f
75
+ end
76
76
 
77
- def datetime(datetime_klass = DateTime)
78
- if Float.method_defined?(:to_r)
79
- fractions_of_a_second = time.to_f % 1
80
- datetime_klass.new(year, month, day, hour, min, (fractions_of_a_second + sec), utc_offset_to_rational(utc_offset))
81
- else
82
- datetime_klass.new(year, month, day, hour, min, sec, utc_offset_to_rational(utc_offset))
83
- end
77
+ def date(date_klass = Date)
78
+ date_klass.jd(time.__send__(:to_date).jd)
79
+ end
80
+
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))
84
87
  end
88
+ end
85
89
 
86
- private
90
+ private
87
91
 
88
- def rational_to_utc_offset(rational)
89
- ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
90
- end
92
+ def rational_to_utc_offset(rational)
93
+ ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
94
+ end
91
95
 
92
- def utc_offset_to_rational(utc_offset)
93
- Rational(utc_offset, 24 * 60 * 60)
94
- end
96
+ def utc_offset_to_rational(utc_offset)
97
+ Rational(utc_offset, 24 * 60 * 60)
98
+ end
95
99
 
96
- def parse_time(*args)
97
- arg = args.shift
98
- if arg.is_a?(Time)
99
- arg
100
- elsif Object.const_defined?(:DateTime) && arg.is_a?(DateTime)
101
- time_klass.at(arg.to_time.to_f).getlocal
102
- elsif Object.const_defined?(:Date) && arg.is_a?(Date)
103
- time_klass.local(arg.year, arg.month, arg.day, 0, 0, 0)
104
- elsif args.empty? && arg.kind_of?(Integer)
105
- Time.now + arg
106
- elsif arg.nil?
107
- Time.now
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
112
+ else
113
+ if arg.is_a?(String) && Time.respond_to?(:parse)
114
+ time_klass.parse(arg)
108
115
  else
109
- if arg.is_a?(String) && Time.respond_to?(:parse)
110
- Time.parse(arg)
111
- else
112
- # we'll just assume it's a list of y/m/d/h/m/s
113
- year = arg || 2000
114
- month = args.shift || 1
115
- day = args.shift || 1
116
- hour = args.shift || 0
117
- minute = args.shift || 0
118
- second = args.shift || 0
119
- time_klass.local(year, month, day, hour, minute, second)
120
- end
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)
121
124
  end
122
125
  end
126
+ end
123
127
 
124
- def compute_travel_offset
125
- return nil if mock_type == :freeze
126
- time - Time.now_without_mock_time
127
- end
128
+ def compute_travel_offset
129
+ time - Time.now_without_mock_time
130
+ end
128
131
 
129
- def times_are_equal_within_epsilon t1, t2, epsilon_in_seconds
130
- (t1 - t2).abs < epsilon_in_seconds
131
- end
132
+ def times_are_equal_within_epsilon t1, t2, epsilon_in_seconds
133
+ (t1 - t2).abs < epsilon_in_seconds
134
+ end
132
135
 
133
- def time_klass
134
- Time.respond_to?(:zone) && Time.zone ? Time.zone : Time
135
- end
136
+ def time_klass
137
+ Time.respond_to?(:zone) && Time.zone ? Time.zone : Time
136
138
  end
139
+ end
137
140
  end