activesupport 5.2.4.3 → 7.0.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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +244 -459
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +31 -5
  8. data/lib/active_support/benchmarkable.rb +3 -3
  9. data/lib/active_support/cache/file_store.rb +47 -41
  10. data/lib/active_support/cache/mem_cache_store.rb +151 -40
  11. data/lib/active_support/cache/memory_store.rb +68 -34
  12. data/lib/active_support/cache/null_store.rb +16 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +103 -101
  14. data/lib/active_support/cache/strategy/local_cache.rb +56 -64
  15. data/lib/active_support/cache.rb +333 -116
  16. data/lib/active_support/callbacks.rb +244 -128
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +72 -5
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  20. data/lib/active_support/concurrency/share_lock.rb +2 -3
  21. data/lib/active_support/configurable.rb +15 -16
  22. data/lib/active_support/configuration_file.rb +51 -0
  23. data/lib/active_support/core_ext/array/access.rb +15 -7
  24. data/lib/active_support/core_ext/array/conversions.rb +18 -17
  25. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  26. data/lib/active_support/core_ext/array/extract.rb +21 -0
  27. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  28. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  29. data/lib/active_support/core_ext/array.rb +2 -1
  30. data/lib/active_support/core_ext/benchmark.rb +2 -2
  31. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  32. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  33. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  34. data/lib/active_support/core_ext/date/blank.rb +1 -1
  35. data/lib/active_support/core_ext/date/calculations.rb +15 -14
  36. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  37. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  38. data/lib/active_support/core_ext/date.rb +1 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  42. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  43. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  44. data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
  45. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  46. data/lib/active_support/core_ext/date_time.rb +1 -0
  47. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  48. data/lib/active_support/core_ext/enumerable.rb +241 -76
  49. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  50. data/lib/active_support/core_ext/hash/conversions.rb +3 -4
  51. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  52. data/lib/active_support/core_ext/hash/except.rb +2 -2
  53. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  54. data/lib/active_support/core_ext/hash/keys.rb +2 -31
  55. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  56. data/lib/active_support/core_ext/hash.rb +1 -2
  57. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  58. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  59. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  60. data/lib/active_support/core_ext/kernel.rb +0 -1
  61. data/lib/active_support/core_ext/load_error.rb +1 -1
  62. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  63. data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
  64. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
  65. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  66. data/lib/active_support/core_ext/module/delegation.rb +70 -33
  67. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  68. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  69. data/lib/active_support/core_ext/module.rb +0 -1
  70. data/lib/active_support/core_ext/name_error.rb +23 -2
  71. data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
  72. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  73. data/lib/active_support/core_ext/numeric.rb +1 -1
  74. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  75. data/lib/active_support/core_ext/object/blank.rb +3 -4
  76. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  77. data/lib/active_support/core_ext/object/duplicable.rb +14 -110
  78. data/lib/active_support/core_ext/object/json.rb +44 -27
  79. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  80. data/lib/active_support/core_ext/object/try.rb +24 -14
  81. data/lib/active_support/core_ext/object/with_options.rb +21 -2
  82. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  83. data/lib/active_support/core_ext/pathname.rb +3 -0
  84. data/lib/active_support/core_ext/range/compare_range.rb +23 -27
  85. data/lib/active_support/core_ext/range/conversions.rb +32 -30
  86. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  87. data/lib/active_support/core_ext/range/each.rb +1 -2
  88. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  89. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  90. data/lib/active_support/core_ext/range.rb +1 -1
  91. data/lib/active_support/core_ext/regexp.rb +8 -5
  92. data/lib/active_support/core_ext/securerandom.rb +23 -3
  93. data/lib/active_support/core_ext/string/access.rb +5 -16
  94. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  95. data/lib/active_support/core_ext/string/filters.rb +42 -1
  96. data/lib/active_support/core_ext/string/inflections.rb +46 -7
  97. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  98. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  99. data/lib/active_support/core_ext/string/output_safety.rb +129 -20
  100. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  101. data/lib/active_support/core_ext/string/strip.rb +3 -1
  102. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  103. data/lib/active_support/core_ext/symbol.rb +3 -0
  104. data/lib/active_support/core_ext/time/calculations.rb +59 -10
  105. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  106. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  107. data/lib/active_support/core_ext/time/zones.rb +7 -22
  108. data/lib/active_support/core_ext/time.rb +1 -0
  109. data/lib/active_support/core_ext/uri.rb +3 -22
  110. data/lib/active_support/core_ext.rb +2 -1
  111. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  112. data/lib/active_support/current_attributes.rb +47 -16
  113. data/lib/active_support/dependencies/interlock.rb +10 -18
  114. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  115. data/lib/active_support/dependencies.rb +60 -715
  116. data/lib/active_support/deprecation/behaviors.rb +21 -5
  117. data/lib/active_support/deprecation/disallowed.rb +56 -0
  118. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  119. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +31 -8
  121. data/lib/active_support/deprecation/reporting.rb +50 -7
  122. data/lib/active_support/deprecation.rb +7 -2
  123. data/lib/active_support/descendants_tracker.rb +190 -34
  124. data/lib/active_support/digest.rb +5 -3
  125. data/lib/active_support/duration/iso8601_parser.rb +5 -7
  126. data/lib/active_support/duration/iso8601_serializer.rb +27 -15
  127. data/lib/active_support/duration.rb +149 -67
  128. data/lib/active_support/encrypted_configuration.rb +12 -5
  129. data/lib/active_support/encrypted_file.rb +23 -5
  130. data/lib/active_support/environment_inquirer.rb +20 -0
  131. data/lib/active_support/error_reporter.rb +117 -0
  132. data/lib/active_support/evented_file_update_checker.rb +85 -122
  133. data/lib/active_support/execution_context/test_helper.rb +13 -0
  134. data/lib/active_support/execution_context.rb +53 -0
  135. data/lib/active_support/execution_wrapper.rb +44 -21
  136. data/lib/active_support/executor/test_helper.rb +7 -0
  137. data/lib/active_support/file_update_checker.rb +0 -1
  138. data/lib/active_support/fork_tracker.rb +71 -0
  139. data/lib/active_support/gem_version.rb +5 -5
  140. data/lib/active_support/hash_with_indifferent_access.rb +73 -43
  141. data/lib/active_support/html_safe_translation.rb +43 -0
  142. data/lib/active_support/i18n.rb +2 -0
  143. data/lib/active_support/i18n_railtie.rb +15 -8
  144. data/lib/active_support/inflector/inflections.rb +25 -14
  145. data/lib/active_support/inflector/methods.rb +38 -71
  146. data/lib/active_support/inflector/transliterate.rb +47 -18
  147. data/lib/active_support/isolated_execution_state.rb +72 -0
  148. data/lib/active_support/json/decoding.rb +25 -26
  149. data/lib/active_support/json/encoding.rb +14 -6
  150. data/lib/active_support/key_generator.rb +23 -38
  151. data/lib/active_support/lazy_load_hooks.rb +19 -5
  152. data/lib/active_support/locale/en.rb +33 -0
  153. data/lib/active_support/locale/en.yml +8 -4
  154. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  155. data/lib/active_support/log_subscriber.rb +51 -11
  156. data/lib/active_support/logger.rb +6 -22
  157. data/lib/active_support/logger_silence.rb +11 -19
  158. data/lib/active_support/logger_thread_safe_level.rb +45 -10
  159. data/lib/active_support/message_encryptor.rb +20 -19
  160. data/lib/active_support/message_verifier.rb +53 -21
  161. data/lib/active_support/messages/metadata.rb +13 -4
  162. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  163. data/lib/active_support/messages/rotator.rb +10 -9
  164. data/lib/active_support/multibyte/chars.rb +17 -76
  165. data/lib/active_support/multibyte/unicode.rb +7 -331
  166. data/lib/active_support/multibyte.rb +1 -1
  167. data/lib/active_support/notifications/fanout.rb +163 -37
  168. data/lib/active_support/notifications/instrumenter.rb +90 -11
  169. data/lib/active_support/notifications.rb +88 -30
  170. data/lib/active_support/number_helper/number_converter.rb +6 -9
  171. data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
  172. data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
  173. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  174. data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -4
  175. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  176. data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
  177. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  178. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  179. data/lib/active_support/number_helper.rb +36 -12
  180. data/lib/active_support/option_merger.rb +15 -4
  181. data/lib/active_support/ordered_hash.rb +2 -2
  182. data/lib/active_support/ordered_options.rb +14 -4
  183. data/lib/active_support/parameter_filter.rb +138 -0
  184. data/lib/active_support/per_thread_registry.rb +6 -1
  185. data/lib/active_support/rails.rb +1 -10
  186. data/lib/active_support/railtie.rb +77 -5
  187. data/lib/active_support/reloader.rb +5 -6
  188. data/lib/active_support/rescuable.rb +8 -8
  189. data/lib/active_support/ruby_features.rb +7 -0
  190. data/lib/active_support/secure_compare_rotator.rb +51 -0
  191. data/lib/active_support/security_utils.rb +19 -12
  192. data/lib/active_support/string_inquirer.rb +2 -3
  193. data/lib/active_support/subscriber.rb +79 -46
  194. data/lib/active_support/tagged_logging.rb +58 -9
  195. data/lib/active_support/test_case.rb +79 -0
  196. data/lib/active_support/testing/assertions.rb +62 -11
  197. data/lib/active_support/testing/deprecation.rb +52 -2
  198. data/lib/active_support/testing/file_fixtures.rb +2 -0
  199. data/lib/active_support/testing/isolation.rb +4 -4
  200. data/lib/active_support/testing/method_call_assertions.rb +32 -5
  201. data/lib/active_support/testing/parallelization/server.rb +82 -0
  202. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  203. data/lib/active_support/testing/parallelization.rb +55 -0
  204. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  205. data/lib/active_support/testing/stream.rb +4 -7
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +60 -14
  208. data/lib/active_support/time_with_zone.rb +139 -64
  209. data/lib/active_support/values/time_zone.rb +66 -30
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +3 -4
  212. data/lib/active_support/xml_mini/libxml.rb +7 -7
  213. data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
  214. data/lib/active_support/xml_mini/nokogiri.rb +6 -6
  215. data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
  216. data/lib/active_support/xml_mini/rexml.rb +11 -4
  217. data/lib/active_support/xml_mini.rb +7 -14
  218. data/lib/active_support.rb +30 -1
  219. metadata +64 -35
  220. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  221. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  222. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  223. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  224. data/lib/active_support/core_ext/marshal.rb +0 -24
  225. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  226. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  227. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  228. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -5,6 +5,8 @@ require "active_support/i18n"
