activesupport 4.2.3 → 4.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +219 -0
- data/lib/active_support/cache/mem_cache_store.rb +1 -1
- data/lib/active_support/cache.rb +1 -1
- data/lib/active_support/callbacks.rb +12 -4
- data/lib/active_support/core_ext/class/subclasses.rb +0 -2
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +22 -2
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/enumerable.rb +16 -0
- data/lib/active_support/core_ext/hash/compact.rb +19 -15
- data/lib/active_support/core_ext/hash/conversions.rb +1 -2
- data/lib/active_support/core_ext/hash/transform_values.rb +2 -2
- data/lib/active_support/core_ext/marshal.rb +8 -5
- data/lib/active_support/core_ext/module/method_transplanting.rb +3 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +11 -3
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/duplicable.rb +58 -32
- data/lib/active_support/core_ext/object/json.rb +1 -1
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/string/access.rb +1 -1
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/time/calculations.rb +17 -1
- data/lib/active_support/core_ext/time/compatibility.rb +14 -0
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/gem_version.rb +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +22 -3
- data/lib/active_support/inflector/methods.rb +1 -1
- data/lib/active_support/logger.rb +50 -0
- 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 +8 -1
- data/lib/active_support/message_verifier.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +1 -1
- data/lib/active_support/per_thread_registry.rb +5 -3
- data/lib/active_support/security_utils.rb +7 -0
- data/lib/active_support/testing/time_helpers.rb +16 -13
- data/lib/active_support/time_with_zone.rb +29 -17
- data/lib/active_support/values/time_zone.rb +5 -3
- 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 +1 -3
- data/lib/active_support/xml_mini.rb +30 -15
- data/lib/active_support.rb +9 -0
- metadata +7 -23
@@ -1,7 +1,7 @@
|
|
1
1
|
#--
|
2
|
-
# Most objects are cloneable, but not all. For example you can't dup
|
2
|
+
# Most objects are cloneable, but not all. For example you can't dup methods:
|
3
3
|
#
|
4
|
-
#
|
4
|
+
# method(:puts).dup # => TypeError: allocator undefined for Method
|
5
5
|
#
|
6
6
|
# Classes may signal their instances are not duplicable removing +dup+/+clone+
|
7
7
|
# or raising exceptions from them. So, to dup an arbitrary object you normally
|
@@ -27,52 +27,78 @@ class Object
|
|
27
27
|
end
|
28
28
|
|
29
29
|
class NilClass
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
begin
|
31
|
+
nil.dup
|
32
|
+
rescue TypeError
|
33
|
+
|
34
|
+
# +nil+ is not duplicable:
|
35
|
+
#
|
36
|
+
# nil.duplicable? # => false
|
37
|
+
# nil.dup # => TypeError: can't dup NilClass
|
38
|
+
def duplicable?
|
39
|
+
false
|
40
|
+
end
|
36
41
|
end
|
37
42
|
end
|
38
43
|
|
39
44
|
class FalseClass
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
45
|
+
begin
|
46
|
+
false.dup
|
47
|
+
rescue TypeError
|
48
|
+
|
49
|
+
# +false+ is not duplicable:
|
50
|
+
#
|
51
|
+
# false.duplicable? # => false
|
52
|
+
# false.dup # => TypeError: can't dup FalseClass
|
53
|
+
def duplicable?
|
54
|
+
false
|
55
|
+
end
|
46
56
|
end
|
47
57
|
end
|
48
58
|
|
49
59
|
class TrueClass
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
60
|
+
begin
|
61
|
+
true.dup
|
62
|
+
rescue TypeError
|
63
|
+
|
64
|
+
# +true+ is not duplicable:
|
65
|
+
#
|
66
|
+
# true.duplicable? # => false
|
67
|
+
# true.dup # => TypeError: can't dup TrueClass
|
68
|
+
def duplicable?
|
69
|
+
false
|
70
|
+
end
|
56
71
|
end
|
57
72
|
end
|
58
73
|
|
59
74
|
class Symbol
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
75
|
+
begin
|
76
|
+
:symbol.dup # Ruby 2.4.x.
|
77
|
+
'symbol_from_string'.to_sym.dup # Some symbols can't `dup` in Ruby 2.4.0.
|
78
|
+
rescue TypeError
|
79
|
+
|
80
|
+
# Symbols are not duplicable:
|
81
|
+
#
|
82
|
+
# :my_symbol.duplicable? # => false
|
83
|
+
# :my_symbol.dup # => TypeError: can't dup Symbol
|
84
|
+
def duplicable?
|
85
|
+
false
|
86
|
+
end
|
66
87
|
end
|
67
88
|
end
|
68
89
|
|
69
90
|
class Numeric
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
91
|
+
begin
|
92
|
+
1.dup
|
93
|
+
rescue TypeError
|
94
|
+
|
95
|
+
# Numbers are not duplicable:
|
96
|
+
#
|
97
|
+
# 3.duplicable? # => false
|
98
|
+
# 3.dup # => TypeError: can't dup Integer
|
99
|
+
def duplicable?
|
100
|
+
false
|
101
|
+
end
|
76
102
|
end
|
77
103
|
end
|
78
104
|
|
@@ -26,7 +26,7 @@ require 'active_support/core_ext/module/aliasing'
|
|
26
26
|
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
|
27
27
|
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
|
28
28
|
# should give exactly the same results with or without active support.
|
29
|
-
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass
|
29
|
+
[Enumerable, Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
|
30
30
|
klass.class_eval do
|
31
31
|
def to_json_with_active_support_encoder(options = nil) # :nodoc:
|
32
32
|
if options.is_a?(::JSON::State)
|
@@ -65,10 +65,10 @@ class Object
|
|
65
65
|
|
66
66
|
# Same as #try, but will raise a NoMethodError exception if the receiver is not +nil+ and
|
67
67
|
# does not implement the tried method.
|
68
|
-
|
68
|
+
|
69
69
|
def try!(*a, &b)
|
70
70
|
if a.empty? && block_given?
|
71
|
-
if b.arity
|
71
|
+
if b.arity == 0
|
72
72
|
instance_eval(&b)
|
73
73
|
else
|
74
74
|
yield self
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class String
|
2
|
-
# If you pass a single
|
2
|
+
# If you pass a single integer, returns a substring of one character at that
|
3
3
|
# position. The first character of the string is at position 0, the next at
|
4
4
|
# position 1, and so on. If a range is supplied, a substring containing
|
5
5
|
# characters at offsets given by the range is returned. In both cases, if an
|
@@ -3,6 +3,7 @@ require 'active_support/core_ext/time/conversions'
|
|
3
3
|
require 'active_support/time_with_zone'
|
4
4
|
require 'active_support/core_ext/time/zones'
|
5
5
|
require 'active_support/core_ext/date_and_time/calculations'
|
6
|
+
require 'active_support/core_ext/date/calculations'
|
6
7
|
|
7
8
|
class Time
|
8
9
|
include DateAndTime::Calculations
|
@@ -62,6 +63,13 @@ class Time
|
|
62
63
|
end_of_day.to_i - to_i
|
63
64
|
end
|
64
65
|
|
66
|
+
# Returns the fraction of a second as a +Rational+
|
67
|
+
#
|
68
|
+
# Time.new(2012, 8, 29, 0, 0, 0.5).sec_fraction # => (1/2)
|
69
|
+
def sec_fraction
|
70
|
+
subsec
|
71
|
+
end
|
72
|
+
|
65
73
|
# Returns a new Time where one or more of the elements have been changed according
|
66
74
|
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
|
67
75
|
# <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
|
@@ -104,6 +112,12 @@ class Time
|
|
104
112
|
# takes a hash with any of these keys: <tt>:years</tt>, <tt>:months</tt>,
|
105
113
|
# <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>, <tt>:minutes</tt>,
|
106
114
|
# <tt>:seconds</tt>.
|
115
|
+
#
|
116
|
+
# Time.new(2015, 8, 1, 14, 35, 0).advance(seconds: 1) # => 2015-08-01 14:35:01 -0700
|
117
|
+
# Time.new(2015, 8, 1, 14, 35, 0).advance(minutes: 1) # => 2015-08-01 14:36:00 -0700
|
118
|
+
# Time.new(2015, 8, 1, 14, 35, 0).advance(hours: 1) # => 2015-08-01 15:35:00 -0700
|
119
|
+
# Time.new(2015, 8, 1, 14, 35, 0).advance(days: 1) # => 2015-08-02 14:35:00 -0700
|
120
|
+
# Time.new(2015, 8, 1, 14, 35, 0).advance(weeks: 1) # => 2015-08-08 14:35:00 -0700
|
107
121
|
def advance(options)
|
108
122
|
unless options[:weeks].nil?
|
109
123
|
options[:weeks], partial_weeks = options[:weeks].divmod(1)
|
@@ -243,7 +257,9 @@ class Time
|
|
243
257
|
# can be chronologically compared with a Time
|
244
258
|
def compare_with_coercion(other)
|
245
259
|
# we're avoiding Time#to_datetime cause it's expensive
|
246
|
-
if other.
|
260
|
+
if other.class == Time
|
261
|
+
compare_without_coercion(other)
|
262
|
+
elsif other.is_a?(Time)
|
247
263
|
compare_without_coercion(other.to_time)
|
248
264
|
else
|
249
265
|
to_datetime <=> other
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "active_support/core_ext/date_and_time/compatibility"
|
2
|
+
require "active_support/core_ext/module/remove_method"
|
3
|
+
|
4
|
+
class Time
|
5
|
+
include DateAndTime::Compatibility
|
6
|
+
|
7
|
+
remove_possible_method :to_time
|
8
|
+
|
9
|
+
# Either return +self+ or the time in the local system timezone depending
|
10
|
+
# on the setting of +ActiveSupport.to_time_preserves_timezone+.
|
11
|
+
def to_time
|
12
|
+
preserve_timezone ? self : getlocal
|
13
|
+
end
|
14
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/time/acts_like'
|
2
2
|
require 'active_support/core_ext/time/calculations'
|
3
|
+
require 'active_support/core_ext/time/compatibility'
|
3
4
|
require 'active_support/core_ext/time/conversions'
|
4
5
|
require 'active_support/core_ext/time/marshal'
|
5
6
|
require 'active_support/core_ext/time/zones'
|
@@ -188,7 +188,7 @@ module ActiveSupport
|
|
188
188
|
# dup[:a][:c] # => "c"
|
189
189
|
def dup
|
190
190
|
self.class.new(self).tap do |new_hash|
|
191
|
-
new_hash
|
191
|
+
set_defaults(new_hash)
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
@@ -245,9 +245,20 @@ module ActiveSupport
|
|
245
245
|
dup.tap { |hash| hash.reject!(*args, &block) }
|
246
246
|
end
|
247
247
|
|
248
|
+
def transform_values(*args, &block)
|
249
|
+
return to_enum(:transform_values) unless block_given?
|
250
|
+
dup.tap { |hash| hash.transform_values!(*args, &block) }
|
251
|
+
end
|
252
|
+
|
253
|
+
def compact
|
254
|
+
dup.tap(&:compact!)
|
255
|
+
end
|
256
|
+
|
248
257
|
# Convert to a regular hash with string keys.
|
249
258
|
def to_hash
|
250
|
-
_new_hash = Hash.new
|
259
|
+
_new_hash = Hash.new
|
260
|
+
set_defaults(_new_hash)
|
261
|
+
|
251
262
|
each do |key, value|
|
252
263
|
_new_hash[key] = convert_value(value, for: :to_hash)
|
253
264
|
end
|
@@ -267,7 +278,7 @@ module ActiveSupport
|
|
267
278
|
value.nested_under_indifferent_access
|
268
279
|
end
|
269
280
|
elsif value.is_a?(Array)
|
270
|
-
|
281
|
+
if options[:for] != :assignment || value.frozen?
|
271
282
|
value = value.dup
|
272
283
|
end
|
273
284
|
value.map! { |e| convert_value(e, options) }
|
@@ -275,6 +286,14 @@ module ActiveSupport
|
|
275
286
|
value
|
276
287
|
end
|
277
288
|
end
|
289
|
+
|
290
|
+
def set_defaults(target)
|
291
|
+
if default_proc
|
292
|
+
target.default_proc = default_proc.dup
|
293
|
+
else
|
294
|
+
target.default = default
|
295
|
+
end
|
296
|
+
end
|
278
297
|
end
|
279
298
|
end
|
280
299
|
|
@@ -153,7 +153,7 @@ module ActiveSupport
|
|
153
153
|
# 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
|
154
154
|
# 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
|
155
155
|
def titleize(word)
|
156
|
-
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) {
|
156
|
+
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
|
157
157
|
end
|
158
158
|
|
159
159
|
# Create the name of a table like Rails does for models to table names. This
|
@@ -1,11 +1,24 @@
|
|
1
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
2
2
|
require 'active_support/logger_silence'
|
3
|
+
require 'active_support/logger_thread_safe_level'
|
3
4
|
require 'logger'
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
class Logger < ::Logger
|
8
|
+
include ActiveSupport::LoggerThreadSafeLevel
|
7
9
|
include LoggerSilence
|
8
10
|
|
11
|
+
# Returns true if the logger destination matches one of the sources
|
12
|
+
#
|
13
|
+
# logger = Logger.new(STDOUT)
|
14
|
+
# ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
|
15
|
+
# # => true
|
16
|
+
def self.logger_outputs_to?(logger, *sources)
|
17
|
+
logdev = logger.instance_variable_get("@logdev")
|
18
|
+
logger_source = logdev.dev if logdev.respond_to?(:dev)
|
19
|
+
sources.any? { |source| source == logger_source }
|
20
|
+
end
|
21
|
+
|
9
22
|
# Broadcasts logs to multiple loggers.
|
10
23
|
def self.broadcast(logger) # :nodoc:
|
11
24
|
Module.new do
|
@@ -38,12 +51,49 @@ module ActiveSupport
|
|
38
51
|
logger.level = level
|
39
52
|
super(level)
|
40
53
|
end
|
54
|
+
|
55
|
+
define_method(:local_level=) do |level|
|
56
|
+
logger.local_level = level if logger.respond_to?(:local_level=)
|
57
|
+
super(level) if respond_to?(:local_level=)
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method(:silence) do |level = Logger::ERROR, &block|
|
61
|
+
if logger.respond_to?(:silence) && logger.method(:silence).owner != ::Kernel
|
62
|
+
logger.silence(level) do
|
63
|
+
if respond_to?(:silence) && method(:silence).owner != ::Kernel
|
64
|
+
super(level, &block)
|
65
|
+
else
|
66
|
+
block.call(self)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
if respond_to?(:silence) && method(:silence).owner != ::Kernel
|
71
|
+
super(level, &block)
|
72
|
+
else
|
73
|
+
block.call(self)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
41
77
|
end
|
42
78
|
end
|
43
79
|
|
44
80
|
def initialize(*args)
|
45
81
|
super
|
46
82
|
@formatter = SimpleFormatter.new
|
83
|
+
after_initialize if respond_to? :after_initialize
|
84
|
+
end
|
85
|
+
|
86
|
+
def add(severity, message = nil, progname = nil, &block)
|
87
|
+
return true if @logdev.nil? || (severity || UNKNOWN) < level
|
88
|
+
super
|
89
|
+
end
|
90
|
+
|
91
|
+
Logger::Severity.constants.each do |severity|
|
92
|
+
class_eval(<<-EOT, __FILE__, __LINE__ + 1)
|
93
|
+
def #{severity.downcase}? # def debug?
|
94
|
+
Logger::#{severity} >= level # DEBUG >= level
|
95
|
+
end # end
|
96
|
+
EOT
|
47
97
|
end
|
48
98
|
|
49
99
|
# Simple formatter which only displays the message.
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'active_support/concern'
|
2
|
+
require 'thread_safe'
|
2
3
|
|
3
4
|
module LoggerSilence
|
4
5
|
extend ActiveSupport::Concern
|
5
|
-
|
6
|
+
|
6
7
|
included do
|
7
8
|
cattr_accessor :silencer
|
8
9
|
self.silencer = true
|
@@ -12,13 +13,15 @@ module LoggerSilence
|
|
12
13
|
def silence(temporary_level = Logger::ERROR)
|
13
14
|
if silencer
|
14
15
|
begin
|
15
|
-
|
16
|
+
old_local_level = local_level
|
17
|
+
self.local_level = temporary_level
|
18
|
+
|
16
19
|
yield self
|
17
20
|
ensure
|
18
|
-
self.
|
21
|
+
self.local_level = old_local_level
|
19
22
|
end
|
20
23
|
else
|
21
24
|
yield self
|
22
25
|
end
|
23
26
|
end
|
24
|
-
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'thread_safe'
|
3
|
+
|
4
|
+
module ActiveSupport
|
5
|
+
module LoggerThreadSafeLevel
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def after_initialize
|
9
|
+
@local_levels = ThreadSafe::Cache.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def local_log_id
|
13
|
+
Thread.current.__id__
|
14
|
+
end
|
15
|
+
|
16
|
+
def local_level
|
17
|
+
@local_levels[local_log_id]
|
18
|
+
end
|
19
|
+
|
20
|
+
def local_level=(level)
|
21
|
+
if level
|
22
|
+
@local_levels[local_log_id] = level
|
23
|
+
else
|
24
|
+
@local_levels.delete(local_log_id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def level
|
29
|
+
local_level || super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -18,6 +18,8 @@ module ActiveSupport
|
|
18
18
|
# encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
|
19
19
|
# crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
|
20
20
|
class MessageEncryptor
|
21
|
+
DEFAULT_CIPHER = "aes-256-cbc"
|
22
|
+
|
21
23
|
module NullSerializer #:nodoc:
|
22
24
|
def self.load(value)
|
23
25
|
value
|
@@ -64,6 +66,11 @@ module ActiveSupport
|
|
64
66
|
_decrypt(verifier.verify(value))
|
65
67
|
end
|
66
68
|
|
69
|
+
# Given a cipher, returns the key length of the cipher to help generate the key of desired size
|
70
|
+
def self.key_len(cipher = DEFAULT_CIPHER)
|
71
|
+
OpenSSL::Cipher.new(cipher).key_len
|
72
|
+
end
|
73
|
+
|
67
74
|
private
|
68
75
|
|
69
76
|
def _encrypt(value)
|
@@ -97,7 +104,7 @@ module ActiveSupport
|
|
97
104
|
end
|
98
105
|
|
99
106
|
def new_cipher
|
100
|
-
OpenSSL::Cipher
|
107
|
+
OpenSSL::Cipher.new(@cipher)
|
101
108
|
end
|
102
109
|
|
103
110
|
def verifier
|
@@ -35,7 +35,7 @@ module ActiveSupport
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def verify(signed_message)
|
38
|
-
raise InvalidSignature if signed_message.blank?
|
38
|
+
raise InvalidSignature if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
|
39
39
|
|
40
40
|
data, digest = signed_message.split("--")
|
41
41
|
if data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
|
1
3
|
module ActiveSupport
|
2
4
|
# This module is used to encapsulate access to thread local variables.
|
3
5
|
#
|
@@ -43,9 +45,9 @@ module ActiveSupport
|
|
43
45
|
protected
|
44
46
|
def method_missing(name, *args, &block) # :nodoc:
|
45
47
|
# Caches the method definition as a singleton method of the receiver.
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
#
|
49
|
+
# By letting #delegate handle it, we avoid an enclosure that'll capture args.
|
50
|
+
singleton_class.delegate name, to: :instance
|
49
51
|
|
50
52
|
send(name, *args, &block)
|
51
53
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
1
3
|
module ActiveSupport
|
2
4
|
module SecurityUtils
|
3
5
|
# Constant time string comparison.
|
@@ -16,5 +18,10 @@ module ActiveSupport
|
|
16
18
|
res == 0
|
17
19
|
end
|
18
20
|
module_function :secure_compare
|
21
|
+
|
22
|
+
def variable_size_secure_compare(a, b) # :nodoc:
|
23
|
+
secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b))
|
24
|
+
end
|
25
|
+
module_function :variable_size_secure_compare
|
19
26
|
end
|
20
27
|
end
|
@@ -7,7 +7,7 @@ module ActiveSupport
|
|
7
7
|
@stubs = {}
|
8
8
|
end
|
9
9
|
|
10
|
-
def stub_object(object, method_name,
|
10
|
+
def stub_object(object, method_name, &block)
|
11
11
|
key = [object.object_id, method_name]
|
12
12
|
|
13
13
|
if stub = @stubs[key]
|
@@ -19,7 +19,7 @@ module ActiveSupport
|
|
19
19
|
@stubs[key] = Stub.new(object, method_name, new_name)
|
20
20
|
|
21
21
|
object.singleton_class.send :alias_method, new_name, method_name
|
22
|
-
object.define_singleton_method(method_name)
|
22
|
+
object.define_singleton_method(method_name, &block)
|
23
23
|
end
|
24
24
|
|
25
25
|
def unstub_all!
|
@@ -42,12 +42,13 @@ module ActiveSupport
|
|
42
42
|
# Containing helpers that helps you test passage of time.
|
43
43
|
module TimeHelpers
|
44
44
|
# Changes current time to the time in the future or in the past by a given time difference by
|
45
|
-
# stubbing +Time.now+ and +
|
45
|
+
# stubbing +Time.now+, +Date.today+, and +DateTime.now+.
|
46
46
|
#
|
47
|
-
# Time.current
|
47
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
48
48
|
# travel 1.day
|
49
|
-
# Time.current
|
50
|
-
# Date.current
|
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
|
51
52
|
#
|
52
53
|
# This method also accepts a block, which will return the current time back to its original
|
53
54
|
# state at the end of the block:
|
@@ -61,13 +62,14 @@ module ActiveSupport
|
|
61
62
|
travel_to Time.now + duration, &block
|
62
63
|
end
|
63
64
|
|
64
|
-
# Changes current time to the given time by stubbing +Time.now
|
65
|
-
# +Date.today+ to return the time or date passed into this method.
|
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.
|
66
67
|
#
|
67
|
-
# Time.current
|
68
|
+
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
|
68
69
|
# travel_to Time.new(2004, 11, 24, 01, 04, 44)
|
69
|
-
# Time.current
|
70
|
-
# Date.current
|
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
|
71
73
|
#
|
72
74
|
# Dates are taken as their timestamp at the beginning of the day in the
|
73
75
|
# application time zone. <tt>Time.current</tt> returns said timestamp,
|
@@ -97,8 +99,9 @@ module ActiveSupport
|
|
97
99
|
now = date_or_time.to_time.change(usec: 0)
|
98
100
|
end
|
99
101
|
|
100
|
-
simple_stubs.stub_object(Time, :now
|
101
|
-
simple_stubs.stub_object(Date, :today
|
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)) }
|
102
105
|
|
103
106
|
if block_given?
|
104
107
|
begin
|