activesupport 4.2.0 → 5.0.0.1

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +630 -220
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +2 -3
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +1 -1
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +36 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +63 -54
  10. data/lib/active_support/cache/memory_store.rb +16 -21
  11. data/lib/active_support/cache/null_store.rb +1 -4
  12. data/lib/active_support/cache/strategy/local_cache.rb +31 -20
  13. data/lib/active_support/cache.rb +73 -89
  14. data/lib/active_support/callbacks.rb +195 -155
  15. data/lib/active_support/concern.rb +2 -2
  16. data/lib/active_support/concurrency/latch.rb +7 -15
  17. data/lib/active_support/concurrency/share_lock.rb +186 -0
  18. data/lib/active_support/configurable.rb +1 -0
  19. data/lib/active_support/core_ext/array/access.rb +27 -1
  20. data/lib/active_support/core_ext/array/conversions.rb +6 -4
  21. data/lib/active_support/core_ext/array/grouping.rb +9 -18
  22. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  23. data/lib/active_support/core_ext/array/wrap.rb +5 -4
  24. data/lib/active_support/core_ext/array.rb +1 -0
  25. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
  26. data/lib/active_support/core_ext/class/attribute.rb +10 -9
  27. data/lib/active_support/core_ext/class/subclasses.rb +3 -4
  28. data/lib/active_support/core_ext/class.rb +0 -1
  29. data/lib/active_support/core_ext/date/blank.rb +12 -0
  30. data/lib/active_support/core_ext/date/calculations.rb +1 -1
  31. data/lib/active_support/core_ext/date/conversions.rb +13 -6
  32. data/lib/active_support/core_ext/date.rb +1 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +109 -25
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  35. data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
  36. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  37. data/lib/active_support/core_ext/date_time/calculations.rb +36 -10
  38. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  39. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  40. data/lib/active_support/core_ext/date_time.rb +2 -1
  41. data/lib/active_support/core_ext/enumerable.rb +49 -5
  42. data/lib/active_support/core_ext/file/atomic.rb +30 -25
  43. data/lib/active_support/core_ext/hash/conversions.rb +23 -4
  44. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
  45. data/lib/active_support/core_ext/hash/except.rb +9 -8
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +23 -19
  48. data/lib/active_support/core_ext/hash/slice.rb +1 -1
  49. data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
  50. data/lib/active_support/core_ext/integer/time.rb +1 -16
  51. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
  53. data/lib/active_support/core_ext/kernel/reporting.rb +2 -83
  54. data/lib/active_support/core_ext/kernel.rb +0 -1
  55. data/lib/active_support/core_ext/load_error.rb +4 -2
  56. data/lib/active_support/core_ext/marshal.rb +12 -11
  57. data/lib/active_support/core_ext/module/aliasing.rb +6 -1
  58. data/lib/active_support/core_ext/module/anonymous.rb +10 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  62. data/lib/active_support/core_ext/module/concerning.rb +4 -4
  63. data/lib/active_support/core_ext/module/delegation.rb +35 -25
  64. data/lib/active_support/core_ext/module/deprecation.rb +2 -2
  65. data/lib/active_support/core_ext/module/introspection.rb +4 -0
  66. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -11
  67. data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
  68. data/lib/active_support/core_ext/module/remove_method.rb +23 -0
  69. data/lib/active_support/core_ext/module.rb +1 -0
  70. data/lib/active_support/core_ext/name_error.rb +15 -2
  71. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  72. data/lib/active_support/core_ext/numeric/conversions.rb +74 -64
  73. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  74. data/lib/active_support/core_ext/numeric/time.rb +24 -19
  75. data/lib/active_support/core_ext/numeric.rb +1 -0
  76. data/lib/active_support/core_ext/object/blank.rb +17 -5
  77. data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
  78. data/lib/active_support/core_ext/object/duplicable.rb +8 -13
  79. data/lib/active_support/core_ext/object/inclusion.rb +2 -2
  80. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  81. data/lib/active_support/core_ext/object/json.rb +15 -7
  82. data/lib/active_support/core_ext/object/to_query.rb +1 -1
  83. data/lib/active_support/core_ext/object/try.rb +68 -22
  84. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  85. data/lib/active_support/core_ext/object.rb +0 -1
  86. data/lib/active_support/core_ext/range/conversions.rb +18 -6
  87. data/lib/active_support/core_ext/range/each.rb +16 -18
  88. data/lib/active_support/core_ext/range/include_range.rb +20 -20
  89. data/lib/active_support/core_ext/securerandom.rb +23 -0
  90. data/lib/active_support/core_ext/string/access.rb +1 -1
  91. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  92. data/lib/active_support/core_ext/string/conversions.rb +4 -3
  93. data/lib/active_support/core_ext/string/filters.rb +5 -5
  94. data/lib/active_support/core_ext/string/inflections.rb +32 -5
  95. data/lib/active_support/core_ext/string/multibyte.rb +11 -7
  96. data/lib/active_support/core_ext/string/output_safety.rb +18 -16
  97. data/lib/active_support/core_ext/string/strip.rb +3 -6
  98. data/lib/active_support/core_ext/struct.rb +3 -6
  99. data/lib/active_support/core_ext/time/calculations.rb +36 -11
  100. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  101. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  102. data/lib/active_support/core_ext/time/marshal.rb +2 -29
  103. data/lib/active_support/core_ext/time/zones.rb +36 -4
  104. data/lib/active_support/core_ext/time.rb +1 -1
  105. data/lib/active_support/core_ext/uri.rb +1 -3
  106. data/lib/active_support/core_ext.rb +2 -1
  107. data/lib/active_support/dependencies/interlock.rb +51 -0
  108. data/lib/active_support/dependencies.rb +87 -95
  109. data/lib/active_support/deprecation/behaviors.rb +16 -2
  110. data/lib/active_support/deprecation/method_wrappers.rb +42 -16
  111. data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
  112. data/lib/active_support/deprecation/reporting.rb +23 -5
  113. data/lib/active_support/deprecation.rb +1 -1
  114. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  115. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  116. data/lib/active_support/duration.rb +55 -10
  117. data/lib/active_support/evented_file_update_checker.rb +194 -0
  118. data/lib/active_support/execution_wrapper.rb +117 -0
  119. data/lib/active_support/executor.rb +6 -0
  120. data/lib/active_support/file_update_checker.rb +23 -3
  121. data/lib/active_support/gem_version.rb +4 -4
  122. data/lib/active_support/hash_with_indifferent_access.rb +46 -13
  123. data/lib/active_support/i18n_railtie.rb +25 -4
  124. data/lib/active_support/inflector/inflections.rb +36 -5
  125. data/lib/active_support/inflector/methods.rb +97 -90
  126. data/lib/active_support/inflector/transliterate.rb +36 -21
  127. data/lib/active_support/json/decoding.rb +11 -10
  128. data/lib/active_support/json/encoding.rb +4 -49
  129. data/lib/active_support/key_generator.rb +7 -9
  130. data/lib/active_support/locale/en.yml +2 -0
  131. data/lib/active_support/log_subscriber/test_helper.rb +3 -3
  132. data/lib/active_support/log_subscriber.rb +1 -1
  133. data/lib/active_support/logger.rb +50 -1
  134. data/lib/active_support/logger_silence.rb +8 -4
  135. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  136. data/lib/active_support/message_encryptor.rb +4 -4
  137. data/lib/active_support/message_verifier.rb +70 -8
  138. data/lib/active_support/multibyte/chars.rb +13 -4
  139. data/lib/active_support/multibyte/unicode.rb +44 -21
  140. data/lib/active_support/notifications/fanout.rb +6 -6
  141. data/lib/active_support/notifications/instrumenter.rb +20 -2
  142. data/lib/active_support/notifications.rb +2 -2
  143. data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
  144. data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
  145. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
  146. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
  147. data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
  148. data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
  149. data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
  150. data/lib/active_support/number_helper.rb +90 -67
  151. data/lib/active_support/ordered_hash.rb +1 -1
  152. data/lib/active_support/ordered_options.rb +15 -1
  153. data/lib/active_support/per_thread_registry.rb +8 -3
  154. data/lib/active_support/rails.rb +2 -2
  155. data/lib/active_support/railtie.rb +6 -1
  156. data/lib/active_support/reloader.rb +129 -0
  157. data/lib/active_support/rescuable.rb +93 -47
  158. data/lib/active_support/security_utils.rb +7 -0
  159. data/lib/active_support/string_inquirer.rb +1 -1
  160. data/lib/active_support/subscriber.rb +5 -10
  161. data/lib/active_support/tagged_logging.rb +3 -1
  162. data/lib/active_support/test_case.rb +15 -29
  163. data/lib/active_support/testing/assertions.rb +15 -13
  164. data/lib/active_support/testing/autorun.rb +8 -1
  165. data/lib/active_support/testing/deprecation.rb +9 -8
  166. data/lib/active_support/testing/file_fixtures.rb +34 -0
  167. data/lib/active_support/testing/isolation.rb +22 -8
  168. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  169. data/lib/active_support/testing/stream.rb +42 -0
  170. data/lib/active_support/testing/time_helpers.rb +13 -10
  171. data/lib/active_support/time_with_zone.rb +135 -46
  172. data/lib/active_support/values/time_zone.rb +95 -47
  173. data/lib/active_support/values/unicode_tables.dat +0 -0
  174. data/lib/active_support/xml_mini/jdom.rb +7 -6
  175. data/lib/active_support/xml_mini/libxml.rb +2 -2
  176. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  177. data/lib/active_support/xml_mini/rexml.rb +7 -8
  178. data/lib/active_support/xml_mini.rb +22 -14
  179. data/lib/active_support.rb +20 -6
  180. metadata +33 -35
  181. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
  182. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  183. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  184. data/lib/active_support/core_ext/object/itself.rb +0 -15
  185. data/lib/active_support/core_ext/thread.rb +0 -86
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require 'active_support/core_ext/string/multibyte'
3
2
  require 'active_support/i18n'