5
5
 
6
6
  module ActiveSupport
7
7
  module Inflector
8
+ ALLOWED_ENCODINGS_FOR_TRANSLITERATE = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030].freeze
9
+
8
10
  # Replaces non-ASCII characters with an ASCII approximation, or if none
9
11
  # exists, a replacement character which defaults to "?".
10
12
  #
@@ -51,20 +53,43 @@ module ActiveSupport
51
53
  #
52
54
  # Now you can have different transliterations for each locale:
53
55
  #
54
- # I18n.locale = :en
55
- # transliterate('Jürgen')
56
+ # transliterate('Jürgen', locale: :en)
56
57
  # # => "Jurgen"
57
58
  #
58
- # I18n.locale = :de
59
- # transliterate('Jürgen')
59
+ # transliterate('Jürgen', locale: :de)
60
60
  # # => "Juergen"
61
- def transliterate(string, replacement = "?".freeze)
61
+ #
62
+ # Transliteration is restricted to UTF-8, US-ASCII, and GB18030 strings.
63
+ # Other encodings will raise an ArgumentError.
64
+ def transliterate(string, replacement = "?", locale: nil)
65
+ string = string.dup if string.frozen?
62
66
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
67
+ raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
68
+
69
+ input_encoding = string.encoding
70
+
71
+ # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
72
+ # US-ASCII is given. This way we can let tidy_bytes handle the string
73
+ # in the same way as we do for UTF-8
74
+ string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII
75
+
76
+ # GB18030 is Unicode compatible but is not a direct mapping so needs to be
77
+ # transcoded. Using invalid/undef :replace will result in loss of data in
78
+ # the event of invalid characters, but since tidy_bytes will replace
79
+ # invalid/undef with a "?" we're safe to do the same beforehand
80
+ string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030
81
+
82
+ transliterated = I18n.transliterate(
83
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
84
+ replacement: replacement,
85
+ locale: locale
86
+ )
87
+
88
+ # Restore the string encoding of the input if it was not UTF-8.
89
+ # Apply invalid/undef :replace as tidy_bytes does
90
+ transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding
63
91
 
64
- I18n.transliterate(
65
- ActiveSupport::Multibyte::Unicode.normalize(
66
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
67
- replacement: replacement)
92
+ transliterated
68
93
  end
69
94
 
70
95
  # Replaces special characters in a string so that it may be used as part of
@@ -75,8 +100,8 @@ module ActiveSupport
75
100
  #
76
101
  # To use a custom separator, override the +separator+ argument.
77
102
  #
78
- # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
79
- # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
103
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
104
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
80
105
  #
81
106
  # To preserve the case of the characters in a string, use the +preserve_case+ argument.
82
107
  #
@@ -85,19 +110,23 @@ module ActiveSupport
85
110
  #
86
111
  # It preserves dashes and underscores unless they are used as separators:
87
112
  #
88
- # parameterize("^très|Jolie__ ") # => "tres-jolie__"
89
- # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
90
- # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
113
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
114
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
115
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
91
116
  #
92
- def parameterize(string, separator: "-", preserve_case: false)
117
+ # If the optional parameter +locale+ is specified,
118
+ # the word will be parameterized as a word of that language.
119
+ # By default, this parameter is set to <tt>nil</tt> and it will use
120
+ # the configured <tt>I18n.locale</tt>.
121
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
93
122
  # Replace accented chars with their ASCII equivalents.
94
- parameterized_string = transliterate(string)
123
+ parameterized_string = transliterate(string, locale: locale)
95
124
 
96
125
  # Turn unwanted chars into the separator.
97
126
  parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
98
127
 
99
128
  unless separator.nil? || separator.empty?
100
- if separator == "-".freeze
129
+ if separator == "-"
101
130
  re_duplicate_separator = /-{2,}/
102
131
  re_leading_trailing_separator = /^-|-$/i
103
132
  else
@@ -108,7 +137,7 @@ module ActiveSupport
108
137
  # No more than one of the separator in a row.
109
138
  parameterized_string.gsub!(re_duplicate_separator, separator)
110
139
  # Remove leading/trailing separator.
111
- parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
140
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
112
141
  end
113
142
 
114
143
  parameterized_string.downcase! unless preserve_case
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fiber"
4
+
5
+ module ActiveSupport
6
+ module IsolatedExecutionState # :nodoc:
7
+ @isolation_level = :thread
8
+
9
+ Thread.attr_accessor :active_support_execution_state
10
+ Fiber.attr_accessor :active_support_execution_state
11
+
12
+ class << self
13
+ attr_reader :isolation_level
14
+
15
+ def isolation_level=(level)
16
+ unless %i(thread fiber).include?(level)
17
+ raise ArgumentError, "isolation_level must be `:thread` or `:fiber`, got: `#{level.inspect}`"
18
+ end
19
+
20
+ if level != isolation_level
21
+ clear
22
+ singleton_class.alias_method(:current, "current_#{level}")
23
+ singleton_class.send(:private, :current)
24
+ @isolation_level = level
25
+ end
26
+ end
27
+
28
+ def unique_id
29
+ self[:__id__] ||= Object.new
30
+ end
31
+
32
+ def [](key)
33
+ current[key]
34
+ end
35
+
36
+ def []=(key, value)
37
+ current[key] = value
38
+ end
39
+
40
+ def key?(key)
41
+ current.key?(key)
42
+ end
43
+
44
+ def delete(key)
45
+ current.delete(key)
46
+ end
47
+
48
+ def clear
49
+ current.clear
50
+ end
51
+
52
+ def share_with(other)
53
+ # Action Controller streaming spawns a new thread and copy thread locals.
54
+ # We do the same here for backward compatibility, but this is very much a hack
55
+ # and streaming should be rethought.
56
+ context = @isolation_level == :thread ? Thread.current : Fiber.current
57
+ context.active_support_execution_state = other.active_support_execution_state.dup
58
+ end
59
+
60
+ private
61
+ def current_thread
62
+ Thread.current.active_support_execution_state ||= {}
63
+ end
64
+
65
+ def current_fiber
66
+ Fiber.current.active_support_execution_state ||= {}
67
+ end
68
+
69
+ alias_method :current, :current_thread
70
+ end
71
+ end
72
+ end
@@ -10,8 +10,8 @@ module ActiveSupport
10
10
 
11
11
  module JSON
12
12
  # matches YAML-formatted dates
13
- DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
14
- 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
+ DATE_REGEX = /\A\d{4}-\d{2}-\d{2}\z/
14
+ DATETIME_REGEX = /\A(?:\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})?)?)\z/
15
15
 
