activesupport 5.2.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +362 -333
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +33 -33
  10. data/lib/active_support/cache/mem_cache_store.rb +31 -29
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +84 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +174 -113
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +76 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +46 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  36. data/lib/active_support/core_ext/digest.rb +3 -0
  37. data/lib/active_support/core_ext/enumerable.rb +171 -70
  38. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  39. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  40. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  41. data/lib/active_support/core_ext/hash/except.rb +2 -2
  42. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  43. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  44. data/lib/active_support/core_ext/hash.rb +1 -2
  45. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  46. data/lib/active_support/core_ext/kernel.rb +0 -1
  47. data/lib/active_support/core_ext/load_error.rb +1 -1
  48. data/lib/active_support/core_ext/marshal.rb +2 -0
  49. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  50. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  51. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  52. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  53. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  54. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  55. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  56. data/lib/active_support/core_ext/module.rb +0 -1
  57. data/lib/active_support/core_ext/name_error.rb +29 -2
  58. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  59. data/lib/active_support/core_ext/numeric.rb +0 -1
  60. data/lib/active_support/core_ext/object/blank.rb +1 -2
  61. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  62. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  63. data/lib/active_support/core_ext/object/json.rb +7 -2
  64. data/lib/active_support/core_ext/object/to_query.rb +5 -2
  65. data/lib/active_support/core_ext/object/try.rb +17 -7
  66. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  67. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  68. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  69. data/lib/active_support/core_ext/range/each.rb +0 -1
  70. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  71. data/lib/active_support/core_ext/range.rb +1 -1
  72. data/lib/active_support/core_ext/regexp.rb +8 -5
  73. data/lib/active_support/core_ext/securerandom.rb +23 -3
  74. data/lib/active_support/core_ext/string/access.rb +5 -16
  75. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  76. data/lib/active_support/core_ext/string/filters.rb +42 -1
  77. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  78. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  79. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  80. data/lib/active_support/core_ext/string/output_safety.rb +69 -12
  81. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  82. data/lib/active_support/core_ext/string/strip.rb +3 -1
  83. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  84. data/lib/active_support/core_ext/symbol.rb +3 -0
  85. data/lib/active_support/core_ext/time/calculations.rb +50 -3
  86. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  87. data/lib/active_support/core_ext/uri.rb +7 -5
  88. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  89. data/lib/active_support/current_attributes.rb +15 -2
  90. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  91. data/lib/active_support/dependencies.rb +118 -35
  92. data/lib/active_support/deprecation/behaviors.rb +20 -3
  93. data/lib/active_support/deprecation/disallowed.rb +56 -0
  94. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  95. data/lib/active_support/deprecation/method_wrappers.rb +21 -13
  96. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  97. data/lib/active_support/deprecation/reporting.rb +51 -8
  98. data/lib/active_support/deprecation.rb +6 -1
  99. data/lib/active_support/descendants_tracker.rb +59 -9
  100. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  101. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  102. data/lib/active_support/duration.rb +90 -38
  103. data/lib/active_support/encrypted_configuration.rb +1 -5
  104. data/lib/active_support/encrypted_file.rb +23 -5
  105. data/lib/active_support/environment_inquirer.rb +20 -0
  106. data/lib/active_support/evented_file_update_checker.rb +82 -117
  107. data/lib/active_support/execution_wrapper.rb +1 -0
  108. data/lib/active_support/file_update_checker.rb +0 -1
  109. data/lib/active_support/fork_tracker.rb +62 -0
  110. data/lib/active_support/gem_version.rb +2 -2
  111. data/lib/active_support/hash_with_indifferent_access.rb +78 -41
  112. data/lib/active_support/i18n.rb +1 -0
  113. data/lib/active_support/i18n_railtie.rb +16 -5
  114. data/lib/active_support/inflector/inflections.rb +2 -7
  115. data/lib/active_support/inflector/methods.rb +50 -57
  116. data/lib/active_support/inflector/transliterate.rb +47 -18
  117. data/lib/active_support/json/decoding.rb +25 -26
  118. data/lib/active_support/json/encoding.rb +11 -3
  119. data/lib/active_support/key_generator.rb +1 -33
  120. data/lib/active_support/lazy_load_hooks.rb +5 -2
  121. data/lib/active_support/locale/en.rb +33 -0
  122. data/lib/active_support/locale/en.yml +7 -3
  123. data/lib/active_support/log_subscriber.rb +39 -9
  124. data/lib/active_support/logger.rb +2 -17
  125. data/lib/active_support/logger_silence.rb +11 -19
  126. data/lib/active_support/logger_thread_safe_level.rb +52 -7
  127. data/lib/active_support/message_encryptor.rb +8 -13
  128. data/lib/active_support/message_verifier.rb +10 -10
  129. data/lib/active_support/messages/metadata.rb +11 -2
  130. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  131. data/lib/active_support/messages/rotator.rb +10 -9
  132. data/lib/active_support/multibyte/chars.rb +10 -68
  133. data/lib/active_support/multibyte/unicode.rb +15 -327
  134. data/lib/active_support/notifications/fanout.rb +116 -16
  135. data/lib/active_support/notifications/instrumenter.rb +71 -9
  136. data/lib/active_support/notifications.rb +72 -8
  137. data/lib/active_support/number_helper/number_converter.rb +5 -6
  138. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  139. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  140. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  141. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  142. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  143. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  144. data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
  145. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  146. data/lib/active_support/number_helper.rb +38 -12
  147. data/lib/active_support/option_merger.rb +22 -3
  148. data/lib/active_support/ordered_hash.rb +1 -1
  149. data/lib/active_support/ordered_options.rb +13 -3
  150. data/lib/active_support/parameter_filter.rb +133 -0
  151. data/lib/active_support/per_thread_registry.rb +1 -1
  152. data/lib/active_support/rails.rb +1 -10
  153. data/lib/active_support/railtie.rb +23 -1
  154. data/lib/active_support/reloader.rb +4 -5
  155. data/lib/active_support/secure_compare_rotator.rb +51 -0
  156. data/lib/active_support/security_utils.rb +19 -12
  157. data/lib/active_support/string_inquirer.rb +4 -3
  158. data/lib/active_support/subscriber.rb +72 -24
  159. data/lib/active_support/tagged_logging.rb +42 -8
  160. data/lib/active_support/test_case.rb +92 -1
  161. data/lib/active_support/testing/assertions.rb +30 -9
  162. data/lib/active_support/testing/deprecation.rb +0 -1
  163. data/lib/active_support/testing/file_fixtures.rb +2 -0
  164. data/lib/active_support/testing/isolation.rb +2 -2
  165. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  166. data/lib/active_support/testing/parallelization/server.rb +78 -0
  167. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  168. data/lib/active_support/testing/parallelization.rb +51 -0
  169. data/lib/active_support/testing/setup_and_teardown.rb +5 -9
  170. data/lib/active_support/testing/stream.rb +1 -2
  171. data/lib/active_support/testing/time_helpers.rb +47 -12
  172. data/lib/active_support/time_with_zone.rb +81 -47
  173. data/lib/active_support/values/time_zone.rb +34 -18
  174. data/lib/active_support/xml_mini/jdom.rb +2 -3
  175. data/lib/active_support/xml_mini/libxml.rb +2 -2
  176. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  177. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  178. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  179. data/lib/active_support/xml_mini/rexml.rb +10 -3
  180. data/lib/active_support/xml_mini.rb +2 -10
  181. data/lib/active_support.rb +14 -1
  182. metadata +57 -30
  183. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  184. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  185. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  186. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  187. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  188. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  189. data/lib/active_support/core_ext/range/include_range.rb +0 -25
  190. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Testing
