activesupport 5.2.7 → 6.0.0.beta1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +182 -566
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/backtrace_cleaner.rb +23 -0
  6. data/lib/active_support/cache/file_store.rb +19 -12
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -2
  8. data/lib/active_support/cache/memory_store.rb +5 -0
  9. data/lib/active_support/cache/null_store.rb +5 -0
  10. data/lib/active_support/cache/redis_cache_store.rb +39 -20
  11. data/lib/active_support/cache.rb +40 -18
  12. data/lib/active_support/callbacks.rb +16 -5
  13. data/lib/active_support/configurable.rb +4 -8
  14. data/lib/active_support/core_ext/array/extract.rb +21 -0
  15. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  16. data/lib/active_support/core_ext/array.rb +1 -1
  17. data/lib/active_support/core_ext/class/attribute.rb +1 -1
  18. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  19. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  20. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -17
  21. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  22. data/lib/active_support/core_ext/enumerable.rb +71 -67
  23. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  24. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  25. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  26. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  27. data/lib/active_support/core_ext/hash.rb +0 -2
  28. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  29. data/lib/active_support/core_ext/load_error.rb +1 -1
  30. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -5
  31. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -14
  32. data/lib/active_support/core_ext/module/delegation.rb +27 -7
  33. data/lib/active_support/core_ext/module/introspection.rb +37 -13
  34. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  35. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  36. data/lib/active_support/core_ext/module.rb +0 -1
  37. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  38. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  39. data/lib/active_support/core_ext/numeric.rb +0 -1
  40. data/lib/active_support/core_ext/object/blank.rb +1 -2
  41. data/lib/active_support/core_ext/object/duplicable.rb +5 -2
  42. data/lib/active_support/core_ext/object/json.rb +1 -0
  43. data/lib/active_support/core_ext/object/try.rb +15 -7
  44. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  45. data/lib/active_support/core_ext/range/compare_range.rb +1 -1
  46. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  47. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  48. data/lib/active_support/core_ext/regexp.rb +0 -4
  49. data/lib/active_support/core_ext/securerandom.rb +23 -3
  50. data/lib/active_support/core_ext/string/access.rb +8 -0
  51. data/lib/active_support/core_ext/string/filters.rb +41 -0
  52. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  53. data/lib/active_support/core_ext/string/output_safety.rb +16 -5
  54. data/lib/active_support/core_ext/string/strip.rb +3 -1
  55. data/lib/active_support/core_ext/uri.rb +1 -0
  56. data/lib/active_support/current_attributes.rb +2 -0
  57. data/lib/active_support/dependencies.rb +28 -11
  58. data/lib/active_support/deprecation/behaviors.rb +1 -1
  59. data/lib/active_support/deprecation/method_wrappers.rb +4 -5
  60. data/lib/active_support/deprecation/proxy_wrappers.rb +0 -2
  61. data/lib/active_support/deprecation.rb +1 -1
  62. data/lib/active_support/descendants_tracker.rb +6 -5
  63. data/lib/active_support/duration/iso8601_parser.rb +2 -3
  64. data/lib/active_support/duration/iso8601_serializer.rb +3 -4
  65. data/lib/active_support/duration.rb +12 -14
  66. data/lib/active_support/encrypted_configuration.rb +0 -4
  67. data/lib/active_support/evented_file_update_checker.rb +25 -7
  68. data/lib/active_support/execution_wrapper.rb +14 -16
  69. data/lib/active_support/gem_version.rb +4 -4
  70. data/lib/active_support/hash_with_indifferent_access.rb +16 -28
  71. data/lib/active_support/i18n.rb +1 -0
  72. data/lib/active_support/i18n_railtie.rb +8 -1
  73. data/lib/active_support/inflector/inflections.rb +1 -4
  74. data/lib/active_support/inflector/methods.rb +15 -27
  75. data/lib/active_support/inflector/transliterate.rb +6 -6
  76. data/lib/active_support/json/decoding.rb +23 -23
  77. data/lib/active_support/json/encoding.rb +6 -2
  78. data/lib/active_support/key_generator.rb +0 -32
  79. data/lib/active_support/lazy_load_hooks.rb +5 -1
  80. data/lib/active_support/locale/en.rb +31 -0
  81. data/lib/active_support/log_subscriber.rb +31 -8
  82. data/lib/active_support/logger.rb +0 -15
  83. data/lib/active_support/logger_silence.rb +28 -12
  84. data/lib/active_support/logger_thread_safe_level.rb +27 -6
  85. data/lib/active_support/message_encryptor.rb +2 -4
  86. data/lib/active_support/message_verifier.rb +2 -2
  87. data/lib/active_support/multibyte/chars.rb +29 -48
  88. data/lib/active_support/multibyte/unicode.rb +44 -281
  89. data/lib/active_support/notifications/fanout.rb +42 -4
  90. data/lib/active_support/notifications/instrumenter.rb +73 -2
  91. data/lib/active_support/notifications.rb +32 -4
  92. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
  93. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
  94. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
  95. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
  96. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  97. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
  98. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
  99. data/lib/active_support/number_helper.rb +7 -0
  100. data/lib/active_support/ordered_options.rb +1 -1
  101. data/lib/active_support/parameter_filter.rb +124 -0
  102. data/lib/active_support/rails.rb +0 -6
  103. data/lib/active_support/reloader.rb +5 -6
  104. data/lib/active_support/subscriber.rb +16 -26
  105. data/lib/active_support/tagged_logging.rb +13 -4
  106. data/lib/active_support/test_case.rb +91 -0
  107. data/lib/active_support/testing/assertions.rb +15 -1
  108. data/lib/active_support/testing/deprecation.rb +0 -1
  109. data/lib/active_support/testing/file_fixtures.rb +2 -0
  110. data/lib/active_support/testing/isolation.rb +2 -2
  111. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  112. data/lib/active_support/testing/parallelization.rb +109 -0
  113. data/lib/active_support/testing/stream.rb +1 -1
  114. data/lib/active_support/testing/time_helpers.rb +7 -7
  115. data/lib/active_support/time_with_zone.rb +15 -5
  116. data/lib/active_support/values/time_zone.rb +12 -7
  117. data/lib/active_support/xml_mini/jdom.rb +2 -2
  118. data/lib/active_support/xml_mini/libxml.rb +2 -2
  119. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  120. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  121. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  122. data/lib/active_support/xml_mini/rexml.rb +2 -2
  123. data/lib/active_support/xml_mini.rb +2 -9
  124. data/lib/active_support.rb +1 -1
  125. metadata +12 -10
  126. data/lib/active_support/core_ext/digest.rb +0 -3
  127. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -11,6 +11,8 @@ require "active_support/testing/isolation"