16
16
  class << self
17
17
  # Parses a JSON string (JavaScript Object Notation) into a hash.
@@ -44,33 +44,32 @@ module ActiveSupport
44
44
  end
45
45
 
46
46
  private
47
-
48
- def convert_dates_from(data)
49
- case data
50
- when nil
51
- nil
52
- when DATE_REGEX
53
- begin
54
- Date.parse(data)
55
- rescue ArgumentError
56
- data
57
- end
58
- when DATETIME_REGEX
59
- begin
60
- Time.zone.parse(data)
61
- rescue ArgumentError
47
+ def convert_dates_from(data)
48
+ case data
49
+ when nil
50
+ nil
51
+ when DATE_REGEX
52
+ begin
53
+ Date.parse(data)
54
+ rescue ArgumentError
55
+ data
56
+ end
57
+ when DATETIME_REGEX
58
+ begin
59
+ Time.zone.parse(data)
60
+ rescue ArgumentError
61
+ data
62
+ end
63
+ when Array
64
+ data.map! { |d| convert_dates_from(d) }
65
+ when Hash
66
+ data.transform_values! do |value|
67
+ convert_dates_from(value)
68
+ end
69
+ else
62
70
  data
63
71
  end
64
- when Array
65
- data.map! { |d| convert_dates_from(d) }
66
- when Hash
67
- data.each do |key, value|
68
- data[key] = convert_dates_from(value)
69
- end
70
- else
71
- data
72
72
  end
