activesupport 6.0.3.7 → 7.0.0

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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +220 -533
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/actionable_error.rb +1 -1
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +3 -3
  8. data/lib/active_support/benchmarkable.rb +3 -3
  9. data/lib/active_support/cache/file_store.rb +18 -11
  10. data/lib/active_support/cache/mem_cache_store.rb +143 -37
  11. data/lib/active_support/cache/memory_store.rb +56 -28
  12. data/lib/active_support/cache/null_store.rb +10 -2
  13. data/lib/active_support/cache/redis_cache_store.rb +63 -88
  14. data/lib/active_support/cache/strategy/local_cache.rb +46 -57
  15. data/lib/active_support/cache.rb +273 -82
  16. data/lib/active_support/callbacks.rb +226 -118
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +49 -5
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  20. data/lib/active_support/concurrency/share_lock.rb +2 -2
  21. data/lib/active_support/configurable.rb +9 -6
  22. data/lib/active_support/configuration_file.rb +51 -0
  23. data/lib/active_support/core_ext/array/access.rb +1 -5
  24. data/lib/active_support/core_ext/array/conversions.rb +9 -7
  25. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  27. data/lib/active_support/core_ext/array.rb +1 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  31. data/lib/active_support/core_ext/class/subclasses.rb +21 -40
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  34. data/lib/active_support/core_ext/date/conversions.rb +5 -4
  35. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  36. data/lib/active_support/core_ext/date.rb +1 -0
  37. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  38. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  39. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  42. data/lib/active_support/core_ext/date_time.rb +1 -0
  43. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  44. data/lib/active_support/core_ext/enumerable.rb +139 -15
  45. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/except.rb +1 -1
  49. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  50. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/load_error.rb +1 -1
  53. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  54. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  55. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  56. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  57. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  58. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  59. data/lib/active_support/core_ext/name_error.rb +23 -2
  60. data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
  61. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  62. data/lib/active_support/core_ext/numeric.rb +1 -0
  63. data/lib/active_support/core_ext/object/blank.rb +2 -2
  64. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  65. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  66. data/lib/active_support/core_ext/object/json.rb +42 -26
  67. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  68. data/lib/active_support/core_ext/object/try.rb +20 -20
  69. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  70. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  71. data/lib/active_support/core_ext/pathname.rb +3 -0
  72. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  73. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  74. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  75. data/lib/active_support/core_ext/range/each.rb +1 -1
  76. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  77. data/lib/active_support/core_ext/range.rb +1 -1
  78. data/lib/active_support/core_ext/regexp.rb +8 -1
  79. data/lib/active_support/core_ext/string/access.rb +5 -24
  80. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  81. data/lib/active_support/core_ext/string/filters.rb +1 -1
  82. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  83. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  84. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  85. data/lib/active_support/core_ext/string/output_safety.rb +69 -45
  86. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  87. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  88. data/lib/active_support/core_ext/symbol.rb +3 -0
  89. data/lib/active_support/core_ext/time/calculations.rb +26 -6
  90. data/lib/active_support/core_ext/time/conversions.rb +6 -3
  91. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  92. data/lib/active_support/core_ext/time/zones.rb +4 -19
  93. data/lib/active_support/core_ext/time.rb +1 -0
  94. data/lib/active_support/core_ext/uri.rb +3 -23
  95. data/lib/active_support/core_ext.rb +2 -1
  96. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  97. data/lib/active_support/current_attributes.rb +39 -16
  98. data/lib/active_support/dependencies/interlock.rb +10 -18
  99. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  100. data/lib/active_support/dependencies.rb +58 -764
  101. data/lib/active_support/deprecation/behaviors.rb +19 -3
  102. data/lib/active_support/deprecation/disallowed.rb +56 -0
  103. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  104. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  105. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  106. data/lib/active_support/deprecation/reporting.rb +50 -7
  107. data/lib/active_support/deprecation.rb +6 -1
  108. data/lib/active_support/descendants_tracker.rb +177 -64
  109. data/lib/active_support/digest.rb +5 -3
  110. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  111. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  112. data/lib/active_support/duration.rb +134 -55
  113. data/lib/active_support/encrypted_configuration.rb +11 -1
  114. data/lib/active_support/encrypted_file.rb +20 -3
  115. data/lib/active_support/environment_inquirer.rb +20 -0
  116. data/lib/active_support/error_reporter.rb +117 -0
  117. data/lib/active_support/evented_file_update_checker.rb +70 -134
  118. data/lib/active_support/execution_context/test_helper.rb +13 -0
  119. data/lib/active_support/execution_context.rb +53 -0
  120. data/lib/active_support/execution_wrapper.rb +30 -4
  121. data/lib/active_support/executor/test_helper.rb +7 -0
  122. data/lib/active_support/fork_tracker.rb +71 -0
  123. data/lib/active_support/gem_version.rb +3 -3
  124. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  125. data/lib/active_support/html_safe_translation.rb +43 -0
  126. data/lib/active_support/i18n.rb +1 -0
  127. data/lib/active_support/i18n_railtie.rb +14 -19
  128. data/lib/active_support/inflector/inflections.rb +24 -9
  129. data/lib/active_support/inflector/methods.rb +29 -49
  130. data/lib/active_support/inflector/transliterate.rb +4 -4
  131. data/lib/active_support/isolated_execution_state.rb +56 -0
  132. data/lib/active_support/json/decoding.rb +4 -4
  133. data/lib/active_support/json/encoding.rb +8 -4
  134. data/lib/active_support/key_generator.rb +19 -2
  135. data/lib/active_support/locale/en.yml +8 -4
  136. data/lib/active_support/log_subscriber.rb +21 -3
  137. data/lib/active_support/logger.rb +1 -1
  138. data/lib/active_support/logger_silence.rb +2 -26
  139. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  140. data/lib/active_support/message_encryptor.rb +12 -10
  141. data/lib/active_support/message_verifier.rb +50 -18
  142. data/lib/active_support/messages/metadata.rb +11 -3
  143. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  144. data/lib/active_support/messages/rotator.rb +6 -5
  145. data/lib/active_support/multibyte/chars.rb +13 -52
  146. data/lib/active_support/multibyte/unicode.rb +1 -87
  147. data/lib/active_support/multibyte.rb +1 -1
  148. data/lib/active_support/notifications/fanout.rb +110 -69
  149. data/lib/active_support/notifications/instrumenter.rb +37 -29
  150. data/lib/active_support/notifications.rb +47 -26
  151. data/lib/active_support/number_helper/number_converter.rb +2 -4
  152. data/lib/active_support/number_helper/number_to_currency_converter.rb +10 -9
  153. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  154. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  155. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  156. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  157. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  158. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  159. data/lib/active_support/number_helper.rb +29 -16
  160. data/lib/active_support/option_merger.rb +9 -16
  161. data/lib/active_support/ordered_hash.rb +1 -1
  162. data/lib/active_support/ordered_options.rb +8 -2
  163. data/lib/active_support/parameter_filter.rb +21 -11
  164. data/lib/active_support/per_thread_registry.rb +6 -1
  165. data/lib/active_support/rails.rb +1 -4
  166. data/lib/active_support/railtie.rb +77 -5
  167. data/lib/active_support/rescuable.rb +6 -6
  168. data/lib/active_support/ruby_features.rb +7 -0
  169. data/lib/active_support/secure_compare_rotator.rb +51 -0
  170. data/lib/active_support/security_utils.rb +19 -12
  171. data/lib/active_support/string_inquirer.rb +2 -2
  172. data/lib/active_support/subscriber.rb +19 -25
  173. data/lib/active_support/tagged_logging.rb +31 -6
  174. data/lib/active_support/test_case.rb +9 -21
  175. data/lib/active_support/testing/assertions.rb +49 -12
  176. data/lib/active_support/testing/deprecation.rb +52 -1
  177. data/lib/active_support/testing/isolation.rb +2 -2
  178. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  179. data/lib/active_support/testing/parallelization/server.rb +82 -0
  180. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  181. data/lib/active_support/testing/parallelization.rb +16 -95
  182. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  183. data/lib/active_support/testing/stream.rb +3 -5
  184. data/lib/active_support/testing/tagged_logging.rb +1 -1
  185. data/lib/active_support/testing/time_helpers.rb +53 -5
  186. data/lib/active_support/time_with_zone.rb +120 -55
  187. data/lib/active_support/values/time_zone.rb +49 -18
  188. data/lib/active_support/xml_mini/jdom.rb +1 -1
  189. data/lib/active_support/xml_mini/libxml.rb +5 -5
  190. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  191. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  192. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  193. data/lib/active_support/xml_mini/rexml.rb +9 -2
  194. data/lib/active_support/xml_mini.rb +5 -4
  195. data/lib/active_support.rb +29 -1
  196. metadata +46 -45
  197. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  198. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  199. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  200. data/lib/active_support/core_ext/marshal.rb +0 -24
  201. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  202. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  203. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  204. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module HtmlSafeTranslation # :nodoc:
5
+ extend self
6
+
7
+ def translate(key, **options)
8
+ if html_safe_translation_key?(key)
9
+ html_safe_options = html_escape_translation_options(options)
10
+ translation = I18n.translate(key, **html_safe_options)
11
+ html_safe_translation(translation)
12
+ else
13
+ I18n.translate(key, **options)
14
+ end
15
+ end
16
+
17
+ private
18
+ def html_safe_translation_key?(key)
19
+ /(?:_|\b)html\z/.match?(key)
20
+ end
21
+
22
+ def html_escape_translation_options(options)
23
+ options.each do |name, value|
24
+ unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
25
+ options[name] = ERB::Util.html_escape(value.to_s)
26
+ end
27
+ end
28
+ end
29
+
30
+ def i18n_option?(name)
31
+ (@i18n_option_names ||= I18n::RESERVED_KEYS.to_set).include?(name)
32
+ end
33
+
34
+
35
+ def html_safe_translation(translation)
36
+ if translation.respond_to?(:map)
37
+ translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
38
+ else
39
+ translation.respond_to?(:html_safe) ? translation.html_safe : translation
40
+ end
41
+ end
42
+ end
43
+ end
@@ -5,6 +5,7 @@ require "active_support/core_ext/hash/except"
5
5
  require "active_support/core_ext/hash/slice"
6
6
  begin
7
7
  require "i18n"
8
+ require "i18n/backend/fallbacks"
8
9
  rescue LoadError => e
9
10
  $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
10
11
  raise e
@@ -12,9 +12,7 @@ module I18n
12
12
  config.i18n.load_path = []
13
13
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
14
14
 
15
- if I18n.respond_to?(:eager_load!)
16
- config.eager_load_namespaces << I18n
17
- end
15
+ config.eager_load_namespaces << I18n
18
16
 
19
17
  # Set the i18n configuration after initialization since a lot of
20
18
  # configuration is still usually done in application initializers.
@@ -50,8 +48,10 @@ module I18n
50
48
  app.config.i18n.load_path.unshift(*value.flat_map(&:existent))
51
49
  when :load_path
52
50
  I18n.load_path += value
51
+ when :raise_on_missing_translations
52
+ forward_raise_on_missing_translations_config(app)
53
53
  else
54
- I18n.send("#{setting}=", value)
54
+ I18n.public_send("#{setting}=", value)
55
55
  end
56
56
  end
57
57
 
@@ -64,8 +64,6 @@ module I18n
64
64
  reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
65
65
  I18n.load_path.keep_if { |p| File.exist?(p) }
66
66
  I18n.load_path |= reloadable_paths.flat_map(&:existent)
67
-
68
- I18n.reload!
69
67
  end
70
68
 
71
69
  app.reloaders << reloader
@@ -77,6 +75,16 @@ module I18n
77
75
  @i18n_inited = true
78
76
  end
79
77
 
78
+ def self.forward_raise_on_missing_translations_config(app)
79
+ ActiveSupport.on_load(:action_view) do
80
+ ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
81
+ end
82
+
83
+ ActiveSupport.on_load(:action_controller) do
84
+ AbstractController::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
85
+ end
86
+ end
87
+
80
88
  def self.include_fallbacks_module
81
89
  I18n.backend.class.include(I18n::Backend::Fallbacks)
82
90
  end
@@ -94,19 +102,6 @@ module I18n
94
102
  [I18n.default_locale]
95
103
  end
96
104
 
97
- if args.empty? || args.first.is_a?(Hash)
98
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
99
- Using I18n fallbacks with an empty `defaults` sets the defaults to
100
- include the `default_locale`. This behavior will change in Rails 6.1.
101
- If you desire the default locale to be included in the defaults, please
102
- explicitly configure it with `config.i18n.fallbacks.defaults =
103
- [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
104
- {...}]`. If you want to opt-in to the new behavior, use
105
- `config.i18n.fallbacks.defaults = [nil, {...}]`.
106
- MSG
107
- args.unshift I18n.default_locale
108
- end
109
-
110
105
  I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
111
106
  end
112
107
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "concurrent/map"
4
4
  require "active_support/i18n"
5
- require "active_support/deprecation"
6
5
 
7
6
  module ActiveSupport
8
7
  module Inflector
@@ -17,13 +16,13 @@ module ActiveSupport
17
16
  # inflect.plural /^(ox)$/i, '\1\2en'
18
17
  # inflect.singular /^(ox)en/i, '\1'
19
18
  #
20
- # inflect.irregular 'octopus', 'octopi'
19
+ # inflect.irregular 'cactus', 'cacti'
21
20
  #
22
21
  # inflect.uncountable 'equipment'
23
22
  # end
24
23
  #
25
24
  # New rules are added at the top. So in the example above, the irregular
26
- # rule for octopus will now be the first of the pluralization and
25
+ # rule for cactus will now be the first of the pluralization and
27
26
  # singularization rules that is runs. This guarantees that your rules run
28
27
  # before any of the rules that may already have been loaded.
29
28
  class Inflections
@@ -65,6 +64,13 @@ module ActiveSupport
65
64
  @__instance__[locale] ||= new
66
65
  end
67
66
 
67
+ def self.instance_or_fallback(locale)
68
+ I18n.fallbacks[locale].each do |k|
69
+ return @__instance__[k] if @__instance__.key?(k)
70
+ end
71
+ instance(locale)
72
+ end
73
+
68
74
  attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
69
75
 
70
76
  attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
@@ -77,7 +83,7 @@ module ActiveSupport
77
83
  # Private, for the test suite.
78
84
  def initialize_dup(orig) # :nodoc:
79
85
  %w(plurals singulars uncountables humans acronyms).each do |scope|
80
- instance_variable_set("@#{scope}", orig.send(scope).dup)
86
+ instance_variable_set("@#{scope}", orig.public_send(scope).dup)
81
87
  end
82
88
  define_acronym_regex_patterns
83
89
  end
@@ -161,7 +167,7 @@ module ActiveSupport
161
167
  # regular expressions. You simply pass the irregular in singular and
162
168
  # plural form.
163
169
  #
164
- # irregular 'octopus', 'octopi'
170
+ # irregular 'cactus', 'cacti'
165
171
  # irregular 'person', 'people'
166
172
  def irregular(singular, plural)
167
173
  @uncountables.delete(singular)
@@ -216,15 +222,24 @@ module ActiveSupport
216
222
  # Clears the loaded inflections within a given scope (default is
217
223
  # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
218
224
  # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
219
- # <tt>:humans</tt>.
225
+ # <tt>:humans</tt>, <tt>:acronyms</tt>.
220
226
  #
221
227
  # clear :all
222
228
  # clear :plurals
223
229
  def clear(scope = :all)
224
230
  case scope
225
231
  when :all
226
- @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
227
- else
232
+ clear(:acronyms)
233
+ clear(:plurals)
234
+ clear(:singulars)
235
+ clear(:uncountables)
236
+ clear(:humans)
237
+ when :acronyms
238
+ @acronyms = {}
239
+ define_acronym_regex_patterns
240
+ when :uncountables
241
+ @uncountables = Uncountables.new
242
+ when :plurals, :singulars, :humans
228
243
  instance_variable_set "@#{scope}", []
229
244
  end
230
245
  end
@@ -249,7 +264,7 @@ module ActiveSupport
249
264
  if block_given?
250
265
  yield Inflections.instance(locale)
251
266
  else
252
- Inflections.instance(locale)
267
+ Inflections.instance_or_fallback(locale)
253
268
  end
254
269
  end
255
270
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/inflections"
4
+ require "active_support/core_ext/object/blank"
4
5
 
5
6
  module ActiveSupport
6
7
  # The Inflector transforms words from singular to plural, class names to table
@@ -67,13 +68,17 @@ module ActiveSupport
67
68
  # camelize(underscore('SSLError')) # => "SslError"
68
69
  def camelize(term, uppercase_first_letter = true)
69
70
  string = term.to_s
70
- if uppercase_first_letter
71
- string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
71
+ # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
72
+ if !uppercase_first_letter || uppercase_first_letter == :lower
73
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
72
74
  else
73
- string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
75
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
76
+ end
77
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
78
+ word = $2
79
+ substituted = inflections.acronyms[word] || word.capitalize! || word
80
+ $1 ? "::#{substituted}" : substituted
74
81
  end
75
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
76
- string.gsub!("/", "::")
77
82
  string
78
83
  end
79
84
 
@@ -89,11 +94,10 @@ module ActiveSupport
89
94
  #
90
95
  # camelize(underscore('SSLError')) # => "SslError"
91
96
  def underscore(camel_cased_word)
92
- return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
97
+ return camel_cased_word.to_s unless /[A-Z-]|::/.match?(camel_cased_word)
93
98
  word = camel_cased_word.to_s.gsub("::", "/")
94
99
  word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
95
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
96
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
100
+ word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
97
101
  word.tr!("-", "_")
98
102
  word.downcase!
99
103
  word
@@ -105,7 +109,7 @@ module ActiveSupport
105
109
  #
106
110
  # * Applies human inflection rules to the argument.
107
111
  # * Deletes leading underscores, if any.
108
- # * Removes a "_id" suffix if present.
112
+ # * Removes an "_id" suffix if present.
109
113
  # * Replaces underscores with spaces, if any.
110
114
  # * Downcases all words except acronyms.
111
115
  # * Capitalizes the first word.
@@ -119,7 +123,7 @@ module ActiveSupport
119
123
  # humanize('author_id') # => "Author"
120
124
  # humanize('author_id', capitalize: false) # => "author"
121
125
  # humanize('_id') # => "Id"
122
- # humanize('author_id', keep_id_suffix: true) # => "Author Id"
126
+ # humanize('author_id', keep_id_suffix: true) # => "Author id"
123
127
  #
124
128
  # If "SSL" was defined to be an acronym:
125
129
  #
@@ -130,18 +134,22 @@ module ActiveSupport
130
134
 
131
135
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
132
136
 
133
- result.sub!(/\A_+/, "")
137
+ result.tr!("_", " ")
138
+ result.lstrip!
134
139
  unless keep_id_suffix
135
- result.sub!(/_id\z/, "")
140
+ result.delete_suffix!(" id")
136
141
  end
137
- result.tr!("_", " ")
138
142
 
139
- result.gsub!(/([a-z\d]*)/i) do |match|
140
- "#{inflections.acronyms[match.downcase] || match.downcase}"
143
+ result.gsub!(/([a-z\d]+)/i) do |match|
144
+ match.downcase!
145
+ inflections.acronyms[match] || match
141
146
  end
142
147
 
143
148
  if capitalize
144
- result.sub!(/\A\w/) { |match| match.upcase }
149
+ result.sub!(/\A\w/) do |match|
150
+ match.upcase!
151
+ match
152
+ end
145
153
  end
146
154
 
147
155
  result
@@ -172,7 +180,7 @@ module ActiveSupport
172
180
  # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
173
181
  # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
174
182
  def titleize(word, keep_id_suffix: false)
175
- humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`])[a-z]/) do |match|
183
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])[a-z]/) do |match|
176
184
  match.capitalize
177
185
  end
178
186
  end
@@ -269,34 +277,7 @@ module ActiveSupport
269
277
  # NameError is raised when the name is not in CamelCase or the constant is
270
278
  # unknown.
271
279
  def constantize(camel_cased_word)
272
- names = camel_cased_word.split("::")
273
-
274
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
275
- Object.const_get(camel_cased_word) if names.empty?
276
-
277
- # Remove the first blank element in case of '::ClassName' notation.
278
- names.shift if names.size > 1 && names.first.empty?
279
-
280
- names.inject(Object) do |constant, name|
281
- if constant == Object
282
- constant.const_get(name)
283
- else
284
- candidate = constant.const_get(name)
285
- next candidate if constant.const_defined?(name, false)
286
- next candidate unless Object.const_defined?(name)
287
-
288
- # Go down the ancestors to check if it is owned directly. The check
289
- # stops when we reach Object or the end of ancestors tree.
290
- constant = constant.ancestors.inject(constant) do |const, ancestor|
291
- break const if ancestor == Object
292
- break ancestor if ancestor.const_defined?(name, false)
293
- const
294
- end
295
-
296
- # owner is in Object, so raise
297
- constant.const_get(name, false)
298
- end
299
- end
280
+ Object.const_get(camel_cased_word)
300
281
  end
301
282
 
302
283
  # Tries to find a constant with the name specified in the argument string.
@@ -326,10 +307,9 @@ module ActiveSupport
326
307
  rescue NameError => e
327
308
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
328
309
  e.name.to_s == camel_cased_word.to_s)
329
- rescue ArgumentError => e
330
- raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
331
310
  rescue LoadError => e
332
- raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
311
+ message = e.respond_to?(:original_message) ? e.original_message : e.message
312
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message)
333
313
  end
334
314
 
335
315
  # Returns the suffix that should be added to a number to denote the position
@@ -371,7 +351,7 @@ module ActiveSupport
371
351
 
372
352
  last = parts.pop
373
353
 
374
- parts.reverse.inject(last) do |acc, part|
354
+ parts.reverse!.inject(last) do |acc, part|
375
355
  part.empty? ? acc : "#{part}(::#{acc})?"
376
356
  end
377
357
  end
@@ -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
  #
@@ -62,9 +64,7 @@ module ActiveSupport
62
64
  def transliterate(string, replacement = "?", locale: nil)
63
65
  string = string.dup if string.frozen?
64
66
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
65
-
66
- allowed_encodings = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030]
67
- raise ArgumentError, "Can not transliterate strings with #{string.encoding} encoding" unless allowed_encodings.include?(string.encoding)
67
+ raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
68
68
 
69
69
  input_encoding = string.encoding
70
70
 
@@ -117,7 +117,7 @@ module ActiveSupport
117
117
  # If the optional parameter +locale+ is specified,
118
118
  # the word will be parameterized as a word of that language.
119
119
  # By default, this parameter is set to <tt>nil</tt> and it will use
120
- # the configured <tt>I18n.locale<tt>.
120
+ # the configured <tt>I18n.locale</tt>.
121
121
  def parameterize(string, separator: "-", preserve_case: false, locale: nil)
122
122
  # Replace accented chars with their ASCII equivalents.
123
123
  parameterized_string = transliterate(string, locale: locale)
@@ -0,0 +1,56 @@
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 clear
41
+ current.clear
42
+ end
43
+
44
+ private
45
+ def current_thread
46
+ Thread.current.active_support_execution_state ||= {}
47
+ end
48
+
49
+ def current_fiber
50
+ Fiber.current.active_support_execution_state ||= {}
51
+ end
52
+
53
+ alias_method :current, :current_thread
54
+ end
55
+ end
56
+ 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.
@@ -63,8 +63,8 @@ module ActiveSupport
63
63
  when Array
64
64
  data.map! { |d| convert_dates_from(d) }
65
65
  when Hash
66
- data.each do |key, value|
67
- data[key] = convert_dates_from(value)
66
+ data.transform_values! do |value|
67
+ convert_dates_from(value)
68
68
  end
69
69
  else
70
70
  data
@@ -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,7 +51,7 @@ 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
57
  s = super
@@ -93,7 +93,11 @@ module ActiveSupport
93
93
  when Numeric, NilClass, TrueClass, FalseClass
94
94
  value.as_json
95
95
  when Hash
96
- 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
97
101
  when Array
98
102
  value.map { |v| jsonify(v) }
99
103
  else
@@ -9,18 +9,35 @@ 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
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
38
  # i.e. OpenSSL::Digest::SHA1#block_length
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
 
@@ -35,7 +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
58
  end
@@ -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()
@@ -29,6 +29,9 @@ module ActiveSupport
29
29
  # subscriber, the line above should be called after your
30
30
  # <tt>ActiveRecord::LogSubscriber</tt> definition.
31
31
  #
32
+ # A logger also needs to be set with <tt>ActiveRecord::LogSubscriber.logger=</tt>.
33
+ # This is assigned automatically in a Rails environment.
34
+ #
32
35
  # After configured, whenever a <tt>"sql.active_record"</tt> notification is published,
33
36
  # it will properly dispatch the event
34
37
  # (<tt>ActiveSupport::Notifications::Event</tt>) to the sql method.
@@ -93,6 +96,11 @@ module ActiveSupport
93
96
  def flush_all!
94
97
  logger.flush if logger.respond_to?(:flush)
95
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
96
104
  end
97
105
 
98
106
  def logger
@@ -106,9 +114,13 @@ module ActiveSupport
106
114
  def finish(name, id, payload)
107
115
  super if logger
108
116
  rescue => e
109
- if logger
110
- logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
111
- end
117
+ log_exception(name, e)
118
+ end
119
+
120
+ def publish_event(event)
121
+ super if logger
122
+ rescue => e
123
+ log_exception(event.name, e)
112
124
  end
113
125
 
114
126
  private
@@ -130,5 +142,11 @@ module ActiveSupport
130
142
  bold = bold ? BOLD : ""
131
143
  "#{bold}#{color}#{text}#{CLEAR}"
132
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
133
151
  end
134
152
  end
@@ -14,7 +14,7 @@ module ActiveSupport
14
14
  # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
15
15
  # # => true
16
16
  def self.logger_outputs_to?(logger, *sources)
17
- logdev = logger.instance_variable_get("@logdev")
17
+ logdev = logger.instance_variable_get(:@logdev)
18
18
  logger_source = logdev.dev if logdev.respond_to?(:dev)
19
19
  sources.any? { |source| source == logger_source }
20
20
  end