11
11
  require "active_support/testing/constant_lookup"
12
12
  require "active_support/testing/time_helpers"
13
13
  require "active_support/testing/file_fixtures"
14
+ require "active_support/testing/parallelization"
15
+ require "concurrent/utility/processor_counter"
14
16
 
15
17
  module ActiveSupport
16
18
  class TestCase < ::Minitest::Test
@@ -39,6 +41,95 @@ module ActiveSupport
39
41
  def test_order
40
42
  ActiveSupport.test_order ||= :random
41
43
  end
44
+
45
+ # Parallelizes the test suite.
46
+ #
47
+ # Takes a +workers+ argument that controls how many times the process
48
+ # is forked. For each process a new database will be created suffixed
49
+ # with the worker number.
50
+ #
51
+ # test-database-0
52
+ # test-database-1
53
+ #
54
+ # If <tt>ENV["PARALLEL_WORKERS"]</tt> is set the workers argument will be ignored
55
+ # and the environment variable will be used instead. This is useful for CI
56
+ # environments, or other environments where you may need more workers than
57
+ # you do for local testing.
58
+ #
59
+ # If the number of workers is set to +1+ or fewer, the tests will not be
60
+ # parallelized.
61
+ #
62
+ # If +workers+ is set to +:number_of_processors+, the number of workers will be
63
+ # set to the actual core count on the machine you are on.
64
+ #
65
+ # The default parallelization method is to fork processes. If you'd like to
66
+ # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
67
+ # method. Note the threaded parallelization does not create multiple
68
+ # database and will not work with system tests at this time.
69
+ #
70
+ # parallelize(workers: :number_of_processors, with: :threads)
71
+ #
72
+ # The threaded parallelization uses minitest's parallel executor directly.
73
+ # The processes parallelization uses a Ruby DRb server.
74
+ def parallelize(workers: :number_of_processors, with: :processes)
75
+ workers = Concurrent.physical_processor_count if workers == :number_of_processors
76
+ workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
77
+
78
+ return if workers <= 1
79
+
80
+ executor = case with
81
+ when :processes
82
+ Testing::Parallelization.new(workers)
83
+ when :threads
84
+ Minitest::Parallel::Executor.new(workers)
85
+ else
86
+ raise ArgumentError, "#{with} is not a supported parallelization executor."
87
+ end
88
+
89
+ self.lock_threads = false if defined?(self.lock_threads) && with == :threads
90
+
91
+ Minitest.parallel_executor = executor
92
+
93
+ parallelize_me!
94
+ end
95
+
96
+ # Set up hook for parallel testing. This can be used if you have multiple
97
+ # databases or any behavior that needs to be run after the process is forked
98
+ # but before the tests run.
99
+ #
100
+ # Note: this feature is not available with the threaded parallelization.
101
+ #
102
+ # In your +test_helper.rb+ add the following:
103
+ #
104
+ # class ActiveSupport::TestCase
105
+ # parallelize_setup do
106
+ # # create databases
107
+ # end
108
+ # end
109
+ def parallelize_setup(&block)
110
+ ActiveSupport::Testing::Parallelization.after_fork_hook do |worker|
111
+ yield worker
112
+ end
113
+ end
114
+
115
+ # Clean up hook for parallel testing. This can be used to drop databases
116
+ # if your app uses multiple write/read databases or other clean up before
117
+ # the tests finish. This runs before the forked process is closed.
118
+ #
119
+ # Note: this feature is not available with the threaded parallelization.
120
+ #
121
+ # In your +test_helper.rb+ add the following:
122
+ #
123
+ # class ActiveSupport::TestCase
124
+ # parallelize_teardown do
125
+ # # drop databases
126
+ # end
127
+ # end
128
+ def parallelize_teardown(&block)
129
+ ActiveSupport::Testing::Parallelization.run_cleanup_hook do |worker|
130
+ yield worker
131
+ end
132
+ end
42
133
  end