73
- end
74
73
  end
75
74
  end
76
75
  end
@@ -22,8 +22,8 @@ module ActiveSupport
22
22
  Encoding.json_encoder.new(options).encode(value)
23
23
  end
24
24
 
25
- module Encoding #:nodoc:
26
- class JSONGemEncoder #:nodoc:
25
+ module Encoding # :nodoc:
26
+ class JSONGemEncoder # :nodoc:
27
27
  attr_reader :options
28
28
 
29
29
  def initialize(options = nil)
@@ -51,12 +51,16 @@ module ActiveSupport
51
51
  ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
52
52
 
53
53
  # This class wraps all the strings we see and does the extra escaping
54
- class EscapedString < String #:nodoc:
54
+ class EscapedString < String # :nodoc:
55
55
  def to_json(*)
56
56
  if Encoding.escape_html_entities_in_json
57
- super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
57
+ s = super
58
+ s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
59
+ s
58
60
  else
59
- super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
61
+ s = super
62
+ s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
63
+ s
60
64
  end
61
65
  end
62
66
 
@@ -89,7 +93,11 @@ module ActiveSupport
89
93
  when Numeric, NilClass, TrueClass, FalseClass
90
94
  value.as_json
91
95
  when Hash
92
- Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
96
+ result = {}
97
+ value.each do |k, v|
98
+ result[jsonify(k)] = jsonify(v)
99
+ end
100
+ result
93
101
  when Array
94
102
  value.map { |v| jsonify(v) }
95
103
  else
@@ -9,24 +9,41 @@ module ActiveSupport
9
9
  # This lets Rails applications have a single secure secret, but avoid reusing that
10
10
  # key in multiple incompatible contexts.
11
11
  class KeyGenerator
12
+ class << self
13
+ def hash_digest_class=(klass)
14
+ if klass.kind_of?(Class) && klass < OpenSSL::Digest
15
+ @hash_digest_class = klass
16
+ else
17
+ raise ArgumentError, "#{klass} is expected to be an OpenSSL::Digest subclass"
18
+ end
19
+ end
20
+
21
+ def hash_digest_class
22
+ @hash_digest_class ||= OpenSSL::Digest::SHA1
23
+ end
24
+ end
25
+
12
26
  def initialize(secret, options = {})
