activesupport 4.0.13 → 4.2.11.3
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +406 -418
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -2
- data/lib/active_support/backtrace_cleaner.rb +8 -8
- data/lib/active_support/benchmarkable.rb +0 -10
- data/lib/active_support/cache/file_store.rb +32 -22
- data/lib/active_support/cache/mem_cache_store.rb +5 -7
- data/lib/active_support/cache/memory_store.rb +1 -0
- data/lib/active_support/cache/strategy/local_cache.rb +11 -30
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
- data/lib/active_support/cache.rb +75 -41
- data/lib/active_support/callbacks.rb +482 -261
- data/lib/active_support/concern.rb +23 -7
- data/lib/active_support/configurable.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +11 -1
- data/lib/active_support/core_ext/array/conversions.rb +2 -17
- data/lib/active_support/core_ext/array/grouping.rb +29 -12
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +16 -0
- data/lib/active_support/core_ext/class/attribute.rb +1 -2
- data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -170
- data/lib/active_support/core_ext/class/delegating_attributes.rb +13 -8
- data/lib/active_support/core_ext/class/subclasses.rb +0 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/date/calculations.rb +10 -0
- data/lib/active_support/core_ext/date/conversions.rb +9 -1
- data/lib/active_support/core_ext/date/zones.rb +2 -33
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -11
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +45 -22
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
- data/lib/active_support/core_ext/date_time/zones.rb +3 -21
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +51 -0
- data/lib/active_support/core_ext/enumerable.rb +17 -1
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/compact.rb +24 -0
- data/lib/active_support/core_ext/hash/conversions.rb +9 -8
- data/lib/active_support/core_ext/hash/except.rb +8 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
- data/lib/active_support/core_ext/hash/keys.rb +25 -19
- data/lib/active_support/core_ext/hash/slice.rb +8 -2
- data/lib/active_support/core_ext/hash/transform_values.rb +23 -0
- data/lib/active_support/core_ext/hash.rb +2 -1
- data/lib/active_support/core_ext/integer/time.rb +0 -15
- data/lib/active_support/core_ext/kernel/concern.rb +10 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +13 -2
- data/lib/active_support/core_ext/kernel.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +4 -1
- data/lib/active_support/core_ext/marshal.rb +8 -5
- data/lib/active_support/core_ext/module/aliasing.rb +2 -2
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
- data/lib/active_support/core_ext/module/concerning.rb +135 -0
- data/lib/active_support/core_ext/module/delegation.rb +53 -25
- data/lib/active_support/core_ext/module/deprecation.rb +0 -2
- data/lib/active_support/core_ext/module/introspection.rb +0 -16
- data/lib/active_support/core_ext/module/method_transplanting.rb +13 -0
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +11 -3
- data/lib/active_support/core_ext/numeric/time.rb +4 -29
- data/lib/active_support/core_ext/object/blank.rb +44 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
- data/lib/active_support/core_ext/object/duplicable.rb +72 -33
- data/lib/active_support/core_ext/object/inclusion.rb +16 -15
- data/lib/active_support/core_ext/object/itself.rb +15 -0
- data/lib/active_support/core_ext/object/json.rb +197 -0
- data/lib/active_support/core_ext/object/to_query.rb +14 -6
- data/lib/active_support/core_ext/object/try.rb +36 -14
- data/lib/active_support/core_ext/object/with_options.rb +30 -3
- data/lib/active_support/core_ext/object.rb +2 -1
- data/lib/active_support/core_ext/string/access.rb +35 -35
- data/lib/active_support/core_ext/string/conversions.rb +10 -9
- data/lib/active_support/core_ext/string/exclude.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +51 -3
- data/lib/active_support/core_ext/string/inflections.rb +15 -10
- data/lib/active_support/core_ext/string/output_safety.rb +97 -33
- data/lib/active_support/core_ext/string/zones.rb +1 -0
- data/lib/active_support/core_ext/thread.rb +12 -5
- data/lib/active_support/core_ext/time/calculations.rb +47 -68
- data/lib/active_support/core_ext/time/compatibility.rb +14 -0
- data/lib/active_support/core_ext/time/conversions.rb +4 -2
- data/lib/active_support/core_ext/time/zones.rb +2 -20
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/dependencies/autoload.rb +1 -1
- data/lib/active_support/dependencies.rb +64 -25
- data/lib/active_support/deprecation/behaviors.rb +4 -4
- data/lib/active_support/deprecation.rb +4 -4
- data/lib/active_support/duration.rb +55 -11
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/gem_version.rb +15 -0
- data/lib/active_support/hash_with_indifferent_access.rb +39 -11
- data/lib/active_support/i18n.rb +4 -4
- data/lib/active_support/i18n_railtie.rb +1 -7
- data/lib/active_support/inflections.rb +6 -1
- data/lib/active_support/inflector/inflections.rb +19 -19
- data/lib/active_support/inflector/methods.rb +66 -25
- data/lib/active_support/json/decoding.rb +15 -22
- data/lib/active_support/json/encoding.rb +125 -286
- data/lib/active_support/key_generator.rb +8 -10
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber/test_helper.rb +1 -1
- data/lib/active_support/logger.rb +51 -1
- data/lib/active_support/logger_silence.rb +7 -4
- data/lib/active_support/logger_thread_safe_level.rb +32 -0
- data/lib/active_support/message_encryptor.rb +14 -6
- data/lib/active_support/message_verifier.rb +16 -12
- data/lib/active_support/multibyte/chars.rb +2 -3
- data/lib/active_support/multibyte/unicode.rb +46 -58
- data/lib/active_support/notifications/fanout.rb +12 -7
- data/lib/active_support/notifications/instrumenter.rb +2 -1
- data/lib/active_support/notifications.rb +11 -6
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +23 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +87 -0
- data/lib/active_support/number_helper.rb +32 -324
- data/lib/active_support/ordered_options.rb +8 -0
- data/lib/active_support/per_thread_registry.rb +13 -10
- data/lib/active_support/security_utils.rb +27 -0
- data/lib/active_support/subscriber.rb +35 -3
- data/lib/active_support/test_case.rb +52 -19
- data/lib/active_support/testing/assertions.rb +1 -31
- data/lib/active_support/testing/autorun.rb +2 -2
- data/lib/active_support/testing/constant_lookup.rb +1 -5
- data/lib/active_support/testing/declarative.rb +7 -21
- data/lib/active_support/testing/isolation.rb +29 -69
- data/lib/active_support/testing/setup_and_teardown.rb +17 -2
- data/lib/active_support/testing/tagged_logging.rb +2 -2
- data/lib/active_support/testing/time_helpers.rb +134 -0
- data/lib/active_support/time.rb +0 -2
- data/lib/active_support/time_with_zone.rb +60 -40
- data/lib/active_support/values/time_zone.rb +101 -101
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +4 -7
- data/lib/active_support/xml_mini/jdom.rb +6 -5
- data/lib/active_support/xml_mini/libxml.rb +1 -3
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -4
- data/lib/active_support/xml_mini/nokogiri.rb +1 -3
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -3
- data/lib/active_support/xml_mini/rexml.rb +7 -8
- data/lib/active_support/xml_mini.rb +33 -15
- data/lib/active_support.rb +27 -2
- metadata +43 -43
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/object/to_json.rb +0 -27
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
@@ -9,7 +9,7 @@ module ActiveSupport
|
|
9
9
|
#
|
10
10
|
# assert_not nil # => true
|
11
11
|
# assert_not false # => true
|
12
|
-
# assert_not 'foo' # =>
|
12
|
+
# assert_not 'foo' # => Expected "foo" to be nil or false
|
13
13
|
#
|
14
14
|
# An error message can be specified.
|
15
15
|
#
|
@@ -92,36 +92,6 @@ module ActiveSupport
|
|
92
92
|
def assert_no_difference(expression, message = nil, &block)
|
93
93
|
assert_difference expression, 0, message, &block
|
94
94
|
end
|
95
|
-
|
96
|
-
# Test if an expression is blank. Passes if <tt>object.blank?</tt>
|
97
|
-
# is +true+.
|
98
|
-
#
|
99
|
-
# assert_blank [] # => true
|
100
|
-
# assert_blank [[]] # => [[]] is not blank
|
101
|
-
#
|
102
|
-
# An error message can be specified.
|
103
|
-
#
|
104
|
-
# assert_blank [], 'this should be blank'
|
105
|
-
def assert_blank(object, message=nil)
|
106
|
-
ActiveSupport::Deprecation.warn('"assert_blank" is deprecated. Please use "assert object.blank?" instead')
|
107
|
-
message ||= "#{object.inspect} is not blank"
|
108
|
-
assert object.blank?, message
|
109
|
-
end
|
110
|
-
|
111
|
-
# Test if an expression is not blank. Passes if <tt>object.present?</tt>
|
112
|
-
# is +true+.
|
113
|
-
#
|
114
|
-
# assert_present({ data: 'x' }) # => true
|
115
|
-
# assert_present({}) # => {} is blank
|
116
|
-
#
|
117
|
-
# An error message can be specified.
|
118
|
-
#
|
119
|
-
# assert_present({ data: 'x' }, 'this should not be blank')
|
120
|
-
def assert_present(object, message=nil)
|
121
|
-
ActiveSupport::Deprecation.warn('"assert_present" is deprecated. Please use "assert object.present?" instead')
|
122
|
-
message ||= "#{object.inspect} is blank"
|
123
|
-
assert object.present?, message
|
124
|
-
end
|
125
95
|
end
|
126
96
|
end
|
127
97
|
end
|
@@ -36,12 +36,8 @@ module ActiveSupport
|
|
36
36
|
while names.size > 0 do
|
37
37
|
names.last.sub!(/Test$/, "")
|
38
38
|
begin
|
39
|
-
constant = names.join("::").
|
39
|
+
constant = names.join("::").safe_constantize
|
40
40
|
break(constant) if yield(constant)
|
41
|
-
rescue NoMethodError # subclass of NameError
|
42
|
-
raise
|
43
|
-
rescue NameError
|
44
|
-
# Constant wasn't found, move on
|
45
41
|
ensure
|
46
42
|
names.pop
|
47
43
|
end
|
@@ -1,30 +1,16 @@
|
|
1
1
|
module ActiveSupport
|
2
2
|
module Testing
|
3
3
|
module Declarative
|
4
|
-
|
5
|
-
def self.extended(klass) #:nodoc:
|
6
|
-
klass.class_eval do
|
7
|
-
|
8
|
-
unless method_defined?(:describe)
|
9
|
-
def self.describe(text)
|
10
|
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
11
|
-
def self.name
|
12
|
-
"#{text}"
|
13
|
-
end
|
14
|
-
RUBY_EVAL
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
4
|
unless defined?(Spec)
|
22
|
-
# test
|
23
|
-
#
|
24
|
-
#
|
5
|
+
# Helper to define a test method using a String. Under the hood, it replaces
|
6
|
+
# spaces with underscores and defines the test method.
|
7
|
+
#
|
8
|
+
# test "verify something" do
|
9
|
+
# ...
|
10
|
+
# end
|
25
11
|
def test(name, &block)
|
26
12
|
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
27
|
-
defined =
|
13
|
+
defined = method_defined? test_name
|
28
14
|
raise "#{test_name} is already defined in #{self}" if defined
|
29
15
|
if block_given?
|
30
16
|
define_method(test_name, &block)
|
@@ -1,60 +1,14 @@
|
|
1
1
|
require 'rbconfig'
|
2
|
-
require 'minitest/parallel_each'
|
3
2
|
|
4
3
|
module ActiveSupport
|
5
4
|
module Testing
|
6
|
-
class RemoteError < StandardError
|
7
|
-
|
8
|
-
attr_reader :message, :backtrace
|
9
|
-
|
10
|
-
def initialize(exception)
|
11
|
-
@message = "caught #{exception.class.name}: #{exception.message}"
|
12
|
-
@backtrace = exception.backtrace
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class ProxyTestResult
|
17
|
-
def initialize(calls = [])
|
18
|
-
@calls = calls
|
19
|
-
end
|
20
|
-
|
21
|
-
def add_error(e)
|
22
|
-
e = Test::Unit::Error.new(e.test_name, RemoteError.new(e.exception))
|
23
|
-
@calls << [:add_error, e]
|
24
|
-
end
|
25
|
-
|
26
|
-
def __replay__(result)
|
27
|
-
@calls.each do |name, args|
|
28
|
-
result.send(name, *args)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def marshal_dump
|
33
|
-
@calls
|
34
|
-
end
|
35
|
-
|
36
|
-
def marshal_load(calls)
|
37
|
-
initialize(calls)
|
38
|
-
end
|
39
|
-
|
40
|
-
def method_missing(name, *args)
|
41
|
-
@calls << [name, args]
|
42
|
-
end
|
43
|
-
|
44
|
-
def info_signal
|
45
|
-
Signal.list['INFO']
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
5
|
module Isolation
|
50
6
|
require 'thread'
|
51
7
|
|
52
8
|
def self.included(klass) #:nodoc:
|
53
|
-
klass.
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
})
|
9
|
+
klass.class_eval do
|
10
|
+
parallelize_me!
|
11
|
+
end
|
58
12
|
end
|
59
13
|
|
60
14
|
def self.forking_env?
|
@@ -72,27 +26,24 @@ module ActiveSupport
|
|
72
26
|
end
|
73
27
|
end
|
74
28
|
|
75
|
-
def run
|
76
|
-
|
77
|
-
|
78
|
-
serialized = run_in_isolation do |isolated_runner|
|
79
|
-
super(isolated_runner)
|
29
|
+
def run
|
30
|
+
serialized = run_in_isolation do
|
31
|
+
super
|
80
32
|
end
|
81
33
|
|
82
|
-
|
83
|
-
proxy.__replay__(runner)
|
84
|
-
retval
|
34
|
+
Marshal.load(serialized)
|
85
35
|
end
|
86
36
|
|
87
37
|
module Forking
|
88
38
|
def run_in_isolation(&blk)
|
89
39
|
read, write = IO.pipe
|
40
|
+
read.binmode
|
41
|
+
write.binmode
|
90
42
|
|
91
43
|
pid = fork do
|
92
44
|
read.close
|
93
|
-
|
94
|
-
|
95
|
-
write.puts [Marshal.dump([retval, proxy])].pack("m")
|
45
|
+
yield
|
46
|
+
write.puts [Marshal.dump(self.dup)].pack("m")
|
96
47
|
exit!
|
97
48
|
end
|
98
49
|
|
@@ -112,22 +63,31 @@ module ActiveSupport
|
|
112
63
|
require "tempfile"
|
113
64
|
|
114
65
|
if ENV["ISOLATION_TEST"]
|
115
|
-
|
116
|
-
retval = yield proxy
|
66
|
+
yield
|
117
67
|
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
|
118
|
-
file.puts [Marshal.dump(
|
68
|
+
file.puts [Marshal.dump(self.dup)].pack("m")
|
119
69
|
end
|
120
70
|
exit!
|
121
71
|
else
|
122
72
|
Tempfile.open("isolation") do |tmpfile|
|
123
|
-
|
124
|
-
|
73
|
+
env = {
|
74
|
+
ISOLATION_TEST: self.class.name,
|
75
|
+
ISOLATION_OUTPUT: tmpfile.path
|
76
|
+
}
|
125
77
|
|
126
78
|
load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
79
|
+
orig_args = ORIG_ARGV.join(" ")
|
80
|
+
test_opts = "-n#{self.class.name}##{self.name}"
|
81
|
+
command = "#{Gem.ruby} #{load_paths} #{$0} #{orig_args} #{test_opts}"
|
82
|
+
|
83
|
+
# IO.popen lets us pass env in a cross-platform way
|
84
|
+
child = IO.popen([env, command])
|
85
|
+
|
86
|
+
begin
|
87
|
+
Process.wait(child.pid)
|
88
|
+
rescue Errno::ECHILD # The child process may exit before we wait
|
89
|
+
nil
|
90
|
+
end
|
131
91
|
|
132
92
|
return tmpfile.read.unpack("m")[0]
|
133
93
|
end
|
@@ -3,6 +3,19 @@ require 'active_support/callbacks'
|
|
3
3
|
|
4
4
|
module ActiveSupport
|
5
5
|
module Testing
|
6
|
+
# Adds support for +setup+ and +teardown+ callbacks.
|
7
|
+
# These callbacks serve as a replacement to overwriting the
|
8
|
+
# <tt>#setup</tt> and <tt>#teardown</tt> methods of your TestCase.
|
9
|
+
#
|
10
|
+
# class ExampleTest < ActiveSupport::TestCase
|
11
|
+
# setup do
|
12
|
+
# # ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# teardown do
|
16
|
+
# # ...
|
17
|
+
# end
|
18
|
+
# end
|
6
19
|
module SetupAndTeardown
|
7
20
|
extend ActiveSupport::Concern
|
8
21
|
|
@@ -12,21 +25,23 @@ module ActiveSupport
|
|
12
25
|
end
|
13
26
|
|
14
27
|
module ClassMethods
|
28
|
+
# Add a callback, which runs before <tt>TestCase#setup</tt>.
|
15
29
|
def setup(*args, &block)
|
16
30
|
set_callback(:setup, :before, *args, &block)
|
17
31
|
end
|
18
32
|
|
33
|
+
# Add a callback, which runs after <tt>TestCase#teardown</tt>.
|
19
34
|
def teardown(*args, &block)
|
20
35
|
set_callback(:teardown, :after, *args, &block)
|
21
36
|
end
|
22
37
|
end
|
23
38
|
|
24
|
-
def before_setup
|
39
|
+
def before_setup # :nodoc:
|
25
40
|
super
|
26
41
|
run_callbacks :setup
|
27
42
|
end
|
28
43
|
|
29
|
-
def after_teardown
|
44
|
+
def after_teardown # :nodoc:
|
30
45
|
run_callbacks :teardown
|
31
46
|
super
|
32
47
|
end
|
@@ -6,8 +6,8 @@ module ActiveSupport
|
|
6
6
|
attr_writer :tagged_logger
|
7
7
|
|
8
8
|
def before_setup
|
9
|
-
if tagged_logger
|
10
|
-
heading = "#{self.class}: #{
|
9
|
+
if tagged_logger && tagged_logger.info?
|
10
|
+
heading = "#{self.class}: #{name}"
|
11
11
|
divider = '-' * heading.size
|
12
12
|
tagged_logger.info divider
|
13
13
|
tagged_logger.info heading
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
module Testing
|
3
|
+
class SimpleStubs # :nodoc:
|
4
|
+
Stub = Struct.new(:object, :method_name, :original_method)
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@stubs = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def stub_object(object, method_name, &block)
|
11
|
+
key = [object.object_id, method_name]
|
12
|
+
|
13
|
+
if stub = @stubs[key]
|
14
|
+
unstub_object(stub)
|
15
|
+
end
|
16
|
+
|
17
|
+
new_name = "__simple_stub__#{method_name}"
|
18
|
+
|
19
|
+
@stubs[key] = Stub.new(object, method_name, new_name)
|
20
|
+
|
21
|
+
object.singleton_class.send :alias_method, new_name, method_name
|
22
|
+
object.define_singleton_method(method_name, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def unstub_all!
|
26
|
+
@stubs.each_value do |stub|
|
27
|
+
unstub_object(stub)
|
28
|
+
end
|
29
|
+
@stubs = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def unstub_object(stub)
|
35
|
+
singleton_class = stub.object.singleton_class
|
36
|
+
singleton_class.send :undef_method, stub.method_name
|
37
|
+
singleton_class.send :alias_method, stub.method_name, stub.original_method
|
38
|
+
singleton_class.send :undef_method, stub.original_method
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Containing helpers that helps you test passage of time.
|
43
|
+
module TimeHelpers
|
44
|
+
# Changes current time to the time in the future or in the past by a given time difference by
|
45
|
+
# stubbing +Time.now+, +Date.today+, and +DateTime.now+.
|
46
|
+
#
|
47
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
48
|
+
# travel 1.day
|
49
|
+
# Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
|
50
|
+
# Date.current # => Sun, 10 Nov 2013
|
51
|
+
# DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
|
52
|
+
#
|
53
|
+
# This method also accepts a block, which will return the current time back to its original
|
54
|
+
# state at the end of the block:
|
55
|
+
#
|
56
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
57
|
+
# travel 1.day do
|
58
|
+
# User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
|
59
|
+
# end
|
60
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
61
|
+
def travel(duration, &block)
|
62
|
+
travel_to Time.now + duration, &block
|
63
|
+
end
|
64
|
+
|
65
|
+
# Changes current time to the given time by stubbing +Time.now+,
|
66
|
+
# +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
|
67
|
+
#
|
68
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
69
|
+
# travel_to Time.new(2004, 11, 24, 01, 04, 44)
|
70
|
+
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
|
71
|
+
# Date.current # => Wed, 24 Nov 2004
|
72
|
+
# DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
|
73
|
+
#
|
74
|
+
# Dates are taken as their timestamp at the beginning of the day in the
|
75
|
+
# application time zone. <tt>Time.current</tt> returns said timestamp,
|
76
|
+
# and <tt>Time.now</tt> its equivalent in the system time zone. Similarly,
|
77
|
+
# <tt>Date.current</tt> returns a date equal to the argument, and
|
78
|
+
# <tt>Date.today</tt> the date according to <tt>Time.now</tt>, which may
|
79
|
+
# be different. (Note that you rarely want to deal with <tt>Time.now</tt>,
|
80
|
+
# or <tt>Date.today</tt>, in order to honor the application time zone
|
81
|
+
# please always use <tt>Time.current</tt> and <tt>Date.current</tt>.)
|
82
|
+
#
|
83
|
+
# Note that the usec for the time passed will be set to 0 to prevent rounding
|
84
|
+
# errors with external services, like MySQL (which will round instead of floor,
|
85
|
+
# leading to off-by-one-second errors).
|
86
|
+
#
|
87
|
+
# This method also accepts a block, which will return the current time back to its original
|
88
|
+
# state at the end of the block:
|
89
|
+
#
|
90
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
91
|
+
# travel_to Time.new(2004, 11, 24, 01, 04, 44) do
|
92
|
+
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
|
93
|
+
# end
|
94
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
95
|
+
def travel_to(date_or_time)
|
96
|
+
if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
|
97
|
+
now = date_or_time.midnight.to_time
|
98
|
+
else
|
99
|
+
now = date_or_time.to_time.change(usec: 0)
|
100
|
+
end
|
101
|
+
|
102
|
+
simple_stubs.stub_object(Time, :now) { at(now.to_i) }
|
103
|
+
simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
|
104
|
+
simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
|
105
|
+
|
106
|
+
if block_given?
|
107
|
+
begin
|
108
|
+
yield
|
109
|
+
ensure
|
110
|
+
travel_back
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the current time back to its original state, by removing the stubs added by
|
116
|
+
# `travel` and `travel_to`.
|
117
|
+
#
|
118
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
119
|
+
# travel_to Time.new(2004, 11, 24, 01, 04, 44)
|
120
|
+
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
|
121
|
+
# travel_back
|
122
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
123
|
+
def travel_back
|
124
|
+
simple_stubs.unstub_all!
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def simple_stubs
|
130
|
+
@simple_stubs ||= SimpleStubs.new
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/active_support/time.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/values/time_zone'
|
2
2
|
require 'active_support/core_ext/object/acts_like'
|
3
|
+
require 'active_support/core_ext/date_and_time/compatibility'
|
3
4
|
|
4
5
|
module ActiveSupport
|
5
6
|
# A Time-like class that can represent a time in any time zone. Necessary
|
@@ -40,20 +41,21 @@ module ActiveSupport
|
|
40
41
|
'Time'
|
41
42
|
end
|
42
43
|
|
43
|
-
include Comparable
|
44
|
+
include Comparable, DateAndTime::Compatibility
|
44
45
|
attr_reader :time_zone
|
45
46
|
|
46
47
|
def initialize(utc_time, time_zone, local_time = nil, period = nil)
|
47
|
-
@utc
|
48
|
-
@
|
48
|
+
@utc = utc_time ? transfer_time_values_to_utc_constructor(utc_time) : nil
|
49
|
+
@time_zone, @time = time_zone, local_time
|
50
|
+
@period = @utc ? period : get_period_and_ensure_valid_local_time(period)
|
49
51
|
end
|
50
52
|
|
51
|
-
# Returns a Time
|
53
|
+
# Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
|
52
54
|
def time
|
53
55
|
@time ||= period.to_local(@utc)
|
54
56
|
end
|
55
57
|
|
56
|
-
# Returns a Time
|
58
|
+
# Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
|
57
59
|
def utc
|
58
60
|
@utc ||= period.to_utc(@time)
|
59
61
|
end
|
@@ -73,10 +75,9 @@ module ActiveSupport
|
|
73
75
|
utc.in_time_zone(new_zone)
|
74
76
|
end
|
75
77
|
|
76
|
-
# Returns a <tt>Time
|
77
|
-
|
78
|
-
|
79
|
-
utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
|
78
|
+
# Returns a <tt>Time</tt> instance of the simultaneous time in the system timezone.
|
79
|
+
def localtime(utc_offset = nil)
|
80
|
+
utc.getlocal(utc_offset)
|
80
81
|
end
|
81
82
|
alias_method :getlocal, :localtime
|
82
83
|
|
@@ -132,8 +133,8 @@ module ActiveSupport
|
|
132
133
|
end
|
133
134
|
|
134
135
|
def xmlschema(fraction_digits = 0)
|
135
|
-
fraction = if fraction_digits > 0
|
136
|
-
(".%06i" % time.usec)[0, fraction_digits + 1]
|
136
|
+
fraction = if fraction_digits.to_i > 0
|
137
|
+
(".%06i" % time.usec)[0, fraction_digits.to_i + 1]
|
137
138
|
end
|
138
139
|
|
139
140
|
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
|
@@ -146,21 +147,25 @@ module ActiveSupport
|
|
146
147
|
# to +false+.
|
147
148
|
#
|
148
149
|
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
|
149
|
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
|
150
|
-
# # => "2005-02-
|
150
|
+
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
|
151
|
+
# # => "2005-02-01T05:15:10.000-10:00"
|
151
152
|
#
|
152
153
|
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
|
153
|
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
|
154
|
-
# # => "2005/02/01
|
154
|
+
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
|
155
|
+
# # => "2005/02/01 05:15:10 -1000"
|
155
156
|
def as_json(options = nil)
|
156
157
|
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
|
157
|
-
xmlschema(
|
158
|
+
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
|
158
159
|
else
|
159
160
|
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
|
160
161
|
end
|
161
162
|
end
|
162
163
|
|
163
|
-
def
|
164
|
+
def init_with(coder) #:nodoc:
|
165
|
+
initialize(coder['utc'], coder['zone'], coder['time'])
|
166
|
+
end
|
167
|
+
|
168
|
+
def encode_with(coder) #:nodoc:
|
164
169
|
if coder.respond_to?(:represent_object)
|
165
170
|
coder.represent_object(nil, utc)
|
166
171
|
else
|
@@ -185,8 +190,11 @@ module ActiveSupport
|
|
185
190
|
end
|
186
191
|
alias_method :rfc822, :rfc2822
|
187
192
|
|
188
|
-
#
|
189
|
-
#
|
193
|
+
# Returns a string of the object's date and time.
|
194
|
+
# Accepts an optional <tt>format</tt>:
|
195
|
+
# * <tt>:default</tt> - default value, mimics Ruby 1.9 Time#to_s format.
|
196
|
+
# * <tt>:db</tt> - format outputs time in UTC :db time. See Time#to_formatted_s(:db).
|
197
|
+
# * Any key in <tt>Time::DATE_FORMATS</tt> can be used. See active_support/core_ext/time/conversions.rb.
|
190
198
|
def to_s(format = :default)
|
191
199
|
if format == :db
|
192
200
|
utc.to_s(format)
|
@@ -198,15 +206,11 @@ module ActiveSupport
|
|
198
206
|
end
|
199
207
|
alias_method :to_formatted_s, :to_s
|
200
208
|
|
201
|
-
# Replaces <tt>%Z</tt>
|
202
|
-
#
|
203
|
-
# that zone information is correct
|
209
|
+
# Replaces <tt>%Z</tt> directive with +zone before passing to Time#strftime,
|
210
|
+
# so that zone information is correct.
|
204
211
|
def strftime(format)
|
205
|
-
format = format.gsub(
|
206
|
-
|
207
|
-
.gsub('%:z', formatted_offset(true))
|
208
|
-
.gsub('%::z', formatted_offset(true) + ":00")
|
209
|
-
time.strftime(format)
|
212
|
+
format = format.gsub(/((?:\A|[^%])(?:%%)*)%Z/, "\\1#{zone}")
|
213
|
+
getlocal(utc_offset).strftime(format)
|
210
214
|
end
|
211
215
|
|
212
216
|
# Use the time in UTC for comparisons.
|
@@ -237,7 +241,7 @@ module ActiveSupport
|
|
237
241
|
end
|
238
242
|
|
239
243
|
def eql?(other)
|
240
|
-
|
244
|
+
other.eql?(utc)
|
241
245
|
end
|
242
246
|
|
243
247
|
def hash
|
@@ -259,7 +263,7 @@ module ActiveSupport
|
|
259
263
|
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
|
260
264
|
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
|
261
265
|
if other.acts_like?(:time)
|
262
|
-
|
266
|
+
to_time - other.to_time
|
263
267
|
elsif duration_of_variable_length?(other)
|
264
268
|
method_missing(:-, other)
|
265
269
|
else
|
@@ -277,6 +281,7 @@ module ActiveSupport
|
|
277
281
|
utc.since(other).in_time_zone(time_zone)
|
278
282
|
end
|
279
283
|
end
|
284
|
+
alias_method :in, :since
|
280
285
|
|
281
286
|
def ago(other)
|
282
287
|
since(-other)
|
@@ -317,13 +322,19 @@ module ActiveSupport
|
|
317
322
|
utc.to_r
|
318
323
|
end
|
319
324
|
|
320
|
-
|
321
|
-
|
322
|
-
utc.to_time
|
325
|
+
def to_datetime
|
326
|
+
@to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
|
323
327
|
end
|
324
328
|
|
325
|
-
|
326
|
-
|
329
|
+
# Returns an instance of +Time+, either with the same UTC offset
|
330
|
+
# as +self+ or in the local system timezone depending on the setting
|
331
|
+
# of +ActiveSupport.to_time_preserves_timezone+.
|
332
|
+
def to_time
|
333
|
+
if preserve_timezone
|
334
|
+
@to_time_with_instance_offset ||= getlocal(utc_offset)
|
335
|
+
else
|
336
|
+
@to_time_with_system_offset ||= getlocal
|
337
|
+
end
|
327
338
|
end
|
328
339
|
|
329
340
|
# So that +self+ <tt>acts_like?(:time)</tt>.
|
@@ -338,7 +349,8 @@ module ActiveSupport
|
|
338
349
|
alias_method :kind_of?, :is_a?
|
339
350
|
|
340
351
|
def freeze
|
341
|
-
|
352
|
+
# preload instance variables before freezing
|
353
|
+
period; utc; time; to_datetime; to_time
|
342
354
|
super
|
343
355
|
end
|
344
356
|
|
@@ -350,10 +362,17 @@ module ActiveSupport
|
|
350
362
|
initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
|
351
363
|
end
|
352
364
|
|
365
|
+
# respond_to_missing? is not called in some cases, such as when type conversion is
|
366
|
+
# performed with Kernel#String
|
367
|
+
def respond_to?(sym, include_priv = false)
|
368
|
+
# ensure that we're not going to throw and rescue from NoMethodError in method_missing which is slow
|
369
|
+
return false if sym.to_sym == :to_str
|
370
|
+
super
|
371
|
+
end
|
372
|
+
|
353
373
|
# Ensure proxy class responds to all methods that underlying time instance
|
354
374
|
# responds to.
|
355
375
|
def respond_to_missing?(sym, include_priv)
|
356
|
-
# consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
|
357
376
|
return false if sym.to_sym == :acts_like_date?
|
358
377
|
time.respond_to?(sym, include_priv)
|
359
378
|
end
|
@@ -367,12 +386,12 @@ module ActiveSupport
|
|
367
386
|
end
|
368
387
|
|
369
388
|
private
|
370
|
-
def get_period_and_ensure_valid_local_time
|
389
|
+
def get_period_and_ensure_valid_local_time(period)
|
371
390
|
# we don't want a Time.local instance enforcing its own DST rules as well,
|
372
391
|
# so transfer time values to a utc constructor if necessary
|
373
392
|
@time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
|
374
393
|
begin
|
375
|
-
@time_zone.period_for_local(@time)
|
394
|
+
period || @time_zone.period_for_local(@time)
|
376
395
|
rescue ::TZInfo::PeriodNotFound
|
377
396
|
# time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
|
378
397
|
@time += 1.hour
|
@@ -381,7 +400,7 @@ module ActiveSupport
|
|
381
400
|
end
|
382
401
|
|
383
402
|
def transfer_time_values_to_utc_constructor(time)
|
384
|
-
::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec
|
403
|
+
::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec)
|
385
404
|
end
|
386
405
|
|
387
406
|
def duration_of_variable_length?(obj)
|
@@ -390,7 +409,8 @@ module ActiveSupport
|
|
390
409
|
|
391
410
|
def wrap_with_time_zone(time)
|
392
411
|
if time.acts_like?(:time)
|
393
|
-
|
412
|
+
periods = time_zone.periods_for_local(time)
|
413
|
+
self.class.new(nil, time_zone, time, periods.include?(period) ? period : nil)
|
394
414
|
elsif time.is_a?(Range)
|
395
415
|
wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
|
396
416
|
else
|