43
134
 
44
135
  alias_method :method_name, :name
@@ -113,11 +113,23 @@ module ActiveSupport
113
113
  # post :create, params: { article: invalid_attributes }
114
114
  # end
115
115
  #
116
+ # A lambda can be passed in and evaluated.
117
+ #
118
+ # assert_no_difference -> { Article.count } do
119
+ # post :create, params: { article: invalid_attributes }
120
+ # end
121
+ #
116
122
  # An error message can be specified.
117
123
  #
118
124
  # assert_no_difference 'Article.count', 'An Article should not be created' do
119
125
  # post :create, params: { article: invalid_attributes }
120
126
  # end
127
+ #
128
+ # An array of expressions can also be passed in and evaluated.
129
+ #
130
+ # assert_no_difference [ 'Article.count', -> { Post.count } ] do
131
+ # post :create, params: { article: invalid_attributes }
132
+ # end
121
133
  def assert_no_difference(expression, message = nil, &block)
122
134
  assert_difference expression, 0, message, &block
123
135
  end
@@ -176,7 +188,9 @@ module ActiveSupport
176
188
  assert before != after, error
177
189
 
178
190
  unless to == UNTRACKED
179
- error = "#{expression.inspect} didn't change to #{to}"
191
+ error = "#{expression.inspect} didn't change to as expected\n"
192
+ error = "#{error}Expected: #{to.inspect}\n"
193
+ error = "#{error} Actual: #{after.inspect}"
180
194
  error = "#{message}.\n#{error}" if message
181
195
  assert to === after, error
182
196
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/deprecation"
4
- require "active_support/core_ext/regexp"
5
4
 
6
5
  module ActiveSupport
7
6
  module Testing
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/concern"
4
+
3
5
  module ActiveSupport
4
6
  module Testing
5
7
  # Adds simple access to sample files called file fixtures.
@@ -56,7 +56,7 @@ module ActiveSupport
56
56
  write.close
57
57
  result = read.read
58
58
  Process.wait2(pid)
59
- result.unpack("m")[0]
59
+ result.unpack1("m")
60
60
  end
61
61
  end
62
62
 
@@ -98,7 +98,7 @@ module ActiveSupport
98
98
  nil