5
+ class Parallelization # :nodoc:
6
+ class Worker
7
+ def initialize(number, url)
8
+ @id = SecureRandom.uuid
9
+ @number = number
10
+ @url = url
11
+ @setup_exception = nil
12
+ end
13
+
14
+ def start
15
+ fork do
16
+ set_process_title("(starting)")
17
+
18
+ DRb.stop_service
19
+
20
+ @queue = DRbObject.new_with_uri(@url)
21
+ @queue.start_worker(@id)
22
+
23
+ begin
24
+ after_fork
25
+ rescue => @setup_exception; end
26
+
27
+ work_from_queue
28
+ ensure
29
+ set_process_title("(stopping)")
30
+
31
+ run_cleanup
32
+ @queue.stop_worker(@id)
33
+ end
34
+ end
35
+
36
+ def work_from_queue
37
+ while job = @queue.pop
38
+ perform_job(job)
39
+ end
40
+ end
41
+
42
+ def perform_job(job)
43
+ klass = job[0]
44
+ method = job[1]
45
+ reporter = job[2]
46
+
47
+ set_process_title("#{klass}##{method}")
48
+
49
+ result = klass.with_info_handler reporter do
50
+ Minitest.run_one_method(klass, method)
51
+ end
52
+
53
+ safe_record(reporter, result)
54
+ end
55
+
56
+ def safe_record(reporter, result)
57
+ add_setup_exception(result) if @setup_exception
58
+
59
+ begin
60
+ @queue.record(reporter, result)
61
+ rescue DRb::DRbConnError
62
+ result.failures.map! do |failure|
63
+ if failure.respond_to?(:error)
64
+ # minitest >5.14.0
65
+ error = DRb::DRbRemoteError.new(failure.error)
66
+ else
67
+ error = DRb::DRbRemoteError.new(failure.exception)
68
+ end
69
+ Minitest::UnexpectedError.new(error)
70
+ end
71
+ @queue.record(reporter, result)
72
+ end
73
+
74
+ set_process_title("(idle)")
75
+ end
76
+
77
+ def after_fork
78
+ Parallelization.after_fork_hooks.each do |cb|
79
+ cb.call(@number)
80
+ end
81
+ end
82
+
83
+ def run_cleanup
84
+ Parallelization.run_cleanup_hooks.each do |cb|
85
+ cb.call(@number)
86
+ end
87
+ end
88
+
89
+ private
90
+ def add_setup_exception(result)
91
+ result.failures.prepend Minitest::UnexpectedError.new(@setup_exception)
92
+ end
93
+
94
+ def set_process_title(status)
95
+ Process.setproctitle("Rails test worker #{@number} - #{status}")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "drb"
4
+ require "drb/unix" unless Gem.win_platform?
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require "active_support/testing/parallelization/server"
7
+ require "active_support/testing/parallelization/worker"
8
+
9
+ module ActiveSupport
10
+ module Testing
11
+ class Parallelization # :nodoc:
12
+ @@after_fork_hooks = []
13
+
14
+ def self.after_fork_hook(&blk)
15
+ @@after_fork_hooks << blk
16
+ end
17
+
18
+ cattr_reader :after_fork_hooks
19
+
20
+ @@run_cleanup_hooks = []
21
+
22
+ def self.run_cleanup_hook(&blk)
23
+ @@run_cleanup_hooks << blk
24
+ end
25
+
26
+ cattr_reader :run_cleanup_hooks
27
+
28
+ def initialize(worker_count)
29
+ @worker_count = worker_count
30
+ @queue_server = Server.new
31
+ @worker_pool = []
32
+ @url = DRb.start_service("drbunix:", @queue_server).uri
33
+ end
34
+
35
+ def start
36
+ @worker_pool = @worker_count.times.map do |worker|
37
+ Worker.new(worker, @url).start
38
+ end
39
+ end
40
+
41
+ def <<(work)
42
+ @queue_server << work
43
+ end
44
+
45
+ def shutdown
46
+ @queue_server.shutdown
47
+ @worker_pool.each { |pid| Process.waitpid pid }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/concern"
4
3
  require "active_support/callbacks"