4
3
 
@@ -58,7 +57,7 @@ module ActiveSupport
58
57
  # I18n.locale = :de
59
58
  # transliterate('Jürgen')
60
59
  # # => "Juergen"
61
- def transliterate(string, replacement = "?")
60
+ def transliterate(string, replacement = "?".freeze)
62
61
  I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
63
62
  ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
64
63
  :replacement => replacement)
@@ -67,31 +66,47 @@ module ActiveSupport
67
66
  # Replaces special characters in a string so that it may be used as part of
68
67
  # a 'pretty' URL.
69
68
  #
70
- # class Person
71
- # def to_param
72
- # "#{id}-#{name.parameterize}"
73
- # end
74
- # end
69
+ # parameterize("Donald E. Knuth") # => "donald-e-knuth"
70
+ # parameterize("^trés|Jolie-- ") # => "tres-jolie"
75
71
  #
76
- # @person = Person.find(1)
77
- # # => #<Person id: 1, name: "Donald E. Knuth">
72
+ # To use a custom separator, override the `separator` argument.
78
73
  #
79
- # <%= link_to(@person.name, person_path(@person)) %>
80
- # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
81
- def parameterize(string, sep = '-')
82
- # replace accented chars with their ascii equivalents
74
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
75
+ # parameterize("^trés|Jolie-- ", separator: '_') # => "tres_jolie"
76
+ #
77
+ # To preserve the case of the characters in a string, use the `preserve_case` argument.
78
+ #
79
+ # parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
80
+ # parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie"
81
+ #
82
+ def parameterize(string, sep = :unused, separator: '-', preserve_case: false)
83
+ unless sep == :unused
84
+ ActiveSupport::Deprecation.warn("Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '#{sep}'` instead.")
85
+ separator = sep
86
+ end
87
+ # Replace accented chars with their ASCII equivalents.
83
88
  parameterized_string = transliterate(string)