99
99
  end
100
100
 
101
- return tmpfile.read.unpack("m")[0]
101
+ return tmpfile.read.unpack1("m")
102
102
  end
103
103
  end
104
104
  end
@@ -17,7 +17,7 @@ module ActiveSupport
17
17
  assert_equal times, times_called, error
18
18
  end
19
19
 
20
- def assert_called_with(object, method_name, args = [], returns: nil)
20
+ def assert_called_with(object, method_name, args, returns: nil)
21
21
  mock = Minitest::Mock.new
22
22
 
23
23
  if args.all? { |arg| arg.is_a?(Array) }
@@ -35,6 +35,33 @@ module ActiveSupport
35
35
  assert_called(object, method_name, message, times: 0, &block)
36
36
  end
37
37
 
38
+ def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
39
+ times_called = 0
40
+ klass.define_method("stubbed_#{method_name}") do |*|
41
+ times_called += 1
42
+
43
+ returns
44
+ end
45
+
46
+ klass.alias_method "original_#{method_name}", method_name
47
+ klass.alias_method method_name, "stubbed_#{method_name}"
48
+
49
+ yield
50
+
51
+ error = "Expected #{method_name} to be called #{times} times, but was called #{times_called} times"
52
+ error = "#{message}.\n#{error}" if message
53
+
54
+ assert_equal times, times_called, error
55
+ ensure
56
+ klass.alias_method method_name, "original_#{method_name}"
57
+ klass.undef_method "original_#{method_name}"
58
+ klass.undef_method "stubbed_#{method_name}"
59
+ end
60
+
61
+ def assert_not_called_on_instance_of(klass, method_name, message = nil, &block)
62
+ assert_called_on_instance_of(klass, method_name, message, times: 0, &block)
63
+ end
64
+
38
65
  def stub_any_instance(klass, instance: klass.new)
39
66
  klass.stub(:new, instance) { yield instance }
40
67
  end
@@ -0,0 +1,109 @@
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
+
7
+ module ActiveSupport
8
+ module Testing
9
+ class Parallelization # :nodoc:
10
+ class Server
11
+ include DRb::DRbUndumped
12
+
13
+ def initialize
14
+ @queue = Queue.new
15
+ end
16
+
17
+ def record(reporter, result)
18
+ raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown)
19
+
20
+ reporter.synchronize do
21
+ reporter.record(result)
22
+ end
23
+ end
24
+
25
+ def <<(o)
26
+ o[2] = DRbObject.new(o[2]) if o
27
+ @queue << o
28
+ end
29
+
30
+ def pop; @queue.pop; end
31
+ end
32
+
33
+ @@after_fork_hooks = []
34
+
35
+ def self.after_fork_hook(&blk)
36
+ @@after_fork_hooks << blk
37
+ end
38
+
39
+ cattr_reader :after_fork_hooks
40
+
41
+ @@run_cleanup_hooks = []
42
+
43
+ def self.run_cleanup_hook(&blk)
44
+ @@run_cleanup_hooks << blk
45
+ end
46
+
47
+ cattr_reader :run_cleanup_hooks
48
+
49
+ def initialize(queue_size)
50
+ @queue_size = queue_size
51
+ @queue = Server.new
52
+ @pool = []
53
+
54
+ @url = DRb.start_service("drbunix:", @queue).uri
55
+ end
56
+
57
+ def after_fork(worker)
58
+ self.class.after_fork_hooks.each do |cb|
59
+ cb.call(worker)
60
+ end
61
+ end
62
+
63
+ def run_cleanup(worker)
64
+ self.class.run_cleanup_hooks.each do |cb|
65
+ cb.call(worker)
66
+ end
67
+ end
68
+
69
+ def start
70
+ @pool = @queue_size.times.map do |worker|
71
+ fork do
72
+ DRb.stop_service
73
+
74
+ after_fork(worker)
75
+
76
+ queue = DRbObject.new_with_uri(@url)
77
+
78
+ while job = queue.pop
79
+ klass = job[0]
80
+ method = job[1]
81
+ reporter = job[2]
82
+ result = Minitest.run_one_method(klass, method)
83
+
84
+ begin
85
+ queue.record(reporter, result)
86
+ rescue DRb::DRbConnError
87
+ result.failures.each do |failure|
88
+ failure.exception = DRb::DRbRemoteError.new(failure.exception)
89
+ end
90
+ queue.record(reporter, result)
91
+ end
92
+ end
93
+ ensure
94
+ run_cleanup(worker)
95
+ end
96
+ end
97
+ end
98
+
99
+ def <<(work)
100
+ @queue << work
101
+ end
102
+
103
+ def shutdown
104
+ @queue_size.times { @queue << nil }
105
+ @pool.each { |pid| Process.waitpid pid }
106
+ end
107
+ end
108
+ end
109
+ end
@@ -33,7 +33,7 @@ module ActiveSupport
33
33
  yield
34
34
 
35
35
  stream_io.rewind
36
- return captured_stream.read
36
+ captured_stream.read
37
37
  ensure
38
38
  captured_stream.close
39
39
  captured_stream.unlink
@@ -1,7 +1,6 @@
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
 
@@ -23,7 +22,7 @@ module ActiveSupport
23
22
 
24
23
  @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
25
24
 
26
- object.singleton_class.send :alias_method, new_name, method_name
25
+ object.singleton_class.alias_method new_name, method_name
27
26
  object.define_singleton_method(method_name, &block)
28
27
  end
29
28
 
@@ -44,9 +43,9 @@ module ActiveSupport
44
43
 
45
44
  def unstub_object(stub)
46
45
  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
46
+ singleton_class.silence_redefinition_of_method stub.method_name
47
+ singleton_class.alias_method stub.method_name, stub.original_method
48
+ singleton_class.undef_method stub.original_method
50
49
  end
51
50
  end
52
51
 
@@ -112,7 +111,7 @@ module ActiveSupport
112
111
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
113
112
  def travel_to(date_or_time)
114
113
  if block_given? && simple_stubs.stubbing(Time, :now)
115
- travel_to_nested_block_call = <<-MSG.strip_heredoc
114
+ travel_to_nested_block_call = <<~MSG
116
115
 
117
116
  Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
118
117
 
@@ -159,7 +158,7 @@ module ActiveSupport
159
158
  end
160
159
 
161
160
  # Returns the current time back to its original state, by removing the stubs added by
162
- # +travel+ and +travel_to+.
161
+ # +travel+, +travel_to+, and +freeze_time+.
163
162
  #
164
163
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
165
164
  # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44)
@@ -169,6 +168,7 @@ module ActiveSupport
169
168
  def travel_back
170
169
  simple_stubs.unstub_all!
171
170
  end
171
+ alias_method :unfreeze_time, :travel_back
172
172
 
173
173
  # Calls +travel_to+ with +Time.now+.
174
174
  #
@@ -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
@@ -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.
@@ -284,8 +286,10 @@ module ActiveSupport
284
286
  alias_method :since, :+
285
287
  alias_method :in, :+
286
288
 
287
- # Returns a new TimeWithZone object that represents the difference between
288
- # the current object's time and the +other+ time.
289
+ # Subtracts an interval of time and returns a new TimeWithZone object unless
290
+ # the other value `acts_like?` time. Then it will return a Float of the difference
291
+ # between the two times that represents the difference between the current
292
+ # object's time and the +other+ time.
289
293
  #
290
294
  # Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
291
295
  # now = Time.zone.now # => Mon, 03 Nov 2014 00:26:28 EST -05:00
@@ -300,6 +304,12 @@ module ActiveSupport
300
304
  #
301
305
  # now - 24.hours # => Sun, 02 Nov 2014 01:26:28 EDT -04:00
302
306
  # now - 1.day # => Sun, 02 Nov 2014 00:26:28 EDT -04:00
307
+ #
308
+ # If both the TimeWithZone object and the other value act like Time, a Float
309
+ # will be returned.
310
+ #
311
+ # Time.zone.now - 1.day.ago # => 86399.999967
312
+ #
303
313
  def -(other)
304
314
  if other.acts_like?(:time)
305
315
  to_time - other.to_time
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "tzinfo"
4
4
  require "concurrent/map"
5
- require "active_support/core_ext/object/blank"
6
5
 
7
6
  module ActiveSupport
8
7
  # The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
@@ -183,8 +182,9 @@ module ActiveSupport
183
182
  "Samoa" => "Pacific/Apia"
184
183
  }
185
184
 