5
4
 
6
5
  module ActiveSupport
@@ -19,11 +18,10 @@ module ActiveSupport
19
18
  # end
20
19
  # end
21
20
  module SetupAndTeardown
22
- extend ActiveSupport::Concern
23
-
24
- included do
25
- include ActiveSupport::Callbacks
26
- define_callbacks :setup, :teardown
21
+ def self.prepended(klass)
22
+ klass.include ActiveSupport::Callbacks
23
+ klass.define_callbacks :setup, :teardown
24
+ klass.extend ClassMethods
27
25
  end
28
26
 
29
27
  module ClassMethods
@@ -47,12 +45,10 @@ module ActiveSupport
47
45
  begin
48
46
  run_callbacks :teardown
49
47
  rescue => e
50
- error = e
48
+ self.failures << Minitest::UnexpectedError.new(e)
51
49
  end
52
50
 
53
51
  super
54
- ensure
55
- raise error if error
56
52
  end
57
53
  end
58
54
  end
@@ -4,7 +4,6 @@ module ActiveSupport
4
4
  module Testing
5
5
  module Stream #:nodoc:
6
6
  private
7
-
8
7
  def silence_stream(stream)
9
8
  old_stream = stream.dup
10
9
  stream.reopen(IO::NULL)
@@ -33,7 +32,7 @@ module ActiveSupport
33
32
  yield
