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.

Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +406 -418
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -2
  5. data/lib/active_support/backtrace_cleaner.rb +8 -8
  6. data/lib/active_support/benchmarkable.rb +0 -10
  7. data/lib/active_support/cache/file_store.rb +32 -22
  8. data/lib/active_support/cache/mem_cache_store.rb +5 -7
  9. data/lib/active_support/cache/memory_store.rb +1 -0
  10. data/lib/active_support/cache/strategy/local_cache.rb +11 -30
  11. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  12. data/lib/active_support/cache.rb +75 -41
  13. data/lib/active_support/callbacks.rb +482 -261
  14. data/lib/active_support/concern.rb +23 -7
  15. data/lib/active_support/configurable.rb +1 -1
  16. data/lib/active_support/core_ext/array/access.rb +11 -1
  17. data/lib/active_support/core_ext/array/conversions.rb +2 -17
  18. data/lib/active_support/core_ext/array/grouping.rb +29 -12
  19. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
  20. data/lib/active_support/core_ext/array.rb +0 -1
  21. data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
  22. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +16 -0
  23. data/lib/active_support/core_ext/class/attribute.rb +1 -2
  24. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -170
  25. data/lib/active_support/core_ext/class/delegating_attributes.rb +13 -8
  26. data/lib/active_support/core_ext/class/subclasses.rb +0 -2
  27. data/lib/active_support/core_ext/class.rb +0 -1
  28. data/lib/active_support/core_ext/date/calculations.rb +10 -0
  29. data/lib/active_support/core_ext/date/conversions.rb +9 -1
  30. data/lib/active_support/core_ext/date/zones.rb +2 -33
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -11
  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 +41 -0
  34. data/lib/active_support/core_ext/date_time/calculations.rb +45 -22
  35. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  36. data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
  37. data/lib/active_support/core_ext/date_time/zones.rb +3 -21
  38. data/lib/active_support/core_ext/date_time.rb +1 -0
  39. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  40. data/lib/active_support/core_ext/enumerable.rb +17 -1
  41. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  42. data/lib/active_support/core_ext/hash/compact.rb +24 -0
  43. data/lib/active_support/core_ext/hash/conversions.rb +9 -8
  44. data/lib/active_support/core_ext/hash/except.rb +8 -2
  45. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
  46. data/lib/active_support/core_ext/hash/keys.rb +25 -19
  47. data/lib/active_support/core_ext/hash/slice.rb +8 -2
  48. data/lib/active_support/core_ext/hash/transform_values.rb +23 -0
  49. data/lib/active_support/core_ext/hash.rb +2 -1
  50. data/lib/active_support/core_ext/integer/time.rb +0 -15
  51. data/lib/active_support/core_ext/kernel/concern.rb +10 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
  53. data/lib/active_support/core_ext/kernel/reporting.rb +13 -2
  54. data/lib/active_support/core_ext/kernel.rb +3 -2
  55. data/lib/active_support/core_ext/load_error.rb +4 -1
  56. data/lib/active_support/core_ext/marshal.rb +8 -5
  57. data/lib/active_support/core_ext/module/aliasing.rb +2 -2
  58. data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
  59. data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
  60. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  61. data/lib/active_support/core_ext/module/delegation.rb +53 -25
  62. data/lib/active_support/core_ext/module/deprecation.rb +0 -2
  63. data/lib/active_support/core_ext/module/introspection.rb +0 -16
  64. data/lib/active_support/core_ext/module/method_transplanting.rb +13 -0
  65. data/lib/active_support/core_ext/module.rb +1 -0
  66. data/lib/active_support/core_ext/numeric/conversions.rb +11 -3
  67. data/lib/active_support/core_ext/numeric/time.rb +4 -29
  68. data/lib/active_support/core_ext/object/blank.rb +44 -18
  69. data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
  70. data/lib/active_support/core_ext/object/duplicable.rb +72 -33
  71. data/lib/active_support/core_ext/object/inclusion.rb +16 -15
  72. data/lib/active_support/core_ext/object/itself.rb +15 -0
  73. data/lib/active_support/core_ext/object/json.rb +197 -0
  74. data/lib/active_support/core_ext/object/to_query.rb +14 -6
  75. data/lib/active_support/core_ext/object/try.rb +36 -14
  76. data/lib/active_support/core_ext/object/with_options.rb +30 -3
  77. data/lib/active_support/core_ext/object.rb +2 -1
  78. data/lib/active_support/core_ext/string/access.rb +35 -35
  79. data/lib/active_support/core_ext/string/conversions.rb +10 -9
  80. data/lib/active_support/core_ext/string/exclude.rb +3 -3
  81. data/lib/active_support/core_ext/string/filters.rb +51 -3
  82. data/lib/active_support/core_ext/string/inflections.rb +15 -10
  83. data/lib/active_support/core_ext/string/output_safety.rb +97 -33
  84. data/lib/active_support/core_ext/string/zones.rb +1 -0
  85. data/lib/active_support/core_ext/thread.rb +12 -5
  86. data/lib/active_support/core_ext/time/calculations.rb +47 -68
  87. data/lib/active_support/core_ext/time/compatibility.rb +14 -0
  88. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  89. data/lib/active_support/core_ext/time/zones.rb +2 -20
  90. data/lib/active_support/core_ext/time.rb +1 -0
  91. data/lib/active_support/core_ext.rb +0 -1
  92. data/lib/active_support/dependencies/autoload.rb +1 -1
  93. data/lib/active_support/dependencies.rb +64 -25
  94. data/lib/active_support/deprecation/behaviors.rb +4 -4
  95. data/lib/active_support/deprecation.rb +4 -4
  96. data/lib/active_support/duration.rb +55 -11
  97. data/lib/active_support/file_update_checker.rb +1 -1
  98. data/lib/active_support/gem_version.rb +15 -0
  99. data/lib/active_support/hash_with_indifferent_access.rb +39 -11
  100. data/lib/active_support/i18n.rb +4 -4
  101. data/lib/active_support/i18n_railtie.rb +1 -7
  102. data/lib/active_support/inflections.rb +6 -1
  103. data/lib/active_support/inflector/inflections.rb +19 -19
  104. data/lib/active_support/inflector/methods.rb +66 -25
  105. data/lib/active_support/json/decoding.rb +15 -22
  106. data/lib/active_support/json/encoding.rb +125 -286
  107. data/lib/active_support/key_generator.rb +8 -10
  108. data/lib/active_support/lazy_load_hooks.rb +1 -1
  109. data/lib/active_support/log_subscriber/test_helper.rb +1 -1
  110. data/lib/active_support/logger.rb +51 -1
  111. data/lib/active_support/logger_silence.rb +7 -4
  112. data/lib/active_support/logger_thread_safe_level.rb +32 -0
  113. data/lib/active_support/message_encryptor.rb +14 -6
  114. data/lib/active_support/message_verifier.rb +16 -12
  115. data/lib/active_support/multibyte/chars.rb +2 -3
  116. data/lib/active_support/multibyte/unicode.rb +46 -58
  117. data/lib/active_support/notifications/fanout.rb +12 -7
  118. data/lib/active_support/notifications/instrumenter.rb +2 -1
  119. data/lib/active_support/notifications.rb +11 -6
  120. data/lib/active_support/number_helper/number_converter.rb +182 -0
  121. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  122. data/lib/active_support/number_helper/number_to_delimited_converter.rb +23 -0
  123. data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
  124. data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
  125. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  126. data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
  127. data/lib/active_support/number_helper/number_to_rounded_converter.rb +87 -0
  128. data/lib/active_support/number_helper.rb +32 -324
  129. data/lib/active_support/ordered_options.rb +8 -0
  130. data/lib/active_support/per_thread_registry.rb +13 -10
  131. data/lib/active_support/security_utils.rb +27 -0
  132. data/lib/active_support/subscriber.rb +35 -3
  133. data/lib/active_support/test_case.rb +52 -19
  134. data/lib/active_support/testing/assertions.rb +1 -31
  135. data/lib/active_support/testing/autorun.rb +2 -2
  136. data/lib/active_support/testing/constant_lookup.rb +1 -5
  137. data/lib/active_support/testing/declarative.rb +7 -21
  138. data/lib/active_support/testing/isolation.rb +29 -69
  139. data/lib/active_support/testing/setup_and_teardown.rb +17 -2
  140. data/lib/active_support/testing/tagged_logging.rb +2 -2
  141. data/lib/active_support/testing/time_helpers.rb +134 -0
  142. data/lib/active_support/time.rb +0 -2
  143. data/lib/active_support/time_with_zone.rb +60 -40
  144. data/lib/active_support/values/time_zone.rb +101 -101
  145. data/lib/active_support/values/unicode_tables.dat +0 -0
  146. data/lib/active_support/version.rb +4 -7
  147. data/lib/active_support/xml_mini/jdom.rb +6 -5
  148. data/lib/active_support/xml_mini/libxml.rb +1 -3
  149. data/lib/active_support/xml_mini/libxmlsax.rb +1 -4
  150. data/lib/active_support/xml_mini/nokogiri.rb +1 -3
  151. data/lib/active_support/xml_mini/nokogirisax.rb +1 -3
  152. data/lib/active_support/xml_mini/rexml.rb +7 -8
  153. data/lib/active_support/xml_mini.rb +33 -15
  154. data/lib/active_support.rb +27 -2
  155. metadata +43 -43
  156. data/lib/active_support/basic_object.rb +0 -11
  157. data/lib/active_support/buffered_logger.rb +0 -21
  158. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  159. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  160. data/lib/active_support/core_ext/logger.rb +0 -67
  161. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  162. data/lib/active_support/core_ext/proc.rb +0 -17
  163. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  164. data/lib/active_support/file_watcher.rb +0 -36
  165. data/lib/active_support/json/variable.rb +0 -18
  166. 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' # => 'foo' is not nil or false
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
@@ -1,5 +1,5 @@
1
1
  gem 'minitest'
