aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md34
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb1
-rw-r--r--activesupport/lib/active_support/duration/iso8601_serializer.rb4
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb44
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb51
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb1
-rw-r--r--activesupport/test/caching_test.rb2
-rw-r--r--activesupport/test/core_ext/duration_test.rb1
-rw-r--r--activesupport/test/load_paths_test.rb16
-rw-r--r--activesupport/test/multibyte_conformance_test.rb20
-rw-r--r--activesupport/test/multibyte_grapheme_break_conformance_test.rb32
-rw-r--r--activesupport/test/multibyte_normalization_conformance_test.rb27
-rw-r--r--activesupport/test/multibyte_test_helpers.rb22
-rw-r--r--activesupport/test/testing/file_fixtures_test.rb2
-rw-r--r--activesupport/test/time_travel_test.rb43
-rw-r--r--activesupport/test/time_zone_test.rb7
17 files changed, 200 insertions, 109 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index db439b5732..a8d875640e 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,37 @@
+* Fix `ActiveSupport::TimeZone#strptime`. Now raises `ArgumentError` when the
+ given time doesn't match the format. The error is the same as the one given
+ by Ruby's `Date.strptime`. Previously it raised
+ `NoMethodError: undefined method empty? for nil:NilClass.` due to a bug.
+
+ Fixes #25701.
+
+ *John Gesimondo*
+
+* `travel/travel_to` travel time helpers, now raise on nested calls,
+ as this can lead to confusing time stubbing.
+
+ Instead of:
+
+ travel_to 2.days.from_now do
+ # 2 days from today
+ travel_to 3.days.from_now do
+ # 5 days from today
+ end
+ end
+
+ preferred way to achieve above is:
+
+ travel 2.days do
+ # 2 days from today
+ end
+
+ travel 5.days do
+ # 5 days from today
+ end
+
+ *Vipul A M*
+
+
* Support parsing JSON time in ISO8601 local time strings in
`ActiveSupport::JSON.decode` when `parse_json_times` is enabled.
Strings in the format of `YYYY-MM-DD hh:mm:ss` (without a `Z` at
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index 802d988af2..aa0e2a1a88 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -27,7 +27,7 @@ class Class
# This matches normal Ruby method inheritance: think of writing an attribute
# on a subclass as overriding the reader method. However, you need to be aware
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
- # In such cases, you don't want to do changes in places but use setters:
+ # In such cases, you don't want to do changes in place. Instead use setters:
#
# Base.setting = []
# Base.setting # => []
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 005ad93b08..f1a0c47c54 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -1,5 +1,6 @@
require 'erb'
require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/multibyte/unicode'
class ERB
module Util
diff --git a/activesupport/lib/active_support/duration/iso8601_serializer.rb b/activesupport/lib/active_support/duration/iso8601_serializer.rb
index 05c6a083a9..eb8136e208 100644
--- a/activesupport/lib/active_support/duration/iso8601_serializer.rb
+++ b/activesupport/lib/active_support/duration/iso8601_serializer.rb
@@ -12,8 +12,10 @@ module ActiveSupport
# Builds and returns output string.
def serialize
- output = 'P'
parts, sign = normalize
+ return "PT0S".freeze if parts.empty?
+
+ output = 'P'
output << "#{parts[:years]}Y" if parts.key?(:years)
output << "#{parts[:months]}M" if parts.key?(:months)
output << "#{parts[:weeks]}W" if parts.key?(:weeks)
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index e2b8f0f648..67b54b45ea 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -20,29 +20,37 @@ module ActiveSupport
# +activerecord/lib/active_record/base.rb+ is:
#
# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
- @load_hooks = Hash.new { |h,k| h[k] = [] }
- @loaded = Hash.new { |h,k| h[k] = [] }
-
- def self.on_load(name, options = {}, &block)
- @loaded[name].each do |base|
- execute_hook(base, options, block)
+ module LazyLoadHooks
+ def self.extended(base) # :nodoc:
+ base.class_eval do
+ @load_hooks = Hash.new { |h,k| h[k] = [] }
+ @loaded = Hash.new { |h,k| h[k] = [] }
+ end
end
- @load_hooks[name] << [block, options]
- end
+ def on_load(name, options = {}, &block)
+ @loaded[name].each do |base|
+ execute_hook(base, options, block)
+ end
- def self.execute_hook(base, options, block)
- if options[:yield]
- block.call(base)
- else
- base.instance_eval(&block)
+ @load_hooks[name] << [block, options]
end
- end
- def self.run_load_hooks(name, base = Object)
- @loaded[name] << base
- @load_hooks[name].each do |hook, options|
- execute_hook(base, options, hook)
+ def execute_hook(base, options, block)
+ if options[:yield]
+ block.call(base)
+ else
+ base.instance_eval(&block)
+ end
+ end
+
+ def run_load_hooks(name, base = Object)
+ @loaded[name] << base
+ @load_hooks[name].each do |hook, options|
+ execute_hook(base, options, hook)
+ end
end
end
+
+ extend LazyLoadHooks
end
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index ef27ce8eb5..41dff281ef 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -1,32 +1,39 @@
+require 'active_support/core_ext/string/strip' # for strip_heredoc
+require 'concurrent/map'
+
module ActiveSupport
module Testing
class SimpleStubs # :nodoc:
Stub = Struct.new(:object, :method_name, :original_method)
def initialize
- @stubs = {}
+ @stubs = Concurrent::Map.new { |h, k| h[k] = {} }
end
def stub_object(object, method_name, return_value)
- key = [object.object_id, method_name]
-
- if stub = @stubs[key]
+ if stub = stubbing(object, method_name)
unstub_object(stub)
end
new_name = "__simple_stub__#{method_name}"
- @stubs[key] = Stub.new(object, method_name, new_name)
+ @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
object.singleton_class.send :alias_method, new_name, method_name
object.define_singleton_method(method_name) { return_value }
end
def unstub_all!
- @stubs.each_value do |stub|
- unstub_object(stub)
+ @stubs.each_value do |object_stubs|
+ object_stubs.each_value do |stub|
+ unstub_object(stub)
+ end
end
- @stubs = {}
+ @stubs.clear
+ end
+
+ def stubbing(object, method_name)
+ @stubs[object.object_id][method_name]
end
private
@@ -93,6 +100,34 @@ module ActiveSupport
# end
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
def travel_to(date_or_time)
+ if block_given? && simple_stubs.stubbing(Time, :now)
+ travel_to_nested_block_call = <<-MSG.strip_heredoc
+
+ Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
+
+ Instead of:
+
+ travel_to 2.days.from_now do
+ # 2 days from today
+ travel_to 3.days.from_now do
+ # 5 days from today
+ end
+ end
+
+ preferred way to achieve above is:
+
+ travel 2.days do
+ # 2 days from today
+ end
+
+ travel 5.days do
+ # 5 days from today
+ end
+
+ MSG
+ raise travel_to_nested_block_call
+ end
+
if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
now = date_or_time.midnight.to_time
else
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 19420cee5e..eb89a6d4c5 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -447,6 +447,7 @@ module ActiveSupport
private
def parts_to_time(parts, now)
+ raise ArgumentError, "invalid date" if parts.nil?
return if parts.empty?
time = Time.new(
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index ec7d028d7e..8002c14539 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -3,6 +3,8 @@ require 'abstract_unit'
require 'active_support/cache'
require 'dependencies_test_helpers'
+require 'pathname'
+
module ActiveSupport
module Cache
module Strategy
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 502e2811fa..a24915dfef 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -279,6 +279,7 @@ class DurationTest < ActiveSupport::TestCase
['PT1S', 1.second ],
['PT1.4S', (1.4).seconds ],
['P1Y1M1DT1H', 1.year + 1.month + 1.day + 1.hour],
+ ['PT0S', 0.minutes ],
]
expectations.each do |expected_output, duration|
assert_equal expected_output, duration.iso8601, expected_output.inspect
diff --git a/activesupport/test/load_paths_test.rb b/activesupport/test/load_paths_test.rb
deleted file mode 100644
index ac617a9fd8..0000000000
--- a/activesupport/test/load_paths_test.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require 'abstract_unit'
-
-class LoadPathsTest < ActiveSupport::TestCase
- def test_uniq_load_paths
- load_paths_count = $LOAD_PATH.inject({}) { |paths, path|
- expanded_path = File.expand_path(path)
- paths[expanded_path] ||= 0
- paths[expanded_path] += 1
- paths
- }
- load_paths_count[File.expand_path('../../lib', __FILE__)] -= 1
-
- load_paths_count.select! { |k, v| v > 1 }
- assert load_paths_count.empty?, load_paths_count.inspect
- end
-end
diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb
index 9fca47a985..c10133a7a6 100644
--- a/activesupport/test/multibyte_conformance_test.rb
+++ b/activesupport/test/multibyte_conformance_test.rb
@@ -6,29 +6,9 @@ require 'open-uri'
require 'tmpdir'
class MultibyteConformanceTest < ActiveSupport::TestCase
- class Downloader
- def self.download(from, to)
- unless File.exist?(to)
- unless File.exist?(File.dirname(to))
- system "mkdir -p #{File.dirname(to)}"
- end
- open(from) do |source|
- File.open(to, 'w') do |target|
- source.each_line do |l|
- target.write l
- end
- end
- end
- end
- true
- end
- end
-
include MultibyteTestHelpers
- UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd"
UNIDATA_FILE = '/NormalizationTest.txt'
- CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance"
FileUtils.mkdir_p(CACHE_DIR)
RUN_P = begin
Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE)
diff --git a/activesupport/test/multibyte_grapheme_break_conformance_test.rb b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
index 6e2f02abed..61943b1ab3 100644
--- a/activesupport/test/multibyte_grapheme_break_conformance_test.rb
+++ b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
@@ -1,37 +1,23 @@
# encoding: utf-8
require 'abstract_unit'
+require 'multibyte_test_helpers'
require 'fileutils'
require 'open-uri'
require 'tmpdir'
class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
- class Downloader
- def self.download(from, to)
- unless File.exist?(to)
- $stderr.puts "Downloading #{from} to #{to}"
- unless File.exist?(File.dirname(to))
- system "mkdir -p #{File.dirname(to)}"
- end
- open(from) do |source|
- File.open(to, 'w') do |target|
- source.each_line do |l|
- target.write l
- end
- end
- end
- end
- end
- end
+ include MultibyteTestHelpers
- TEST_DATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd/auxiliary"
- TEST_DATA_FILE = '/GraphemeBreakTest.txt'
- CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance"
+ UNIDATA_FILE = '/auxiliary/GraphemeBreakTest.txt'
+ RUN_P = begin
+ Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE)
+ rescue
+ end
def setup
- FileUtils.mkdir_p(CACHE_DIR)
- Downloader.download(TEST_DATA_URL + TEST_DATA_FILE, CACHE_DIR + TEST_DATA_FILE)
+ skip "Unable to download test data" unless RUN_P
end
def test_breaks
@@ -46,7 +32,7 @@ class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
def each_line_of_break_tests(&block)
lines = 0
max_test_lines = 0 # Don't limit below 21, because that's the header of the testfile
- File.open(File.join(CACHE_DIR, TEST_DATA_FILE), 'r') do | f |
+ File.open(File.join(CACHE_DIR, UNIDATA_FILE), 'r') do | f |
until f.eof? || (max_test_lines > 21 and lines > max_test_lines)
lines += 1
line = f.gets.chomp!
diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb
index 0d31c9520f..77ed0ce6de 100644
--- a/activesupport/test/multibyte_normalization_conformance_test.rb
+++ b/activesupport/test/multibyte_normalization_conformance_test.rb
@@ -8,34 +8,17 @@ require 'open-uri'
require 'tmpdir'
class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase
- class Downloader
- def self.download(from, to)
- unless File.exist?(to)
- $stderr.puts "Downloading #{from} to #{to}"
- unless File.exist?(File.dirname(to))
- system "mkdir -p #{File.dirname(to)}"
- end
- open(from) do |source|
- File.open(to, 'w') do |target|
- source.each_line do |l|
- target.write l
- end
- end
- end
- end
- end
- end
-
include MultibyteTestHelpers
- UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd"
UNIDATA_FILE = '/NormalizationTest.txt'
- CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance"
+ RUN_P = begin
+ Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE)
+ rescue
+ end
def setup
- FileUtils.mkdir_p(CACHE_DIR)
- Downloader.download(UNIDATA_URL + UNIDATA_FILE, CACHE_DIR + UNIDATA_FILE)
@proxy = ActiveSupport::Multibyte::Chars
+ skip "Unable to download test data" unless RUN_P
end
def test_normalizations_C
diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb
index 58cf5488cd..0ada8bc032 100644
--- a/activesupport/test/multibyte_test_helpers.rb
+++ b/activesupport/test/multibyte_test_helpers.rb
@@ -1,4 +1,26 @@
module MultibyteTestHelpers
+ class Downloader
+ def self.download(from, to)
+ unless File.exist?(to)
+ unless File.exist?(File.dirname(to))
+ system "mkdir -p #{File.dirname(to)}"
+ end
+ open(from) do |source|
+ File.open(to, 'w') do |target|
+ source.each_line do |l|
+ target.write l
+ end
+ end
+ end
+ end
+ true
+ end
+ end
+
+ UNIDATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd"
+ CACHE_DIR = "#{Dir.tmpdir}/cache/unicode_conformance"
+ FileUtils.mkdir_p(CACHE_DIR)
+
UNICODE_STRING = 'こにちわ'.freeze
ASCII_STRING = 'ohayo'.freeze
BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT").freeze
diff --git a/activesupport/test/testing/file_fixtures_test.rb b/activesupport/test/testing/file_fixtures_test.rb
index 3587c1a4d1..d98987fb8d 100644
--- a/activesupport/test/testing/file_fixtures_test.rb
+++ b/activesupport/test/testing/file_fixtures_test.rb
@@ -1,5 +1,7 @@
require 'abstract_unit'
+require 'pathname'
+
class FileFixturesTest < ActiveSupport::TestCase
self.file_fixture_path = File.expand_path("../../file_fixtures", __FILE__)
diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb
index 59c3e52c2f..e9f87bdf82 100644
--- a/activesupport/test/time_travel_test.rb
+++ b/activesupport/test/time_travel_test.rb
@@ -77,6 +77,49 @@ class TimeTravelTest < ActiveSupport::TestCase
end
end
+ def test_time_helper_travel_to_with_nested_calls_with_blocks
+ Time.stub(:now, Time.now) do
+ outer_expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ inner_expected_time = Time.new(2004, 10, 24, 01, 04, 44)
+ travel_to outer_expected_time do
+ assert_raises(RuntimeError, /Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing./) do
+ travel_to(inner_expected_time) do
+ #noop
+ end
+ end
+ end
+ end
+ end
+
+ def test_time_helper_travel_to_with_nested_calls
+ Time.stub(:now, Time.now) do
+ outer_expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ inner_expected_time = Time.new(2004, 10, 24, 01, 04, 44)
+ travel_to outer_expected_time do
+ assert_nothing_raised do
+ travel_to(inner_expected_time)
+
+ assert_equal inner_expected_time, Time.now
+ end
+ end
+ end
+ end
+
+ def test_time_helper_travel_to_with_subsequent_calls
+ Time.stub(:now, Time.now) do
+ initial_expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ subsequent_expected_time = Time.new(2004, 10, 24, 01, 04, 44)
+ assert_nothing_raised do
+ travel_to initial_expected_time
+ travel_to subsequent_expected_time
+
+ assert_equal subsequent_expected_time, Time.now
+
+ travel_back
+ end
+ end
+ end
+
def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding
Time.stub(:now, Time.now) do
travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index a15d5c6a0e..76cbb5ce8b 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -388,6 +388,13 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
+ def test_strptime_with_malformed_string
+ with_env_tz 'US/Eastern' do
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ assert_raise(ArgumentError) { zone.strptime('1999-12-31', '%Y/%m/%d') }
+ end
+ end
+
def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize
tzinfo = TZInfo::Timezone.get('America/New_York')
zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo)