activesupport 4.2.5 → 4.2.9

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +175 -0
  3. data/lib/active_support.rb +9 -0
  4. data/lib/active_support/cache/mem_cache_store.rb +1 -1
  5. data/lib/active_support/callbacks.rb +1 -1
  6. data/lib/active_support/core_ext/class/subclasses.rb +0 -2
  7. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  8. data/lib/active_support/core_ext/date_time.rb +1 -0
  9. data/lib/active_support/core_ext/date_time/calculations.rb +22 -2
  10. data/lib/active_support/core_ext/date_time/compatibility.rb +15 -0
  11. data/lib/active_support/core_ext/enumerable.rb +16 -0
  12. data/lib/active_support/core_ext/hash/compact.rb +19 -15
  13. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  14. data/lib/active_support/core_ext/hash/transform_values.rb +2 -2
  15. data/lib/active_support/core_ext/marshal.rb +8 -5
  16. data/lib/active_support/core_ext/numeric/conversions.rb +9 -1
  17. data/lib/active_support/core_ext/object/duplicable.rb +58 -32
  18. data/lib/active_support/core_ext/string/access.rb +1 -1
  19. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  20. data/lib/active_support/core_ext/time.rb +1 -0
  21. data/lib/active_support/core_ext/time/calculations.rb +10 -1
  22. data/lib/active_support/core_ext/time/compatibility.rb +14 -0
  23. data/lib/active_support/gem_version.rb +1 -1
  24. data/lib/active_support/hash_with_indifferent_access.rb +9 -0
  25. data/lib/active_support/inflector/methods.rb +1 -1
  26. data/lib/active_support/logger.rb +50 -0
  27. data/lib/active_support/logger_silence.rb +7 -4
  28. data/lib/active_support/logger_thread_safe_level.rb +32 -0
  29. data/lib/active_support/message_encryptor.rb +8 -1
  30. data/lib/active_support/per_thread_registry.rb +2 -0
  31. data/lib/active_support/security_utils.rb +7 -0
  32. data/lib/active_support/testing/time_helpers.rb +16 -13
  33. data/lib/active_support/time_with_zone.rb +28 -16
  34. data/lib/active_support/values/time_zone.rb +5 -3
  35. data/lib/active_support/xml_mini.rb +30 -15
  36. data/lib/active_support/xml_mini/libxml.rb +1 -3
  37. data/lib/active_support/xml_mini/libxmlsax.rb +1 -4
  38. data/lib/active_support/xml_mini/nokogiri.rb +1 -3
  39. data/lib/active_support/xml_mini/nokogirisax.rb +1 -3
  40. data/lib/active_support/xml_mini/rexml.rb +1 -3
  41. metadata +7 -23
@@ -1,7 +1,7 @@
1
1
  #--
2
- # Most objects are cloneable, but not all. For example you can't dup +nil+:
2
+ # Most objects are cloneable, but not all. For example you can't dup methods:
3
3
  #
4
- # nil.dup # => TypeError: can't dup NilClass
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
- # +nil+ is not duplicable:
31
- #
32
- # nil.duplicable? # => false
33
- # nil.dup # => TypeError: can't dup NilClass
34
- def duplicable?
35
- false
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
- # +false+ is not duplicable:
41
- #
42
- # false.duplicable? # => false
43
- # false.dup # => TypeError: can't dup FalseClass
44
- def duplicable?
45
- false
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
- # +true+ is not duplicable:
51
- #
52
- # true.duplicable? # => false
53
- # true.dup # => TypeError: can't dup TrueClass
54
- def duplicable?
55
- false
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
- # Symbols are not duplicable:
61
- #
62
- # :my_symbol.duplicable? # => false
63
- # :my_symbol.dup # => TypeError: can't dup Symbol
64
- def duplicable?
65
- false
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
- # Numbers are not duplicable:
71
- #
72
- # 3.duplicable? # => false
73
- # 3.dup # => TypeError: can't dup Fixnum
74
- def duplicable?
75
- false
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
 
@@ -1,5 +1,5 @@
1
1
  class String
2
- # If you pass a single Fixnum, returns a substring of one character at that
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
@@ -31,7 +31,7 @@ class String
31
31
  parts.fetch(:offset, form == :utc ? 0 : nil)
32
32
  )
33
33
 
34
- form == :utc ? time.utc : time.getlocal
34
+ form == :utc ? time.utc : time.to_time
35
35
  end
36
36
 
37
37
  # Converts a string to a Date value.
@@ -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'
@@ -63,6 +63,13 @@ class Time
63
63
  end_of_day.to_i - to_i
64
64
  end
65
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
+
66
73
  # Returns a new Time where one or more of the elements have been changed according
67
74
  # to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
68
75
  # <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
@@ -250,7 +257,9 @@ class Time
250
257
  # can be chronologically compared with a Time
251
258
  def compare_with_coercion(other)
252
259
  # we're avoiding Time#to_datetime cause it's expensive
253
- if other.is_a?(Time)
260
+ if other.class == Time
261
+ compare_without_coercion(other)
262
+ elsif other.is_a?(Time)
254
263
  compare_without_coercion(other.to_time)
255
264
  else
256
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
@@ -7,7 +7,7 @@ module ActiveSupport
7
7
  module VERSION
8
8
  MAJOR = 4
9
9
  MINOR = 2
10
- TINY = 5
10
+ TINY = 9
11
11
  PRE = nil
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -245,6 +245,15 @@ 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
259
  _new_hash = Hash.new
@@ -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]/) { $&.capitalize }
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
- old_logger_level, self.level = level, temporary_level
16
+ old_local_level = local_level
17
+ self.local_level = temporary_level
18
+
16
19
  yield self
17
20
  ensure
18
- self.level = old_logger_level
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::Cipher.new(@cipher)
107
+ OpenSSL::Cipher.new(@cipher)
101
108
  end
102
109
 
103
110
  def verifier
@@ -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
  #
@@ -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, return_value)
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) { return_value }
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 +Date.today+.
45
+ # stubbing +Time.now+, +Date.today+, and +DateTime.now+.
46
46
  #
47
- # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
47
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
48
48
  # travel 1.day
49
- # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
50
- # Date.current # => Sun, 10 Nov 2013
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+ and
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 # => Sat, 09 Nov 2013 15:34:49 EST -05:00
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 # => Wed, 24 Nov 2004 01:04:44 EST -05:00
70
- # Date.current # => Wed, 24 Nov 2004
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, now)
101
- simple_stubs.stub_object(Date, :today, now.to_date)
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