13
27
  @secret = secret
14
28
  # The default iterations are higher than required for our key derivation uses
15
29
  # on the off chance someone uses this for password storage
16
30
  @iterations = options[:iterations] || 2**16
31
+ # Also allow configuration here so people can use this to build a rotation
32
+ # scheme when switching the digest class.
33
+ @hash_digest_class = options[:hash_digest_class] || self.class.hash_digest_class
17
34
  end
18
35
 
19
- # Returns a derived key suitable for use. The default key_size is chosen
36
+ # Returns a derived key suitable for use. The default +key_size+ is chosen
20
37
  # to be compatible with the default settings of ActiveSupport::MessageVerifier.
21
- # i.e. OpenSSL::Digest::SHA1#block_length
38
+ # i.e. <tt>OpenSSL::Digest::SHA1#block_length</tt>
22
39
  def generate_key(salt, key_size = 64)
23
- OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
40
+ OpenSSL::PKCS5.pbkdf2_hmac(@secret, salt, @iterations, key_size, @hash_digest_class.new)
24
41
  end
25
42
  end
26
43
 
27
44
  # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
28
- # re-executing the key generation process when it's called using the same salt and
29
- # key_size.
45
+ # re-executing the key generation process when it's called using the same +salt+ and
46
+ # +key_size+.
30
47
  class CachingKeyGenerator
31
48
  def initialize(key_generator)
32
49
  @key_generator = key_generator
@@ -35,39 +52,7 @@ module ActiveSupport
35
52
 
36
53
  # Returns a derived key suitable for use.
37
54
  def generate_key(*args)
38
- @cache_keys[args.join] ||= @key_generator.generate_key(*args)
55
+ @cache_keys[args.join("|")] ||= @key_generator.generate_key(*args)
39
56
  end
40
57
  end
41
-
42
- class LegacyKeyGenerator # :nodoc:
43
- SECRET_MIN_LENGTH = 30 # Characters
44
-
45
- def initialize(secret)
46
- ensure_secret_secure(secret)
47
- @secret = secret
48
- end
49
-
50
- def generate_key(salt)
51
- @secret
52
- end
53
-
54
- private
55
-
56
- # To prevent users from using something insecure like "Password" we make sure that the
57
- # secret they've provided is at least 30 characters in length.
58
- def ensure_secret_secure(secret)
59
- if secret.blank?
60
- raise ArgumentError, "A secret is required to generate an integrity hash " \
61
- "for cookie session data. Set a secret_key_base of at least " \
62
- "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`."
63
- end
64
-
65
- if secret.length < SECRET_MIN_LENGTH
66
- raise ArgumentError, "Secret should be something secure, " \
67
- "like \"#{SecureRandom.hex(16)}\". The value you " \
68
- "provided, \"#{secret}\", is shorter than the minimum length " \
69
- "of #{SECRET_MIN_LENGTH} characters."
70
- end
71
- end
72
- end
73
58
  end
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- # lazy_load_hooks allows Rails to lazily load a lot of components and thus
4
+ # LazyLoadHooks allows Rails to lazily load a lot of components and thus
5
5
  # making the app boot faster. Because of this feature now there is no need to
6
6
  # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
7
7
  # configuration. Instead a hook is registered that applies configuration once
8
8
  # <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
9
9
  # used as example but this feature can be applied elsewhere too.
10
10
  #
11
- # Here is an example where +on_load+ method is called to register a hook.
11
+ # Here is an example where on_load method is called to register a hook.
12
12
  #
13
13
  # initializer 'active_record.initialize_timezone' do
14
14
  # ActiveSupport.on_load(:active_record) do
@@ -18,10 +18,14 @@ module ActiveSupport
18
18
  # end
19
19
  #
20
20
  # When the entirety of +ActiveRecord::Base+ has been
21
- # evaluated then +run_load_hooks+ is invoked. The very last line of
21
+ # evaluated then run_load_hooks is invoked. The very last line of
22
22
  # +ActiveRecord::Base+ is:
23
23
  #
24
24
  # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
25
+ #
26
+ # run_load_hooks will then execute all the hooks that were registered
27
+ # with the on_load method. In the case of the above example, it will
28
+ # execute the block of code that is in the +initializer+.
25
29
  module LazyLoadHooks
26
30
  def self.extended(base) # :nodoc:
27
31
  base.class_eval do
@@ -46,6 +50,13 @@ module ActiveSupport
46
50
  @load_hooks[name] << [block, options]
47
51
  end
48
52
 
53
+ # Executes all blocks registered to +name+ via on_load, using +base+ as the
54
+ # evaluation context.
55
+ #
56
+ # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
57
+ #
58
+ # In the case of the above example, it will execute all hooks registered
59
+ # for +:active_record+ within the class +ActiveRecord::Base+.
49
60
  def run_load_hooks(name, base = Object)
50
61
  @loaded[name] << base
51
62
  @load_hooks[name].each do |hook, options|
@@ -54,7 +65,6 @@ module ActiveSupport
54
65
  end
55
66
 
56
67
  private
57
-
58
68
  def with_execution_control(name, block, once)
59
69
  unless @run_once[name].include?(block)
60
70
  @run_once[name] << block if once
@@ -68,7 +78,11 @@ module ActiveSupport
68
78
  if options[:yield]
69
79
  block.call(base)
70
80
  else
71
- base.instance_eval(&block)
81
+ if base.is_a?(Module)
82
+ base.class_eval(&block)
83
+ else
84
+ base.instance_eval(&block)
85
+ end
72
86
  end
73
87
  end
74
88
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ {
4
+ en: {
5
+ number: {
6
+ nth: {
7
+ ordinals: lambda do |_key, options|
8
+ number = options[:number]
9
+ case number
10
+ when 1; "st"
11
+ when 2; "nd"
12
+ when 3; "rd"
13
+ when 4, 5, 6, 7, 8, 9, 10, 11, 12, 13; "th"
14
+ else
15
+ num_modulo = number.to_i.abs % 100
16
+ num_modulo %= 10 if num_modulo > 13
17
+ case num_modulo
18
+ when 1; "st"
19
+ when 2; "nd"
20
+ when 3; "rd"
21
+ else "th"
22
+ end
23
+ end
24
+ end,
25
+
26
+ ordinalized: lambda do |_key, options|
27
+ number = options[:number]
28
+ "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
29
+ end
30
+ }
31
+ }
32
+ }
33
+ }
@@ -44,22 +44,25 @@ en:
44
44
  delimiter: ","
45
45
  # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
46
46
  precision: 3
47
+ # Determine how rounding is performed (see BigDecimal::mode)
48
+ round_mode: default
47
49
  # If set to true, precision will mean the number of significant digits instead
48
50
  # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
49
51
  significant: false
50
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
52
+ # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
51
53
  strip_insignificant_zeros: false
52
54
 
53
55
  # Used in NumberHelper.number_to_currency()
54
56
  currency:
55
57
  format:
56
- # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
58
+ # Where is the currency sign? %u is the currency unit, %n is the number (default: $5.00)
57
59
  format: "%u%n"
58
60
  unit: "$"
59
- # These five are to override number.format and are optional
61
+ # These six are to override number.format and are optional
60
62
  separator: "."
61
63
  delimiter: ","
62
64
  precision: 2
65
+ # round_mode:
63
66
  significant: false
64
67
  strip_insignificant_zeros: false
65
68
 
@@ -87,10 +90,11 @@ en:
87
90
  # Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
88
91
  human:
89
92
  format:
90
- # These five are to override number.format and are optional
93
+ # These six are to override number.format and are optional
91
94
  # separator:
92
95
  delimiter: ""
93
96
  precision: 3
97
+ # round_mode:
94
98
  significant: true
95
99
  strip_insignificant_zeros: true
96
100
  # Used in number_to_human_size()
@@ -27,13 +27,13 @@ module ActiveSupport
27
27
  #
28
28
  # All you need to do is to ensure that your log subscriber is added to
29
29
  # Rails::Subscriber, as in the second line of the code above. The test
30
- # helpers are responsible for setting up the queue, subscriptions and
30
+ # helpers are responsible for setting up the queue and subscriptions, and
31
31
  # turning colors in logs off.
32
32
  #
33
33
  # The messages are available in the @logger instance, which is a logger with
34
34
  # limited powers (it actually does not send anything to your output), and