84
- # Turn unwanted chars into the separator
85
- parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
86
- unless sep.nil? || sep.empty?
87
- re_sep = Regexp.escape(sep)
89
+
90
+ # Turn unwanted chars into the separator.
91
+ parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
92
+
93
+ unless separator.nil? || separator.empty?
94
+ if separator == "-".freeze
95
+ re_duplicate_separator = /-{2,}/
96
+ re_leading_trailing_separator = /^-|-$/i
97
+ else
98
+ re_sep = Regexp.escape(separator)
99
+ re_duplicate_separator = /#{re_sep}{2,}/
100
+ re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
101
+ end
88
102
  # No more than one of the separator in a row.
89
- parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
103
+ parameterized_string.gsub!(re_duplicate_separator, separator)
90
104
  # Remove leading/trailing separator.
91
- parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
105
+ parameterized_string.gsub!(re_leading_trailing_separator, ''.freeze)
92
106
  end
93
- parameterized_string.downcase
107
+
108
+ parameterized_string.downcase! unless preserve_case
109
+ parameterized_string
94
110
  end
95
-
96
111
  end
97
112
  end
@@ -8,21 +8,16 @@ module ActiveSupport
8
8
 
9
9
  module JSON
10
10
  # matches YAML-formatted dates
11
- DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
12
-
11
+ DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
12
+ DATETIME_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)$/
13
+
13
14
  class << self