2
2
 
3
- require 'minitest/unit'
3
+ require 'minitest'
4
4
 
5
- MiniTest::Unit.autorun
5
+ Minitest.autorun
@@ -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("::").constantize
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 "verify something" do
23
- # ...
24
- # end
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 = instance_method(test_name) rescue false
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.extend(Module.new {
54
- def test_methods
55
- ParallelEach.new super
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(runner)
76
- _run_class_setup
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
- retval, proxy = Marshal.load(serialized)
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
- proxy = ProxyTestResult.new
94
- retval = yield proxy
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
- proxy = ProxyTestResult.new
116
- retval = yield proxy
66
+ yield
117
67
  File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
118
- file.puts [Marshal.dump([retval, proxy])].pack("m")
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
- ENV["ISOLATION_TEST"] = @method_name
124
- ENV["ISOLATION_OUTPUT"] = tmpfile.path
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
- `#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`
128
-
129
- ENV.delete("ISOLATION_TEST")
130
- ENV.delete("ISOLATION_OUTPUT")
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}: #{__name__}"
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
@@ -1,5 +1,3 @@
1
- require 'active_support'
2
-
3
1
  module ActiveSupport
4
2
  autoload :Duration, 'active_support/duration'
5
3
  autoload :TimeWithZone, 'active_support/time_with_zone'
@@ -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, @time_zone, @time = utc_time, time_zone, local_time
48
- @period = @utc ? period : get_period_and_ensure_valid_local_time
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 or DateTime instance that represents the time in +time_zone+.
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 or DateTime instance that represents the time in UTC.
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.local()</tt> instance of the simultaneous time in your
77
- # system's <tt>ENV['TZ']</tt> zone.
78
- def localtime
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-01T15:15:10.000Z"
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 15:15:10 +0000"
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(3)
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 encode_with(coder)
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
- # <tt>:db</tt> format outputs time in UTC; all others output time in local.
189
- # Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
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> and <tt>%z</tt> directives with +zone+ and
202
- # +formatted_offset+, respectively, before passing to Time#strftime, so
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('%Z', zone)
206
- .gsub('%z', formatted_offset(false))
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
- utc.eql?(other)
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
- utc.to_f - other.to_f
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
- # Return an instance of Time in the system timezone.
321
- def to_time
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
- def to_datetime
326
- utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
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
- period; utc; time # preload instance variables before freezing
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, Rational(time.nsec, 1000))
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
- self.class.new(nil, time_zone, time)
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