timecop-rspec 1.0.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.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.devcontainer/devcontainer.json +26 -0
  4. data/.env.local.example +3 -0
  5. data/.envrc +41 -0
  6. data/.github/FUNDING.yml +13 -0
  7. data/.github/dependabot.yml +12 -0
  8. data/.github/workflows/ancient.yml +77 -0
  9. data/.github/workflows/auto-assign.yml +21 -0
  10. data/.github/workflows/codeql-analysis.yml +70 -0
  11. data/.github/workflows/coverage.yml +126 -0
  12. data/.github/workflows/current.yml +85 -0
  13. data/.github/workflows/dependency-review.yml +20 -0
  14. data/.github/workflows/deps_locked.yml +75 -0
  15. data/.github/workflows/deps_unlocked.yml +84 -0
  16. data/.github/workflows/discord-notifier.yml +38 -0
  17. data/.github/workflows/heads.yml +84 -0
  18. data/.github/workflows/jruby.yml +75 -0
  19. data/.github/workflows/legacy.yml +67 -0
  20. data/.github/workflows/style.yml +64 -0
  21. data/.github/workflows/supported.yml +82 -0
  22. data/.github/workflows/truffle.yml +75 -0
  23. data/.github/workflows/unsupported.yml +75 -0
  24. data/.gitignore +45 -0
  25. data/.gitlab-ci.yml +44 -0
  26. data/.idea/.gitignore +8 -0
  27. data/.idea/GitLink.xml +6 -0
  28. data/.idea/misc.xml +4 -0
  29. data/.idea/modules.xml +8 -0
  30. data/.idea/timecop-rspec.iml +120 -0
  31. data/.idea/vcs.xml +6 -0
  32. data/.junie/TASK_NOTE.md +1 -0
  33. data/.junie/guidelines.md +139 -0
  34. data/.qlty/qlty.toml +79 -0
  35. data/.rspec +8 -0
  36. data/.rubocop.yml +15 -0
  37. data/.rubocop_gradual.lock +76 -0
  38. data/.simplecov +11 -0
  39. data/.tool-versions +1 -0
  40. data/.yard_gfm_support.rb +22 -0
  41. data/.yardopts +11 -0
  42. data/Appraisal.root.gemfile +12 -0
  43. data/Appraisals +104 -0
  44. data/CHANGELOG.md +27 -0
  45. data/CITATION.cff +20 -0
  46. data/CNAME +1 -0
  47. data/CODE_OF_CONDUCT.md +134 -0
  48. data/CONTRIBUTING.md +145 -0
  49. data/Gemfile +38 -0
  50. data/Gemfile.lock +315 -0
  51. data/LICENSE.txt +22 -0
  52. data/README.md +563 -0
  53. data/REEK +0 -0
  54. data/RUBOCOP.md +71 -0
  55. data/Rakefile +287 -0
  56. data/SECURITY.md +21 -0
  57. data/bin/appraisal +16 -0
  58. data/bin/bundle-audit +16 -0
  59. data/bin/bundler-audit +16 -0
  60. data/bin/code_climate_reek +16 -0
  61. data/bin/coderay +16 -0
  62. data/bin/console +38 -0
  63. data/bin/erb +16 -0
  64. data/bin/gem_checksums +16 -0
  65. data/bin/htmldiff +16 -0
  66. data/bin/irb +16 -0
  67. data/bin/kramdown +16 -0
  68. data/bin/ldiff +16 -0
  69. data/bin/nokogiri +16 -0
  70. data/bin/pry +16 -0
  71. data/bin/racc +16 -0
  72. data/bin/rake +16 -0
  73. data/bin/rdbg +16 -0
  74. data/bin/rdoc +16 -0
  75. data/bin/reek +16 -0
  76. data/bin/ri +16 -0
  77. data/bin/rspec +16 -0
  78. data/bin/rubocop +16 -0
  79. data/bin/rubocop-gradual +16 -0
  80. data/bin/ruby-parse +16 -0
  81. data/bin/ruby-rewrite +16 -0
  82. data/bin/setup +33 -0
  83. data/bin/standardrb +16 -0
  84. data/bin/thor +16 -0
  85. data/bin/yard +16 -0
  86. data/bin/yard-junk +16 -0
  87. data/bin/yardoc +16 -0
  88. data/bin/yri +16 -0
  89. data/certs/pboling.pem +27 -0
  90. data/checksums/timecop-rspec-1.0.0.gem.sha256 +1 -0
  91. data/checksums/timecop-rspec-1.0.0.gem.sha512 +1 -0
  92. data/docs/Timecop/Rspec/ExampleDecorator.html +626 -0
  93. data/docs/Timecop/Rspec/SequentialTimeMachine.html +338 -0
  94. data/docs/Timecop/Rspec/TimeMachine.html +357 -0
  95. data/docs/Timecop/Rspec/TravelLog.html +440 -0
  96. data/docs/Timecop/Rspec/Traveler.html +353 -0
  97. data/docs/Timecop/Rspec/Version.html +154 -0
  98. data/docs/Timecop/Rspec.html +474 -0
  99. data/docs/Timecop.html +149 -0
  100. data/docs/_index.html +218 -0
  101. data/docs/class_list.html +54 -0
  102. data/docs/css/common.css +1 -0
  103. data/docs/css/full_list.css +58 -0
  104. data/docs/css/style.css +503 -0
  105. data/docs/file.CHANGELOG.html +99 -0
  106. data/docs/file.CITATION.html +92 -0
  107. data/docs/file.CODE_OF_CONDUCT.html +201 -0
  108. data/docs/file.CONTRIBUTING.html +220 -0
  109. data/docs/file.LICENSE.html +70 -0
  110. data/docs/file.README.html +523 -0
  111. data/docs/file.REEK.html +71 -0
  112. data/docs/file.RUBOCOP.html +171 -0
  113. data/docs/file.SECURITY.html +101 -0
  114. data/docs/file.rspec.html +109 -0
  115. data/docs/file.timecop.html +72 -0
  116. data/docs/file_list.html +109 -0
  117. data/docs/frames.html +22 -0
  118. data/docs/index.html +523 -0
  119. data/docs/js/app.js +344 -0
  120. data/docs/js/full_list.js +242 -0
  121. data/docs/js/jquery.js +4 -0
  122. data/docs/method_list.html +198 -0
  123. data/docs/top-level-namespace.html +110 -0
  124. data/gemfiles/audit.gemfile +10 -0
  125. data/gemfiles/coverage.gemfile +10 -0
  126. data/gemfiles/current.gemfile +8 -0
  127. data/gemfiles/deps_unlocked.gemfile +13 -0
  128. data/gemfiles/head.gemfile +9 -0
  129. data/gemfiles/modular/audit.gemfile +5 -0
  130. data/gemfiles/modular/coverage.gemfile +6 -0
  131. data/gemfiles/modular/documentation.gemfile +11 -0
  132. data/gemfiles/modular/style.gemfile +14 -0
  133. data/gemfiles/ruby_2_3.gemfile +5 -0
  134. data/gemfiles/ruby_2_4.gemfile +5 -0
  135. data/gemfiles/ruby_2_5.gemfile +5 -0
  136. data/gemfiles/ruby_2_6.gemfile +8 -0
  137. data/gemfiles/ruby_2_7.gemfile +8 -0
  138. data/gemfiles/ruby_3_0.gemfile +8 -0
  139. data/gemfiles/ruby_3_1.gemfile +8 -0
  140. data/gemfiles/ruby_3_2.gemfile +8 -0
  141. data/gemfiles/ruby_3_3.gemfile +8 -0
  142. data/gemfiles/style.gemfile +10 -0
  143. data/lib/timecop/rspec/example_decorator.rb +100 -0
  144. data/lib/timecop/rspec/sequential_time_machine.rb +80 -0
  145. data/lib/timecop/rspec/time_machine.rb +58 -0
  146. data/lib/timecop/rspec/travel_log.rb +103 -0
  147. data/lib/timecop/rspec/traveler.rb +68 -0
  148. data/lib/timecop/rspec/version.rb +37 -0
  149. data/lib/timecop/rspec.rb +78 -0
  150. data/sig/timecop/rspec.rbs +40 -0
  151. data/sig/timecop.rbs +2 -0
  152. data/timecop-rspec.gemspec +146 -0
  153. data.tar.gz.sig +2 -0
  154. metadata +434 -0
  155. metadata.gz.sig +0 -0
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Documentation
4
+ gem "kramdown", "~> 2.5", ">= 2.5.1" # Ruby >= 2.5
5
+ gem "kramdown-parser-gfm", "~> 1.1" # Ruby >= 2.3
6
+ gem "yard", "~> 0.9", ">= 0.9.37", :require => false
7
+ gem "yard-junk", "~> 0.0", ">= 0.0.10", :github => "pboling/yard-junk", :branch => "next", :require => false
8
+ gem "yard-relative_markdown_links", "~> 0.5.0"
9
+
10
+ # Std Lib extractions
11
+ gem "rdoc", "~> 6.11"
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # We run rubocop on the latest version of Ruby,
4
+ # but in support of the oldest supported version of Ruby
5
+
6
+ gem "reek", "~> 6.4"
7
+ gem "rubocop", "~> 1.73", ">= 1.73.2"
8
+ gem "rubocop-lts", "~> 0.1", ">= 0.1.1" # Linting for Ruby >= 1.8
9
+ gem "rubocop-packaging", "~> 0.6", ">= 0.6.0"
10
+ gem "rubocop-rspec", "~> 3.2"
11
+ gem "standard", ">= 1.50"
12
+
13
+ # Std Lib extractions
14
+ gem "benchmark", "~> 0.4", ">= 0.4.1" # Removed from Std Lib in Ruby 3.5
@@ -0,0 +1,5 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec :path => "../"
@@ -0,0 +1,5 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec :path => "../"
@@ -0,0 +1,5 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mutex_m", "~> 0.2"
6
+ gem "stringio", "~> 3.0"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mutex_m", "~> 0.2"
6
+ gem "stringio", "~> 3.0"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mutex_m", "~> 0.2"
6
+ gem "stringio", "~> 3.0"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mutex_m", "~> 0.2"
6
+ gem "stringio", "~> 3.0"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mutex_m", "~> 0.2"
6
+ gem "stringio", "~> 3.0"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mutex_m", "~> 0.2"
6
+ gem "stringio", "~> 3.0"
7
+
8
+ gemspec :path => "../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal2
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mutex_m", "~> 0.2"
6
+ gem "stringio", "~> 3.0"
7
+
8
+ gemspec :path => "../"
9
+
10
+ eval_gemfile("modular/style.gemfile")
@@ -0,0 +1,100 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2014-2017 Avant
4
+
5
+ # Author Zach Taylor
6
+
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ class Timecop
26
+ module Rspec
27
+ # Decorates an RSpec example to interpret timecop metadata.
28
+ #
29
+ # Recognized metadata keys:
30
+ # - :freeze => Time/Date/DateTime/String/Proc to freeze time to
31
+ # - :travel => Time/Date/DateTime/String/Proc to travel to (and optionally continue)
32
+ # - :skip_global_timecop => when present, disables global time for this example
33
+ class ExampleDecorator < SimpleDelegator
34
+ # @return [Boolean] whether any timecop behavior applies to the example
35
+ def timecop?
36
+ local_timecop? || global_timecop?
37
+ end
38
+
39
+ # Determines the timecop method to invoke.
40
+ # @return [Symbol, nil] :freeze, :travel, or nil
41
+ def timecop_method
42
+ local_timecop_method || global_timecop_method
43
+ end
44
+
45
+ # Determines the time to use for the chosen timecop method.
46
+ # @return [Object, nil] a time-like object or nil when not applicable
47
+ def timecop_time
48
+ local_timecop_time || global_timecop_time
49
+ end
50
+
51
+ # Whether the example has local timecop metadata.
52
+ # @return [Boolean]
53
+ def local_timecop?
54
+ local_timecop_method.present?
55
+ end
56
+
57
+ # Whether a global time is configured and not skipped by the example.
58
+ # @return [Boolean]
59
+ def global_timecop?
60
+ Rspec.global_time_configured? && !skip_global_timecop?
61
+ end
62
+
63
+ # Whether the example requested skipping global timecop behavior.
64
+ # @return [Boolean]
65
+ def skip_global_timecop?
66
+ metadata.key?(:skip_global_timecop)
67
+ end
68
+
69
+ private
70
+
71
+ # Reads the timecop method from example metadata.
72
+ # @return [Symbol, nil]
73
+ def local_timecop_method
74
+ metadata.keys.find do |key|
75
+ key == :freeze || key == :travel
76
+ end
77
+ end
78
+
79
+ # Evaluates local timecop time, supporting Proc values evaluated in the example context.
80
+ # @return [Object, nil]
81
+ def local_timecop_time
82
+ time = metadata[timecop_method]
83
+ return if time.nil?
84
+ time.respond_to?(:call) ? example.instance_exec(&time) : time
85
+ end
86
+
87
+ # Global time always uses :travel.
88
+ # @return [Symbol, nil]
89
+ def global_timecop_method
90
+ :travel if global_timecop?
91
+ end
92
+
93
+ # Global time value when applicable.
94
+ # @return [Time, nil]
95
+ def global_timecop_time
96
+ Rspec.global_time if global_timecop?
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,80 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2014-2017 Avant
4
+
5
+ # Author Zach Taylor
6
+
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ require_relative "example_decorator"
26
+ require_relative "traveler"
27
+ require_relative "travel_log"
28
+
29
+ class Timecop
30
+ module Rspec
31
+ # Executes examples while allowing time travel to continue across examples.
32
+ #
33
+ # Uses TravelLog to coalesce sequential :travel operations when the same
34
+ # method and start time are reused, enabling long-running time journeys
35
+ # across multiple examples.
36
+ class SequentialTimeMachine
37
+ # Singleton instance accessor.
38
+ # @return [Timecop::Rspec::SequentialTimeMachine]
39
+ def self.instance
40
+ @instance ||= new
41
+ end
42
+
43
+ # Run an example with either local or global travel, or without any timecop.
44
+ # @param example [#run,#metadata]
45
+ # @return [Object]
46
+ def run(example)
47
+ example = ExampleDecorator.new(example)
48
+
49
+ runner_for(example).run
50
+ end
51
+
52
+ private
53
+
54
+ # Selects the appropriate runner for the example.
55
+ # @param example [ExampleDecorator]
56
+ # @return [#run]
57
+ def runner_for(example)
58
+ if example.local_timecop?
59
+ Traveler.new(example, local_travel_log)
60
+ elsif example.global_timecop?
61
+ Traveler.new(example, global_travel_log)
62
+ else
63
+ example
64
+ end
65
+ end
66
+
67
+ # Local travel log used for examples that specify local timecop metadata.
68
+ # @return [TravelLog]
69
+ def local_travel_log
70
+ @local_travel_log ||= TravelLog.new
71
+ end
72
+
73
+ # Global travel log used when a global time is configured.
74
+ # @return [TravelLog]
75
+ def global_travel_log
76
+ @global_travel_log ||= TravelLog.new(:travel, Rspec.global_time)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,58 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2014-2017 Avant
4
+
5
+ # Author Zach Taylor
6
+
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ require "timecop"
26
+
27
+ require_relative "example_decorator"
28
+
29
+ class Timecop
30
+ module Rspec
31
+ # Executes an example using Timecop for a single, isolated time operation.
32
+ class TimeMachine
33
+ # Singleton instance accessor.
34
+ # @return [Timecop::Rspec::TimeMachine]
35
+ def self.instance
36
+ @instance ||= new
37
+ end
38
+
39
+ # Run an RSpec example, applying local timecop metadata if present.
40
+ #
41
+ # @param example [#run,#metadata] An RSpec example or object responding to
42
+ # the example protocol that will be wrapped by ExampleDecorator.
43
+ # @return [Object] the result of running the example
44
+ def run(example)
45
+ example = ExampleDecorator.new(example)
46
+
47
+ return example.run unless example.timecop?
48
+
49
+ method = example.timecop_method
50
+ time = example.timecop_time
51
+
52
+ Timecop.public_send(method, time) do
53
+ example.run
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,103 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2014-2017 Avant
4
+
5
+ # Author Zach Taylor
6
+
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ class Timecop
26
+ module Rspec
27
+ # Tracks details about a time travel operation that can be resumed later.
28
+ class TravelLog
29
+ # @param travel_method [Symbol, nil] :travel or :freeze (or nil)
30
+ # @param start_time [Object, nil] a time-like object or string
31
+ def initialize(travel_method = nil, start_time = nil)
32
+ new_trip(travel_method, start_time)
33
+ end
34
+
35
+ # Either resumes a previous trip or starts a new one.
36
+ # @param travel_method [Symbol] :travel or :freeze
37
+ # @param start_time [Object] a time-like object or string
38
+ # @return [Object] the computed starting time for the trip
39
+ def resume_or_new_trip(travel_method, start_time)
40
+ if resume_trip?(travel_method, start_time)
41
+ resume_trip
42
+ else
43
+ new_trip(travel_method, start_time)
44
+ end
45
+ end
46
+
47
+ # Pauses the current trip, recording its duration.
48
+ # @return [void]
49
+ def pause_trip
50
+ self.trip_duration = Time.current - coalesced_start_time
51
+ end
52
+
53
+ private
54
+
55
+ attr_accessor :travel_method, :start_time, :trip_duration
56
+
57
+ # Starts a new trip tracking session.
58
+ # @param travel_method [Symbol, nil]
59
+ # @param start_time [Object, nil]
60
+ # @return [void]
61
+ def new_trip(travel_method, start_time)
62
+ reset_duration
63
+ self.travel_method = travel_method
64
+ self.start_time = start_time
65
+ end
66
+
67
+ # Determines whether the provided method/time match the current trip.
68
+ # @return [Boolean]
69
+ def resume_trip?(other_travel_method, other_start_time)
70
+ travel_method == other_travel_method &&
71
+ start_time == other_start_time &&
72
+ start_time.class == other_start_time.class
73
+ end
74
+
75
+ # The start time for resuming a trip including the elapsed duration.
76
+ # @return [Object]
77
+ def resume_trip
78
+ coalesced_start_time + trip_duration.seconds
79
+ end
80
+
81
+ # Coerces various time-like inputs into a Time-like baseline.
82
+ # @return [Object]
83
+ def coalesced_start_time
84
+ case start_time
85
+ when DateTime
86
+ start_time
87
+ when Date
88
+ start_time.at_beginning_of_day
89
+ when String
90
+ Time.parse(start_time)
91
+ else
92
+ start_time
93
+ end
94
+ end
95
+
96
+ # Resets the recorded duration.
97
+ # @return [void]
98
+ def reset_duration
99
+ self.trip_duration = 0
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,68 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2014-2017 Avant
4
+
5
+ # Author Zach Taylor
6
+
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ class Timecop
26
+ module Rspec
27
+ # Runs an example, optionally continuing a travel across invocations.
28
+ class Traveler
29
+ # @param example [ExampleDecorator]
30
+ # @param travel_log [TravelLog]
31
+ def initialize(example, travel_log)
32
+ @example = example
33
+ @travel_log = travel_log
34
+ end
35
+
36
+ # Executes the example within the appropriate Timecop context.
37
+ # If the method is :travel, the starting time may be adjusted based on
38
+ # prior trips recorded in the travel log.
39
+ #
40
+ # @return [Object]
41
+ def run
42
+ method = example.timecop_method
43
+ time = example.timecop_time
44
+
45
+ if method == :travel
46
+ time = travel_log.resume_or_new_trip(
47
+ example.timecop_method, example.timecop_time
48
+ )
49
+ end
50
+
51
+ ::Timecop.public_send(method, time) do
52
+ begin
53
+ example.run
54
+ ensure
55
+ travel_log.pause_trip if method == :travel
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ # @return [ExampleDecorator]
63
+ attr_reader :example
64
+ # @return [TravelLog]
65
+ attr_reader :travel_log
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,37 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2014-2017 Avant
4
+
5
+ # Author Zach Taylor
6
+
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ class Timecop
26
+ module Rspec
27
+ # Version namespace for Timecop::Rspec.
28
+ module Version
29
+ # The gem version.
30
+ # @return [String]
31
+ VERSION = "1.0.0"
32
+ end
33
+ # Convenience constant aliasing Version::VERSION
34
+ # @return [String]
35
+ VERSION = Version::VERSION
36
+ end
37
+ end
@@ -0,0 +1,78 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2014-2017 Avant
4
+
5
+ # Author Zach Taylor
6
+
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ require "timecop/rspec/version"
26
+ require "active_support/all"
27
+
28
+ Dir.glob(File.join(__dir__, "rspec", "**", "*.rb")).each { |file| require file }
29
+
30
+ # Top-level namespace for Timecop helpers.
31
+ class Timecop
32
+ # RSpec integration for the timecop gem.
33
+ #
34
+ # Provides helpers to run examples with frozen or traveling time, and an
35
+ # optional global time that applies across examples when configured via ENV.
36
+ #
37
+ # Environment variables:
38
+ # - GLOBAL_TIME_TRAVEL_TIME: String representation of a time (preferred)
39
+ # - GLOBAL_TIME_TRAVEL_DATE: String representation of a date (fallback)
40
+ module Rspec
41
+ class << self
42
+ # Selects a time machine strategy.
43
+ #
44
+ # @param sequential [Boolean] when true, uses a sequential strategy that
45
+ # can continue a travel across examples; when false, uses a simple per
46
+ # example strategy.
47
+ # @return [Timecop::Rspec::SequentialTimeMachine, Timecop::Rspec::TimeMachine]
48
+ def time_machine(sequential: false)
49
+ if sequential
50
+ SequentialTimeMachine.instance
51
+ else
52
+ TimeMachine.instance
53
+ end
54
+ end
55
+
56
+ # Whether a global time has been configured via ENV.
57
+ # @return [Boolean]
58
+ def global_time_configured?
59
+ global_time_travel_string.present?
60
+ end
61
+
62
+ # The globally configured time parsed from ENV.
63
+ # @return [Time] the parsed time
64
+ # @raise [ArgumentError] if ENV contains an unparsable time/date string
65
+ def global_time
66
+ @global_time ||= Time.parse(global_time_travel_string)
67
+ end
68
+
69
+ private
70
+
71
+ # The raw ENV value used to determine the global time.
72
+ # @return [String, nil]
73
+ def global_time_travel_string
74
+ ENV["GLOBAL_TIME_TRAVEL_TIME"] || ENV["GLOBAL_TIME_TRAVEL_DATE"]
75
+ end
76
+ end
77
+ end
78
+ end