34
33
 
35
34
  stream_io.rewind
36
- return captured_stream.read
35
+ captured_stream.read
37
36
  ensure
38
37
  captured_stream.close
39
38
  captured_stream.unlink
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/redefine_method"
4
- require "active_support/core_ext/string/strip" # for strip_heredoc
5
4
  require "active_support/core_ext/time/calculations"
6
5
  require "concurrent/map"
7
6
 
8
7
  module ActiveSupport
9
8
  module Testing
9
+ # Manages stubs for TimeHelpers
10
10
  class SimpleStubs # :nodoc:
11
11
  Stub = Struct.new(:object, :method_name, :original_method)
12
12
 
@@ -14,6 +14,13 @@ module ActiveSupport
14
14
  @stubs = Concurrent::Map.new { |h, k| h[k] = {} }
15
15
  end
16
16
 
17
+ # Stubs object.method_name with the given block
18
+ # If the method is already stubbed, remove that stub
19
+ # so that removing this stub will restore the original implementation.
20
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
21
+ # target = Time.zone.local(2004, 11, 24, 1, 4, 44)
22
+ # simple_stubs.stub_object(Time, :now) { at(target.to_i) }
23
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
17
24
  def stub_object(object, method_name, &block)
18
25
  if stub = stubbing(object, method_name)
19
26
  unstub_object(stub)
@@ -23,10 +30,11 @@ module ActiveSupport
23
30
 
24
31
  @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
25
32
 
26
- object.singleton_class.send :alias_method, new_name, method_name
33
+ object.singleton_class.alias_method new_name, method_name
27
34
  object.define_singleton_method(method_name, &block)
28
35
  end
29
36
 
37
+ # Remove all object-method stubs held by this instance
30
38
  def unstub_all!
31
39
  @stubs.each_value do |object_stubs|
32
40
  object_stubs.each_value do |stub|
@@ -36,17 +44,24 @@ module ActiveSupport
36
44
  @stubs.clear
37
45
  end
38
46
 
47
+ # Returns the Stub for object#method_name
48
+ # (nil if it is not stubbed)
39
49
  def stubbing(object, method_name)
40
50
  @stubs[object.object_id][method_name]
41
51
  end
42
52
 
43
- private
53
+ # Returns true if any stubs are set, false if there are none
54
+ def stubbed?
55
+ !@stubs.empty?
56
+ end
44
57
 
58
+ private
59
+ # Restores the original object.method described by the Stub
45
60
  def unstub_object(stub)
46
61
  singleton_class = stub.object.singleton_class