14
15
  # Parses a JSON string (JavaScript Object Notation) into a hash.
15
16
  # See http://www.json.org for more info.
16
17
  #
17
18
  # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
18
19
  # => {"team" => "rails", "players" => "36"}
19
- def decode(json, options = {})
20
- if options.present?
21
- raise ArgumentError, "In Rails 4.1, ActiveSupport::JSON.decode no longer " \
22
- "accepts an options hash for MultiJSON. MultiJSON reached its end of life " \
23
- "and has been removed."
24
- end
25
-
20
+ def decode(json)
26
21
  data = ::JSON.parse(json, quirks_mode: true)
27
22
 
28
23
  if ActiveSupport.parse_json_times
@@ -54,7 +49,13 @@ module ActiveSupport
54
49
  nil
55
50
  when DATE_REGEX
56
51
  begin
57
- DateTime.parse(data)
52
+ Date.parse(data)
53
+ rescue ArgumentError
54
+ data
55
+ end
56
+ when DATETIME_REGEX
57
+ begin
58
+ Time.zone.parse(data)
58
59
  rescue ArgumentError
59
60
  data
60
61
  end
@@ -6,7 +6,6 @@ module ActiveSupport
6
6
  delegate :use_standard_json_time_format, :use_standard_json_time_format=,
7
7
  :time_precision, :time_precision=,
8
8
  :escape_html_entities_in_json, :escape_html_entities_in_json=,
9
- :encode_big_decimal_as_string, :encode_big_decimal_as_string=,
10
9
  :json_encoder, :json_encoder=,
11
10
  :to => :'ActiveSupport::JSON::Encoding'
12
11
  end
@@ -58,6 +57,10 @@ module ActiveSupport
58
57
  super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
59
58
  end
60
59
  end
60
+
61
+ def to_s
62
+ self
63
+ end
61
64
  end
62
65
 
63
66
  # Mark these as private so we don't leak encoding-specific constructs
@@ -113,54 +116,6 @@ module ActiveSupport
113
116
  # Sets the encoder used by Rails to encode Ruby objects into JSON strings
114
117
  # in +Object#to_json+ and +ActiveSupport::JSON.encode+.
115
118
  attr_accessor :json_encoder