186
- UTC_OFFSET_WITH_COLON = "%s%02d:%02d"
187
- UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "")
185
+ UTC_OFFSET_WITH_COLON = "%s%02d:%02d" # :nodoc:
186
+ UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.tr(":", "") # :nodoc:
187
+ private_constant :UTC_OFFSET_WITH_COLON, :UTC_OFFSET_WITHOUT_COLON
188
188
 
189
189
  @lazy_zones_map = Concurrent::Map.new
190
190
  @country_zones = Concurrent::Map.new
@@ -266,7 +266,7 @@ module ActiveSupport
266
266
  private
267
267
  def load_country_zones(code)
268
268
  country = TZInfo::Country.get(code)
269
- country.zone_identifiers.map do |tz_id|
269
+ country.zone_identifiers.flat_map do |tz_id|
270
270
  if MAPPING.value?(tz_id)
271
271
  MAPPING.inject([]) do |memo, (key, value)|
272
272
  memo << self[key] if value == tz_id
@@ -275,7 +275,7 @@ module ActiveSupport
275
275
  else
276
276
  create(tz_id, nil, TZInfo::Timezone.new(tz_id))
277
277
  end
278
- end.flatten(1).sort!
278
+ end.sort!
279
279
  end
280
280
 
281
281
  def zones_map
@@ -355,8 +355,13 @@ module ActiveSupport
355
355
  # Time.zone = 'Hawaii' # => "Hawaii"
356
356
  # Time.utc(2000).to_f # => 946684800.0
357
357
  # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
358
- def at(secs)
359
- Time.at(secs).utc.in_time_zone(self)
358
+ #
359
+ # A second argument can be supplied to specify sub-second precision.
360
+ #
361
+ # Time.zone = 'Hawaii' # => "Hawaii"
362
+ # Time.at(946684800, 123456.789).nsec # => 123456789
363
+ def at(*args)
364
+ Time.at(*args).utc.in_time_zone(self)
360
365
  end
361
366
 
362
367
  # Method for creating new ActiveSupport::TimeWithZone instance in time zone
@@ -18,7 +18,7 @@ module ActiveSupport
18
18
  module XmlMini_JDOM #:nodoc:
19
19
  extend self
20
20
 
21
- CONTENT_KEY = "__content__".freeze
21
+ CONTENT_KEY = "__content__"
22
22
 
23
23
  NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE
24
24
  DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE
@@ -169,7 +169,7 @@ module ActiveSupport
169
169
  # element::
170
170
  # XML element to be checked.
171
171
  def empty_content?(element)
172
- text = "".dup
172
+ text = +""
173
173
  child_nodes = element.child_nodes
174
174
  (0...child_nodes.length).each do |i|
175
175
  item = child_nodes.item(i)
@@ -34,7 +34,7 @@ module LibXML #:nodoc:
34
34
  end
35
35
 
36
36
  module Node #:nodoc:
37
- CONTENT_ROOT = "__content__".freeze
37
+ CONTENT_ROOT = "__content__"
38
38
 
39
39
  # Convert XML document to hash.
40
40
  #
@@ -55,7 +55,7 @@ module LibXML #:nodoc:
55
55
  if c.element?
56
56
  c.to_hash(node_hash)
57
57
  elsif c.text? || c.cdata?
58
- node_hash[CONTENT_ROOT] ||= "".dup
58
+ node_hash[CONTENT_ROOT] ||= +""
59
59
  node_hash[CONTENT_ROOT] << c.content
60
60
  end
61
61
  end
@@ -13,8 +13,8 @@ module ActiveSupport
13
13
  class HashBuilder
14
14
  include LibXML::XML::SaxParser::Callbacks
15
15
 
16
- CONTENT_KEY = "__content__".freeze
17
- HASH_SIZE_KEY = "__hash_size__".freeze
16
+ CONTENT_KEY = "__content__"
17
+ HASH_SIZE_KEY = "__hash_size__"
18
18
 
19
19
  attr_reader :hash
20
20
 
@@ -23,7 +23,7 @@ module ActiveSupport
23
23
  end
24
24
 
25
25
  def on_start_document
26
- @hash = { CONTENT_KEY => "".dup }
26
+ @hash = { CONTENT_KEY => +"" }
27
27
  @hash_stack = [@hash]
28
28
  end