47
- singleton_class.send :silence_redefinition_of_method, stub.method_name
48
- singleton_class.send :alias_method, stub.method_name, stub.original_method
49
- singleton_class.send :undef_method, stub.original_method
62
+ singleton_class.silence_redefinition_of_method stub.method_name
63
+ singleton_class.alias_method stub.method_name, stub.original_method
64
+ singleton_class.undef_method stub.original_method
50
65
  end
51
66
  end
52
67
 
@@ -84,7 +99,7 @@ module ActiveSupport
84
99
  # The stubs are automatically removed at the end of the test.
85
100
  #
86
101
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
87
- # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
102
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
88
103
  # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
89
104
  # Date.current # => Wed, 24 Nov 2004
90
105
  # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
@@ -106,13 +121,13 @@ module ActiveSupport
106
121
  # state at the end of the block:
107
122
  #
108
123
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
109
- # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44) do
124
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) do
110
125
  # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
111
126
  # end
112
127
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
113
128
  def travel_to(date_or_time)
114
129
  if block_given? && simple_stubs.stubbing(Time, :now)
115
- travel_to_nested_block_call = <<-MSG.strip_heredoc
130
+ travel_to_nested_block_call = <<~MSG
116
131
 
117
132
  Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
118
133
 
@@ -159,16 +174,37 @@ module ActiveSupport
159
174
  end
160
175
 
161
176
  # Returns the current time back to its original state, by removing the stubs added by
162
- # +travel+ and +travel_to+.
177
+ # +travel+, +travel_to+, and +freeze_time+.
163
178
  #
164
179
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
165
- # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
180
+ #
181
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
166
182
  # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
183
+ #
167
184
  # travel_back
168
185
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
186
+ #
187
+ # This method also accepts a block, which brings the stubs back at the end of the block:
188
+ #
189
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
190
+ #
191
+ # travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
192
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
193
+ #
194
+ # travel_back do
195
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
196
+ # end
197
+ #
198
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
169
199
  def travel_back
200
+ stubbed_time = Time.current if block_given? && simple_stubs.stubbed?
201
+
170
202
  simple_stubs.unstub_all!
203
+ yield if block_given?
204
+ ensure
205
+ travel_to stubbed_time if stubbed_time
171
206
  end
207
+ alias_method :unfreeze_time, :travel_back
172
208
 
173
209
  # Calls +travel_to+ with +Time.now+.
174
210
  #
@@ -191,7 +227,6 @@ module ActiveSupport
191
227
  end
192
228
 
193
229
  private
194
-
195
230
  def simple_stubs
196
231
  @simple_stubs ||= SimpleStubs.new
197
232
  end
@@ -15,25 +15,25 @@ module ActiveSupport
15
15
  # and +in_time_zone+ on Time and DateTime instances.
16
16
  #
17
17
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
18
- # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
19
- # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
20
- # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
21
- # Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
22
- # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
18
+ # Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
19
+ # Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
20
+ # Time.zone.at(1171139445) # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
21
+ # Time.zone.now # => Sun, 18 May 2008 13:07:55.754107581 EDT -04:00
22
+ # Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45.000000000 EST -05:00
23
23
  #
24
24
  # See Time and TimeZone for further documentation of these methods.
25
25
  #
26
26
  # TimeWithZone instances implement the same API as Ruby Time instances, so
27
27
  # that Time and TimeWithZone instances are interchangeable.
28
28
  #
29
- # t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
29
+ # t = Time.zone.now # => Sun, 18 May 2008 13:27:25.031505668 EDT -04:00
30
30
  # t.hour # => 13
31
31
  # t.dst? # => true
32
32
  # t.utc_offset # => -14400
33
33
  # t.zone # => "EDT"
34
34
  # t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
35
- # t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
36
- # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
35
+ # t + 1.day # => Mon, 19 May 2008 13:27:25.031505668 EDT -04:00
36
+ # t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00.000000000 EST -05:00
37
37
  # t > Time.utc(1999) # => true