116
-
117
- def encode_big_decimal_as_string=(as_string)
118
- message = \
119
- "The JSON encoder in Rails 4.1 no longer supports encoding BigDecimals as JSON numbers. Instead, " \
120
- "the new encoder will always encode them as strings.\n\n" \
121
- "You are seeing this error because you have 'active_support.encode_big_decimal_as_string' in " \
122
- "your configuration file. If you have been setting this to true, you can safely remove it from " \
123
- "your configuration. Otherwise, you should add the 'activesupport-json_encoder' gem to your " \
124
- "Gemfile in order to restore this functionality."
125
-
126
- raise NotImplementedError, message
127
- end
128
-
129
- def encode_big_decimal_as_string
130
- message = \
131
- "The JSON encoder in Rails 4.1 no longer supports encoding BigDecimals as JSON numbers. Instead, " \
132
- "the new encoder will always encode them as strings.\n\n" \
133
- "You are seeing this error because you are trying to check the value of the related configuration, " \
134
- "`active_support.encode_big_decimal_as_string`. If your application depends on this option, you should " \
135
- "add the 'activesupport-json_encoder' gem to your Gemfile. For now, this option will always be true. " \
136
- "In the future, it will be removed from Rails, so you should stop checking its value."
137
-
138
- ActiveSupport::Deprecation.warn message
139
-
140
- true
141
- end
142
-
143
- # Deprecate CircularReferenceError
144
- def const_missing(name)
145
- if name == :CircularReferenceError
146
- message = "The JSON encoder in Rails 4.1 no longer offers protection from circular references. " \
147
- "You are seeing this warning because you are rescuing from (or otherwise referencing) " \
148
- "ActiveSupport::Encoding::CircularReferenceError. In the future, this error will be " \
149
- "removed from Rails. You should remove these rescue blocks from your code and ensure " \
150
- "that your data structures are free of circular references so they can be properly " \
151
- "serialized into JSON.\n\n" \
152
- "For example, the following Hash contains a circular reference to itself:\n" \
153
- " h = {}\n" \
154
- " h['circular'] = h\n" \
155
- "In this case, calling h.to_json would not work properly."
156
-
157
- ActiveSupport::Deprecation.warn message
158
-
159
- SystemStackError
160
- else
161
- super
162
- end
163
- end
164
119
  end
165
120
 
166
121
  self.use_standard_json_time_format = true
@@ -1,8 +1,8 @@
1
- require 'thread_safe'
1
+ require 'concurrent/map'
2
2
  require 'openssl'
3
3
 
4
4
  module ActiveSupport
5
- # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
5
+ # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2.
6
6
  # It can be used to derive a number of keys for various purposes from a given secret.
7
7
  # This lets Rails applications have a single secure secret, but avoid reusing that
8
8
  # key in multiple incompatible contexts.
@@ -24,18 +24,16 @@ module ActiveSupport
24
24
 
25
25
  # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
26
26
  # re-executing the key generation process when it's called using the same salt and
27
- # key_size
27
+ # key_size.
28
28
  class CachingKeyGenerator
29
29
  def initialize(key_generator)
30
30
  @key_generator = key_generator
31
- @cache_keys = ThreadSafe::Cache.new
31
+ @cache_keys = Concurrent::Map.new
32
32
  end
33
33
 
34
- # Returns a derived key suitable for use. The default key_size is chosen
35
- # to be compatible with the default settings of ActiveSupport::MessageVerifier.
36
- # i.e. OpenSSL::Digest::SHA1#block_length
37
- def generate_key(salt, key_size=64)
38
- @cache_keys["#{salt}#{key_size}"] ||= @key_generator.generate_key(salt, key_size)
34
+ # Returns a derived key suitable for use.
35
+ def generate_key(*args)
36
+ @cache_keys[args.join] ||= @key_generator.generate_key(*args)
39
37
  end
40
38
  end
41
39
 
@@ -106,6 +106,8 @@ en:
106
106
  mb: "MB"
107
107
  gb: "GB"
108
108
  tb: "TB"
109
+ pb: "PB"
110
+ eb: "EB"
109
111
  # Used in NumberHelper.number_to_human()
110
112
  decimal_units:
111
113
  format: "%n %u"
@@ -10,7 +10,7 @@ module ActiveSupport
10
10
  # class SyncLogSubscriberTest < ActiveSupport::TestCase
11
11
  # include ActiveSupport::LogSubscriber::TestHelper