35
35
  # you can collect them doing @logger.logged(level), where level is the level
36
- # used in logging, like info, debug, warn and so on.
36
+ # used in logging, like info, debug, warn, and so on.
37
37
  module TestHelper
38
38
  def setup # :nodoc:
39
39
  @logger = MockLogger.new
@@ -5,7 +5,7 @@ require "active_support/core_ext/class/attribute"
5
5
  require "active_support/subscriber"
6
6
 
7
7
  module ActiveSupport
8
- # ActiveSupport::LogSubscriber is an object set to consume
8
+ # <tt>ActiveSupport::LogSubscriber</tt> is an object set to consume
9
9
  # ActiveSupport::Notifications with the sole purpose of logging them.
10
10
  # The log subscriber dispatches notifications to a registered object based
11
11
  # on its given namespace.
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  # module ActiveRecord
17
17
  # class LogSubscriber < ActiveSupport::LogSubscriber
18
18
  # def sql(event)
19
- # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
19
+ # info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
20
20
  # end
21
21
  # end
22
22
  # end
@@ -29,13 +29,39 @@ module ActiveSupport
29
29
  # subscriber, the line above should be called after your
30
30
  # <tt>ActiveRecord::LogSubscriber</tt> definition.
31
31
  #
32
- # After configured, whenever a "sql.active_record" notification is published,
33
- # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
34
- # the sql method.
32
+ # A logger also needs to be set with <tt>ActiveRecord::LogSubscriber.logger=</tt>.
33
+ # This is assigned automatically in a Rails environment.
34
+ #
35
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
36
+ # it will properly dispatch the event
37
+ # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
38
+ #
39
+ # Being an ActiveSupport::Notifications consumer,
40
+ # <tt>ActiveSupport::LogSubscriber</tt> exposes a simple interface to check if
41
+ # instrumented code raises an exception. It is common to log a different
42
+ # message in case of an error, and this can be achieved by extending
43
+ # the previous example:
44
+ #
45
+ # module ActiveRecord
46
+ # class LogSubscriber < ActiveSupport::LogSubscriber
47
+ # def sql(event)
48
+ # exception = event.payload[:exception]
49
+ #
50
+ # if exception
51
+ # exception_object = event.payload[:exception_object]
52
+ #
53
+ # error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \
54
+ # "(#{exception_object.backtrace.first})"
55
+ # else
56
+ # # standard logger code
57
+ # end
58
+ # end
59
+ # end
60
+ # end
35
61
  #
36
62
  # Log subscriber also has some helpers to deal with logging and automatically
37
- # flushes all logs when the request finishes (via action_dispatch.callback
38
- # notification) in a Rails environment.
63
+ # flushes all logs when the request finishes
64
+ # (via <tt>action_dispatch.callback</tt> notification) in a Rails environment.
39
65
  class LogSubscriber < Subscriber
40
66
  # Embed in a String to clear all previous ANSI sequences.
41
67
  CLEAR = "\e[0m"
@@ -70,6 +96,11 @@ module ActiveSupport
70
96
  def flush_all!
71
97
  logger.flush if logger.respond_to?(:flush)
72
98
  end
99
+
100
+ private
101
+ def fetch_public_methods(subscriber, inherit_all)
102
+ subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true)
103
+ end
73
104
  end
74
105
 
75
106
  def logger
@@ -83,13 +114,16 @@ module ActiveSupport
83
114
  def finish(name, id, payload)
84
115
  super if logger
85
116
  rescue => e
86
- if logger
87
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
88
- end
117
+ log_exception(name, e)
89
118
  end
90
119
 
91
- private
120
+ def publish_event(event)
121
+ super if logger
122
+ rescue => e
123
+ log_exception(event.name, e)
124
+ end
92
125
 
126
+ private
93
127
  %w(info debug warn error fatal unknown).each do |level|
94
128
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
95
129
  def #{level}(progname = nil, &block)
@@ -108,5 +142,11 @@ module ActiveSupport
108
142
  bold = bold ? BOLD : ""
109
143
  "#{bold}#{color}#{text}#{CLEAR}"
110
144
  end
145
+
146
+ def log_exception(name, e)
147
+ if logger
148
+ logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
149
+ end
150
+ end
111
151
  end
112
152
  end