38
38
  # t.is_a?(Time) # => true
39
39
  # t.is_a?(ActiveSupport::TimeWithZone) # => true
@@ -43,8 +43,8 @@ module ActiveSupport
43
43
  "Time"
44
44
  end
45
45
 
46
- PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N".freeze }
47
- PRECISIONS[0] = "%FT%T".freeze
46
+ PRECISIONS = Hash.new { |h, n| h[n] = "%FT%T.%#{n}N" }
47
+ PRECISIONS[0] = "%FT%T"
48
48
 
49
49
  include Comparable, DateAndTime::Compatibility
50
50
  attr_reader :time_zone
@@ -57,12 +57,12 @@ module ActiveSupport
57
57
 
58
58
  # Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
59
59
  def time
60
- @time ||= period.to_local(@utc)
60
+ @time ||= incorporate_utc_offset(@utc, utc_offset)
61
61
  end
62
62
 
63
63
  # Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
64
64
  def utc
65
- @utc ||= period.to_utc(@time)
65
+ @utc ||= incorporate_utc_offset(@time, -utc_offset)
66
66
  end
67
67
  alias_method :comparable_time, :utc
68
68
  alias_method :getgm, :utc
@@ -104,13 +104,13 @@ module ActiveSupport
104
104
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
105
105
  # Time.zone.now.utc? # => false
106
106
  def utc?
107
- period.offset.abbreviation == :UTC || period.offset.abbreviation == :UCT
107
+ zone == "UTC" || zone == "UCT"
108
108
  end
109
109
  alias_method :gmt?, :utc?
110
110
 
111
111
  # Returns the offset from current time to UTC time in seconds.
112
112
  def utc_offset
113
- period.utc_total_offset
113
+ period.observed_utc_offset
114
114
  end
115
115
  alias_method :gmt_offset, :utc_offset
116
116
  alias_method :gmtoff, :utc_offset
@@ -132,14 +132,14 @@ module ActiveSupport
132
132
  # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
133
133
  # Time.zone.now.zone # => "EST"
134
134
  def zone
135
- period.zone_identifier.to_s
135
+ period.abbreviation
136
136
  end
137
137
 
138
138
  # Returns a string of the object's date, time, zone, and offset from UTC.
139
139
  #
140
- # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25 EST -05:00"
140
+ # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25.624541392 EST -05:00"
141
141
  def inspect
142
- "#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
142
+ "#{time.strftime('%a, %d %b %Y %H:%M:%S.%9N')} #{zone} #{formatted_offset}"
143
143
  end
144
144
 
145
145
  # Returns a string of the object's date and time in the ISO 8601 standard
@@ -147,7 +147,7 @@ module ActiveSupport
147
147
  #
148
148
  # Time.zone.now.xmlschema # => "2014-12-04T11:02:37-05:00"
149
149
  def xmlschema(fraction_digits = 0)
150
- "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z'.freeze)}"
150
+ "#{time.strftime(PRECISIONS[fraction_digits.to_i])}#{formatted_offset(true, 'Z')}"
151
151
  end
152
152
  alias_method :iso8601, :xmlschema
153
153
  alias_method :rfc3339, :xmlschema
@@ -225,6 +225,8 @@ module ActiveSupport
225
225
  def <=>(other)
226
226
  utc <=> other
227
227
  end
228
+ alias_method :before?, :<
229
+ alias_method :after?, :>
228
230
 
229
231
  # Returns true if the current object's time is within the specified
230
232
  # +min+ and +max+ time.
@@ -243,6 +245,20 @@ module ActiveSupport
243
245
  time.today?
244
246
  end
245
247
 
248
+ # Returns true if the current object's time falls within
249
+ # the next day (tomorrow).
250
+ def tomorrow?
251
+ time.tomorrow?
252
+ end
253
+ alias :next_day? :tomorrow?
254
+
255
+ # Returns true if the current object's time falls within
256
+ # the previous day (yesterday).
257
+ def yesterday?
258
+ time.yesterday?
259
+ end
260
+ alias :prev_day? :yesterday?
261
+
246
262
  # Returns true if the current object's time is in the future.