12
12
  #
13
- # def setup
13
+ # setup do
14
14
  # ActiveRecord::LogSubscriber.attach_to(:active_record)
15
15
  # end
16
16
  #
@@ -33,7 +33,7 @@ module ActiveSupport
33
33
  # you can collect them doing @logger.logged(level), where level is the level
34
34
  # used in logging, like info, debug, warn and so on.
35
35
  module TestHelper
36
- def setup
36
+ def setup # :nodoc:
37
37
  @logger = MockLogger.new
38
38
  @notifier = ActiveSupport::Notifications::Fanout.new
39
39
 
@@ -44,7 +44,7 @@ module ActiveSupport
44
44
  ActiveSupport::Notifications.notifier = @notifier
45
45
  end
46
46
 
47
- def teardown
47
+ def teardown # :nodoc:
48
48
  set_logger(nil)
49
49
  ActiveSupport::Notifications.notifier = @old_notifier
50
50
  end
@@ -95,7 +95,7 @@ module ActiveSupport
95
95
  METHOD
96
96
  end
97
97
 
98
- # Set color by using a string or one of the defined constants. If a third
98
+ # Set color by using a symbol or one of the defined constants. If a third
99
99
  # option is set to +true+, it also adds bold to the string. This is based
100
100
  # on the Highline implementation and will automatically append CLEAR to the
101
101
  # end of the returned String.
@@ -1,11 +1,23 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
1
  require 'active_support/logger_silence'
2
+ require 'active_support/logger_thread_safe_level'
3
3
  require 'logger'
4
4
 
5
5
  module ActiveSupport
6
6
  class Logger < ::Logger
7
+ include ActiveSupport::LoggerThreadSafeLevel
7
8
  include LoggerSilence
8
9
 
10
+ # Returns true if the logger destination matches one of the sources
11
+ #
12
+ # logger = Logger.new(STDOUT)
13
+ # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
14
+ # # => true
15
+ def self.logger_outputs_to?(logger, *sources)
16
+ logdev = logger.instance_variable_get("@logdev")
17
+ logger_source = logdev.dev if logdev.respond_to?(:dev)
18
+ sources.any? { |source| source == logger_source }
19
+ end
20
+
9
21
  # Broadcasts logs to multiple loggers.
10
22
  def self.broadcast(logger) # :nodoc:
11
23
  Module.new do
@@ -38,12 +50,49 @@ module ActiveSupport
38
50
  logger.level = level
39
51
  super(level)
40
52
  end
53
+
54
+ define_method(:local_level=) do |level|
55
+ logger.local_level = level if logger.respond_to?(:local_level=)
56
+ super(level) if respond_to?(:local_level=)
57
+ end
58
+
59
+ define_method(:silence) do |level = Logger::ERROR, &block|
60
+ if logger.respond_to?(:silence)
61
+ logger.silence(level) do
62
+ if respond_to?(:silence)
63
+ super(level, &block)
64
+ else
65
+ block.call(self)
66
+ end
67
+ end
68
+ else
69
+ if respond_to?(:silence)
70
+ super(level, &block)
71
+ else
72
+ block.call(self)
73
+ end
74
+ end
75
+ end
41
76
  end
42
77
  end
43
78
 
44
79
  def initialize(*args)
45
80
  super
46
81
  @formatter = SimpleFormatter.new
82
+ after_initialize if respond_to? :after_initialize
83
+ end
84
+
85
+ def add(severity, message = nil, progname = nil, &block)
86
+ return true if @logdev.nil? || (severity || UNKNOWN) < level
87
+ super
88
+ end
89
+
90
+ Logger::Severity.constants.each do |severity|
91
+ class_eval(<<-EOT, __FILE__, __LINE__ + 1)
92
+ def #{severity.downcase}? # def debug?
93
+ Logger::#{severity} >= level # DEBUG >= level
94
+ end # end
95
+ EOT
47
96
  end
48
97
 
49
98
  # Simple formatter which only displays the message.
@@ -1,8 +1,10 @@
1
1
  require 'active_support/concern'