29
29
 
@@ -33,7 +33,7 @@ module ActiveSupport
33
33
  end
34
34
 
35
35
  def on_start_element(name, attrs = {})
36
- new_hash = { CONTENT_KEY => "".dup }.merge!(attrs)
36
+ new_hash = { CONTENT_KEY => +"" }.merge!(attrs)
37
37
  new_hash[HASH_SIZE_KEY] = new_hash.size + 1
38
38
 
39
39
  case current_hash[name]
@@ -38,7 +38,7 @@ module ActiveSupport
38
38
  end
39
39
 
40
40
  module Node #:nodoc:
41
- CONTENT_ROOT = "__content__".freeze
41
+ CONTENT_ROOT = "__content__"
42
42
 
43
43
  # Convert XML document to hash.
44
44
  #
@@ -59,7 +59,7 @@ module ActiveSupport
59
59
  if c.element?
60
60
  c.to_hash(node_hash)
61
61
  elsif c.text? || c.cdata?
62
- node_hash[CONTENT_ROOT] ||= "".dup
62
+ node_hash[CONTENT_ROOT] ||= +""
63
63
  node_hash[CONTENT_ROOT] << c.content
64
64
  end
65
65
  end
@@ -16,8 +16,8 @@ module ActiveSupport
16
16
  # Class that will build the hash while the XML document
17
17
  # is being parsed using SAX events.
18
18
  class HashBuilder < Nokogiri::XML::SAX::Document
19
- CONTENT_KEY = "__content__".freeze
20
- HASH_SIZE_KEY = "__hash_size__".freeze
19
+ CONTENT_KEY = "__content__"
20
+ HASH_SIZE_KEY = "__hash_size__"
21
21
 
22
22
  attr_reader :hash
23
23
 
@@ -39,7 +39,7 @@ module ActiveSupport
39
39
  end
40
40
 
41
41
  def start_element(name, attrs = [])
42
- new_hash = { CONTENT_KEY => "".dup }.merge!(Hash[attrs])
42
+ new_hash = { CONTENT_KEY => +"" }.merge!(Hash[attrs])
43
43
  new_hash[HASH_SIZE_KEY] = new_hash.size + 1
44
44
 
45
45
  case current_hash[name]
@@ -8,7 +8,7 @@ module ActiveSupport
8
8
  module XmlMini_REXML #:nodoc:
9
9
  extend self
10
10
 
11
- CONTENT_KEY = "__content__".freeze
11
+ CONTENT_KEY = "__content__"
12
12
 
13
13
  # Parse an XML Document string or IO into a simple hash.
14
14
  #
@@ -76,7 +76,7 @@ module ActiveSupport
76
76
  hash
77
77
  else
78
78
  # must use value to prevent double-escaping
79
- texts = "".dup
79
+ texts = +""
80
80
  element.texts.each { |t| texts << t.value }
81
81
  merge!(hash, CONTENT_KEY, texts)
82
82
  end
@@ -3,6 +3,7 @@
3
3
  require "time"
4
4
  require "base64"
5
5
  require "bigdecimal"
6
+ require "bigdecimal/util"
6
7
  require "active_support/core_ext/module/delegation"
7
8
  require "active_support/core_ext/string/inflections"
8
9
  require "active_support/core_ext/date_time/calculations"
@@ -48,10 +49,6 @@ module ActiveSupport
48
49
  "Array" => "array",
49
50
  "Hash" => "hash"
50
51
  }
51
-
52
- # No need to map these on Ruby 2.4+
53
- TYPE_NAMES["Fixnum"] = "integer" unless 0.class == Integer
54
- TYPE_NAMES["Bignum"] = "integer" unless 0.class == Integer
55
52
  end
56
53
 
57
54
  FORMATTING = {
@@ -72,11 +69,7 @@ module ActiveSupport
72
69
  "float" => Proc.new { |float| float.to_f },
73
70
  "decimal" => Proc.new do |number|
74
71
  if String === number
75
- begin
76
- BigDecimal(number)
77
- rescue ArgumentError
78
- BigDecimal(number.to_f.to_s)
79
- end
72
+ number.to_d
80
73
  else
81
74
  BigDecimal(number)
82
75
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2005-2018 David Heinemeier Hansson
4
+ # Copyright (c) 2005-2019 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the