247
263
  def future?
248
264
  utc.future?
@@ -261,8 +277,8 @@ module ActiveSupport
261
277
  # value as a new TimeWithZone object.
262
278
  #
263
279
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
264
- # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
265
- # now + 1000 # => Sun, 02 Nov 2014 01:43:08 EDT -04:00
280
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
281
+ # now + 1000 # => Sun, 02 Nov 2014 01:43:08.725182881 EDT -04:00
266
282
  #
267
283
  # If we're adding a Duration of variable length (i.e., years, months, days),
268
284
  # move forward from #time, otherwise move forward from #utc, for accuracy
@@ -271,8 +287,8 @@ module ActiveSupport
271
287
  # For instance, a time + 24.hours will advance exactly 24 hours, while a
272
288
  # time + 1.day will advance 23-25 hours, depending on the day.
273
289
  #
274
- # now + 24.hours # => Mon, 03 Nov 2014 00:26:28 EST -05:00
275
- # now + 1.day # => Mon, 03 Nov 2014 01:26:28 EST -05:00
290
+ # now + 24.hours # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
291
+ # now + 1.day # => Mon, 03 Nov 2014 01:26:28.725182881 EST -05:00
276
292
  def +(other)
277
293
  if duration_of_variable_length?(other)
278
294
  method_missing(:+, other)
@@ -284,12 +300,14 @@ module ActiveSupport
284
300
  alias_method :since, :+
285
301
  alias_method :in, :+
286
302
 
287
- # Returns a new TimeWithZone object that represents the difference between
288
- # the current object's time and the +other+ time.
303
+ # Subtracts an interval of time and returns a new TimeWithZone object unless
304
+ # the other value `acts_like?` time. Then it will return a Float of the difference
305
+ # between the two times that represents the difference between the current
306
+ # object's time and the +other+ time.
289
307
  #
290
308
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
291
- # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
292
- # now - 1000 # => Mon, 03 Nov 2014 00:09:48 EST -05:00
309
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
310
+ # now - 1000 # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00
293
311
  #
294
312
  # If subtracting a Duration of variable length (i.e., years, months, days),
295
313
  # move backward from #time, otherwise move backward from #utc, for accuracy
@@ -298,8 +316,14 @@ module ActiveSupport
298
316
  # For instance, a time - 24.hours will go subtract exactly 24 hours, while a
299
317
  # time - 1.day will subtract 23-25 hours, depending on the day.
300
318
  #
301
- # now - 24.hours # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
302
- # now - 1.day # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
319
+ # now - 24.hours # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
320
+ # now - 1.day # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00
321
+ #
322
+ # If both the TimeWithZone object and the other value act like Time, a Float
323
+ # will be returned.
324
+ #
325
+ # Time.zone.now - 1.day.ago # => 86399.999967
326
+ #
303
327
  def -(other)
304
328
  if other.acts_like?(:time)
305
329
  to_time - other.to_time
@@ -315,8 +339,8 @@ module ActiveSupport
315
339
  # the result as a new TimeWithZone object.
316
340
  #
317
341
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
318
- # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
319
- # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48 EST -05:00
342
+ # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28.725182881 EST -05:00
343
+ # now.ago(1000) # => Mon, 03 Nov 2014 00:09:48.725182881 EST -05:00
320
344
  #
321
345
  # If we're subtracting a Duration of variable length (i.e., years, months,
322
346
  # days), move backward from #time, otherwise move backward from #utc, for
@@ -326,8 +350,8 @@ module ActiveSupport
326
350
  # while <tt>time.ago(1.day)</tt> will move back 23-25 hours, depending on
327
351
  # the day.
328
352
  #
329
- # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
330
- # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
353
+ # now.ago(24.hours) # => Sun, 02 Nov 2014 01:26:28.725182881 EDT -04:00
354
+ # now.ago(1.day) # => Sun, 02 Nov 2014 00:26:28.725182881 EDT -04:00
331
355
  def ago(other)
332
356
  since(-other)
333
357
  end
@@ -343,12 +367,12 @@ module ActiveSupport
343
367
  # or <tt>:nsec</tt>, not both. Similarly, pass either <tt>:zone</tt> or
344
368
  # <tt>:offset</tt>, not both.
345
369
  #
346
- # t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15 EST -05:00
347
- # t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15 EST -05:00
348
- # t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00 EST -05:00
349
- # t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00 EST -05:00
350
- # t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15 HST -10:00
351
- # t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15 HST -10:00
370
+ # t = Time.zone.now # => Fri, 14 Apr 2017 11:45:15.116992711 EST -05:00
371
+ # t.change(year: 2020) # => Tue, 14 Apr 2020 11:45:15.116992711 EST -05:00
372
+ # t.change(hour: 12) # => Fri, 14 Apr 2017 12:00:00.116992711 EST -05:00
373
+ # t.change(min: 30) # => Fri, 14 Apr 2017 11:30:00.116992711 EST -05:00
374
+ # t.change(offset: "-10:00") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
375
+ # t.change(zone: "Hawaii") # => Fri, 14 Apr 2017 11:45:15.116992711 HST -10:00
352
376
  def change(options)
353
377
  if options[:zone] && options[:offset]
354
378
  raise ArgumentError, "Can't change both :offset and :zone at the same time: #{options.inspect}"
@@ -381,14 +405,14 @@ module ActiveSupport
381
405
  # accuracy when moving across DST boundaries.
382
406
  #
383
407
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
384
- # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
385
- # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29 EDT -04:00
386
- # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28 EDT -04:00
387
- # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28 EST -05:00
388
- # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28 EST -05:00
389
- # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28 EST -05:00
390
- # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28 EST -05:00
391
- # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28 EST -05:00
408
+ # now = Time.zone.now # => Sun, 02 Nov 2014 01:26:28.558049687 EDT -04:00
409
+ # now.advance(seconds: 1) # => Sun, 02 Nov 2014 01:26:29.558049687 EDT -04:00
410
+ # now.advance(minutes: 1) # => Sun, 02 Nov 2014 01:27:28.558049687 EDT -04:00
411
+ # now.advance(hours: 1) # => Sun, 02 Nov 2014 01:26:28.558049687 EST -05:00
412
+ # now.advance(days: 1) # => Mon, 03 Nov 2014 01:26:28.558049687 EST -05:00
413
+ # now.advance(weeks: 1) # => Sun, 09 Nov 2014 01:26:28.558049687 EST -05:00
414
+ # now.advance(months: 1) # => Tue, 02 Dec 2014 01:26:28.558049687 EST -05:00
415
+ # now.advance(years: 1) # => Mon, 02 Nov 2015 01:26:28.558049687 EST -05:00
392
416
  def advance(options)
393
417
  # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
394
418
  # otherwise advance from #utc, for accuracy when moving across DST boundaries
@@ -410,7 +434,7 @@ module ActiveSupport
410
434
  # Returns Array of parts of Time in sequence of
411
435
  # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone].
412
436
  #
413
- # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00
437
+ # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27.485278555 UTC +00:00
414
438
  # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"]
415
439
  def to_a
416
440
  [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
@@ -514,6 +538,16 @@ module ActiveSupport
514
538
  end
515
539
 
516
540
  private
541
+ SECONDS_PER_DAY = 86400
542
+
543
+ def incorporate_utc_offset(time, offset)
544
+ if time.kind_of?(Date)
545
+ time + Rational(offset, SECONDS_PER_DAY)
546
+ else
547
+ time + offset
548
+ end
549
+ end
550
+
517
551
  def get_period_and_ensure_valid_local_time(period)
518
552
  # we don't want a Time.local instance enforcing its own DST rules as well,
519
553
  # so transfer time values to a utc constructor if necessary