2
+ require 'active_support/core_ext/module/attribute_accessors'
3
+ require 'concurrent'
2
4
 
3
5
  module LoggerSilence
4
6
  extend ActiveSupport::Concern
5
-
7
+
6
8
  included do
7
9
  cattr_accessor :silencer
8
10
  self.silencer = true
@@ -12,13 +14,15 @@ module LoggerSilence
12
14
  def silence(temporary_level = Logger::ERROR)
13
15
  if silencer
14
16
  begin
15
- old_logger_level, self.level = level, temporary_level
17
+ old_local_level = local_level
18
+ self.local_level = temporary_level
19
+
16
20
  yield self
17
21
  ensure
18
- self.level = old_logger_level
22
+ self.local_level = old_local_level
19
23
  end
20
24
  else
21
25
  yield self
22
26
  end
23
27
  end
24
- end
28
+ end
@@ -0,0 +1,31 @@
1
+ require 'active_support/concern'
2
+
3
+ module ActiveSupport
4
+ module LoggerThreadSafeLevel # :nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ def after_initialize
8
+ @local_levels = Concurrent::Map.new(initial_capacity: 2)
9
+ end
10
+
11
+ def local_log_id
12
+ Thread.current.__id__
13
+ end
14
+
15
+ def local_level
16
+ @local_levels[local_log_id]
17
+ end
18
+
19
+ def local_level=(level)
20
+ if level
21
+ @local_levels[local_log_id] = level
22
+ else
23
+ @local_levels.delete(local_log_id)
24
+ end
25
+ end
26
+
27
+ def level
28
+ local_level || super
29
+ end
30
+ end
31
+ end
@@ -34,8 +34,8 @@ module ActiveSupport
34
34
  # Initialize a new MessageEncryptor. +secret+ must be at least as long as
35
35
  # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
36
36
  # bits. If you are using a user-entered secret, you can generate a suitable
37
- # key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
38
- # similar.
37
+ # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
38
+ # derivation function.
39
39
  #
40
40
  # Options:
41
41
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
@@ -82,7 +82,7 @@ module ActiveSupport
82
82
 
83
83
  def _decrypt(encrypted_message)
84
84
  cipher = new_cipher
85
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
85
+ encrypted_data, iv = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)}
86
86
 
87
87
  cipher.decrypt
88
88
  cipher.key = @secret
@@ -97,7 +97,7 @@ module ActiveSupport
97
97
  end
98
98
 
99
99
  def new_cipher
100
- OpenSSL::Cipher::Cipher.new(@cipher)
100
+ OpenSSL::Cipher.new(@cipher)
101
101
  end
102
102
 
103
103
  def verifier
@@ -15,7 +15,7 @@ module ActiveSupport
15
15
  # In the authentication filter:
16
16
  #
17
17
  # id, time = @verifier.verify(cookies[:remember_me])
18
- # if time < Time.now
18
+ # if Time.now < time
19
19
  # self.current_user = User.find(id)
20
20
  # end
21
21
  #
@@ -24,6 +24,12 @@ module ActiveSupport
24
24
  # hash upon initialization:
25
25
  #
26
26
  # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)
27
+ #
28
+ # +MessageVerifier+ creates HMAC signatures using SHA1 hash algorithm by default.
29
+ # If you want to use a different hash algorithm, you can change it by providing
30
+ # `:digest` key as an option while initializing the verifier:
31
+ #
32
+ # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256')
27
33
  class MessageVerifier
28
34
  class InvalidSignature < StandardError; end
29
35
 
@@ -34,22 +40,78 @@ module ActiveSupport
34
40
  @serializer = options[:serializer] || Marshal
35
41
  end
36
42
 
37
- def verify(signed_message)
38
- raise InvalidSignature if signed_message.blank?
43
+ # Checks if a signed message could have been generated by signing an object
44
+ # with the +MessageVerifier+'s secret.
45
+ #
46
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
47
+ # signed_message = verifier.generate 'a private message'
48
+ # verifier.valid_message?(signed_message) # => true
49
+ #
50
+ # tampered_message = signed_message.chop # editing the message invalidates the signature
51
+ # verifier.valid_message?(tampered_message) # => false
52
+ def valid_message?(signed_message)
53
+ return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
54
+
55
+ data, digest = signed_message.split("--".freeze)
56
+ data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
57
+ end
39
58
 
40
- data, digest = signed_message.split("--")
41
- if data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
59
+ # Decodes the signed message using the +MessageVerifier+'s secret.
60
+ #
61
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
62
+ #
63
+ # signed_message = verifier.generate 'a private message'
64
+ # verifier.verified(signed_message) # => 'a private message'
65
+ #
66
+ # Returns +nil+ if the message was not signed with the same secret.
67
+ #
68
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
69
+ # other_verifier.verified(signed_message) # => nil
70
+ #
71
+ # Returns +nil+ if the message is not Base64-encoded.
72
+ #
73
+ # invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d"
74
+ # verifier.verified(invalid_message) # => nil
75
+ #
76
+ # Raises any error raised while decoding the signed message.
77
+ #
78
+ # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
79
+ # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
80
+ def verified(signed_message)
81
+ if valid_message?(signed_message)
42
82
  begin
83
+ data = signed_message.split("--".freeze)[0]
43
84
  @serializer.load(decode(data))
44
85
  rescue ArgumentError => argument_error
45
- raise InvalidSignature if argument_error.message =~ %r{invalid base64}
86
+ return if argument_error.message =~ %r{invalid base64}
46
87
  raise
47
88
  end
48
- else
49
- raise InvalidSignature
50
89
  end
51
90
  end
52
91
 
92
+ # Decodes the signed message using the +MessageVerifier+'s secret.
93
+ #
94
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
95
+ # signed_message = verifier.generate 'a private message'
96
+ #
97
+ # verifier.verify(signed_message) # => 'a private message'
98
+ #
99
+ # Raises +InvalidSignature+ if the message was not signed with the same
100
+ # secret or was not Base64-encoded.
101
+ #
102
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
103
+ # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
104
+ def verify(signed_message)
105
+ verified(signed_message) || raise(InvalidSignature)
106
+ end
107
+
108
+ # Generates a signed message for the provided value.
109
+ #
110
+ # The message is signed with the +MessageVerifier+'s secret. Without knowing
111
+ # the secret, the original value cannot be extracted from the message.
112
+ #
113
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
114
+ # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
53
115
  def generate(value)
54
116
  data = encode(@serializer.dump(value))
55
117
  "#{data}--#{generate_digest(data)}"
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require 'active_support/json'
3
2
  require 'active_support/core_ext/string/access'
4
3
  require 'active_support/core_ext/string/behavior'
@@ -86,10 +85,20 @@ module ActiveSupport #:nodoc:
86
85
  @wrapped_string.split(*args).map { |i| self.class.new(i) }
87
86
  end
88
87
 
89
- # Works like like <tt>String#slice!</tt>, but returns an instance of
90
- # Chars, or nil if the string was not modified.
88
+ # Works like <tt>String#slice!</tt>, but returns an instance of
89
+ # Chars, or nil if the string was not modified. The string will not be
90
+ # modified if the range given is out of bounds
91
+ #
92
+ # string = 'Welcome'
93
+ # string.mb_chars.slice!(3) # => #<ActiveSupport::Multibyte::Chars:0x000000038109b8 @wrapped_string="c">
94
+ # string # => 'Welome'
95
+ # string.mb_chars.slice!(0..3) # => #<ActiveSupport::Multibyte::Chars:0x00000002eb80a0 @wrapped_string="Welo">
96
+ # string # => 'me'
91
97
  def slice!(*args)
92
- chars(@wrapped_string.slice!(*args))
98
+ string_sliced = @wrapped_string.slice!(*args)
99
+ if string_sliced
100
+ chars(string_sliced)
101
+ end
93
102
  end
94
103
 
95
104
  # Reverses all characters in the string.