activesupport 6.1.7.2 → 7.0.5

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +261 -511
  3. data/lib/active_support/actionable_error.rb +1 -1
  4. data/lib/active_support/array_inquirer.rb +0 -2
  5. data/lib/active_support/backtrace_cleaner.rb +2 -2
  6. data/lib/active_support/benchmarkable.rb +2 -2
  7. data/lib/active_support/cache/file_store.rb +15 -9
  8. data/lib/active_support/cache/mem_cache_store.rb +148 -37
  9. data/lib/active_support/cache/memory_store.rb +24 -16
  10. data/lib/active_support/cache/null_store.rb +10 -2
  11. data/lib/active_support/cache/redis_cache_store.rb +59 -78
  12. data/lib/active_support/cache/strategy/local_cache.rb +38 -61
  13. data/lib/active_support/cache.rb +299 -147
  14. data/lib/active_support/callbacks.rb +184 -85
  15. data/lib/active_support/code_generator.rb +65 -0
  16. data/lib/active_support/concern.rb +5 -5
  17. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  18. data/lib/active_support/concurrency/share_lock.rb +2 -2
  19. data/lib/active_support/configurable.rb +8 -5
  20. data/lib/active_support/configuration_file.rb +1 -1
  21. data/lib/active_support/core_ext/array/access.rb +1 -5
  22. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  23. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  24. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  25. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  26. data/lib/active_support/core_ext/array.rb +1 -0
  27. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  28. data/lib/active_support/core_ext/class/subclasses.rb +25 -17
  29. data/lib/active_support/core_ext/date/blank.rb +1 -1
  30. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  31. data/lib/active_support/core_ext/date/conversions.rb +14 -14
  32. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  33. data/lib/active_support/core_ext/date.rb +1 -0
  34. data/lib/active_support/core_ext/date_and_time/calculations.rb +4 -4
  35. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  36. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  37. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  38. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  39. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  40. data/lib/active_support/core_ext/date_time.rb +1 -0
  41. data/lib/active_support/core_ext/digest/uuid.rb +39 -14
  42. data/lib/active_support/core_ext/enumerable.rb +106 -37
  43. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  44. data/lib/active_support/core_ext/hash/conversions.rb +0 -1
  45. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  47. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  48. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  49. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  50. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  51. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  52. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  53. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  54. data/lib/active_support/core_ext/name_error.rb +2 -8
  55. data/lib/active_support/core_ext/numeric/conversions.rb +80 -77
  56. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  57. data/lib/active_support/core_ext/numeric.rb +1 -0
  58. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  59. data/lib/active_support/core_ext/object/blank.rb +2 -2
  60. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  61. data/lib/active_support/core_ext/object/duplicable.rb +15 -4
  62. data/lib/active_support/core_ext/object/json.rb +30 -25
  63. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  64. data/lib/active_support/core_ext/object/try.rb +20 -20
  65. data/lib/active_support/core_ext/object/with_options.rb +21 -2
  66. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  67. data/lib/active_support/core_ext/pathname.rb +3 -0
  68. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  69. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  70. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  71. data/lib/active_support/core_ext/range/each.rb +1 -1
  72. data/lib/active_support/core_ext/range/include_time_with_zone.rb +3 -26
  73. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  74. data/lib/active_support/core_ext/range.rb +1 -1
  75. data/lib/active_support/core_ext/securerandom.rb +1 -1
  76. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  77. data/lib/active_support/core_ext/string/filters.rb +1 -1
  78. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  79. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  80. data/lib/active_support/core_ext/string/output_safety.rb +66 -38
  81. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  82. data/lib/active_support/core_ext/time/calculations.rb +11 -8
  83. data/lib/active_support/core_ext/time/conversions.rb +13 -12
  84. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  85. data/lib/active_support/core_ext/time/zones.rb +10 -26
  86. data/lib/active_support/core_ext/time.rb +1 -0
  87. data/lib/active_support/core_ext/uri.rb +3 -27
  88. data/lib/active_support/core_ext.rb +1 -0
  89. data/lib/active_support/current_attributes.rb +31 -15
  90. data/lib/active_support/dependencies/interlock.rb +10 -18
  91. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  92. data/lib/active_support/dependencies.rb +58 -788
  93. data/lib/active_support/deprecation/behaviors.rb +8 -5
  94. data/lib/active_support/deprecation/disallowed.rb +3 -3
  95. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  96. data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
  97. data/lib/active_support/deprecation.rb +2 -2
  98. data/lib/active_support/descendants_tracker.rb +174 -68
  99. data/lib/active_support/digest.rb +4 -4
  100. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  101. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  102. data/lib/active_support/duration.rb +77 -48
  103. data/lib/active_support/encrypted_configuration.rb +45 -3
  104. data/lib/active_support/encrypted_file.rb +13 -1
  105. data/lib/active_support/environment_inquirer.rb +1 -1
  106. data/lib/active_support/error_reporter.rb +117 -0
  107. data/lib/active_support/evented_file_update_checker.rb +3 -5
  108. data/lib/active_support/execution_context/test_helper.rb +13 -0
  109. data/lib/active_support/execution_context.rb +53 -0
  110. data/lib/active_support/execution_wrapper.rb +30 -11
  111. data/lib/active_support/executor/test_helper.rb +7 -0
  112. data/lib/active_support/fork_tracker.rb +19 -12
  113. data/lib/active_support/gem_version.rb +5 -5
  114. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  115. data/lib/active_support/html_safe_translation.rb +43 -0
  116. data/lib/active_support/i18n.rb +1 -0
  117. data/lib/active_support/i18n_railtie.rb +1 -1
  118. data/lib/active_support/inflector/inflections.rb +23 -7
  119. data/lib/active_support/inflector/methods.rb +27 -50
  120. data/lib/active_support/inflector/transliterate.rb +1 -1
  121. data/lib/active_support/isolated_execution_state.rb +72 -0
  122. data/lib/active_support/json/encoding.rb +3 -3
  123. data/lib/active_support/key_generator.rb +22 -5
  124. data/lib/active_support/lazy_load_hooks.rb +28 -4
  125. data/lib/active_support/locale/en.yml +1 -1
  126. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  127. data/lib/active_support/log_subscriber.rb +15 -5
  128. data/lib/active_support/logger_thread_safe_level.rb +4 -13
  129. data/lib/active_support/message_encryptor.rb +12 -6
  130. data/lib/active_support/message_verifier.rb +46 -14
  131. data/lib/active_support/messages/metadata.rb +2 -2
  132. data/lib/active_support/multibyte/chars.rb +10 -11
  133. data/lib/active_support/multibyte/unicode.rb +0 -12
  134. data/lib/active_support/multibyte.rb +1 -1
  135. data/lib/active_support/notifications/fanout.rb +91 -65
  136. data/lib/active_support/notifications/instrumenter.rb +32 -15
  137. data/lib/active_support/notifications.rb +23 -23
  138. data/lib/active_support/number_helper/number_converter.rb +1 -3
  139. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  140. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  141. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  142. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  143. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  144. data/lib/active_support/number_helper.rb +4 -5
  145. data/lib/active_support/option_merger.rb +10 -18
  146. data/lib/active_support/ordered_hash.rb +1 -1
  147. data/lib/active_support/ordered_options.rb +1 -1
  148. data/lib/active_support/parameter_filter.rb +20 -11
  149. data/lib/active_support/per_thread_registry.rb +5 -1
  150. data/lib/active_support/railtie.rb +69 -19
  151. data/lib/active_support/rescuable.rb +12 -12
  152. data/lib/active_support/ruby_features.rb +7 -0
  153. data/lib/active_support/secure_compare_rotator.rb +2 -2
  154. data/lib/active_support/string_inquirer.rb +0 -2
  155. data/lib/active_support/subscriber.rb +7 -18
  156. data/lib/active_support/tagged_logging.rb +1 -1
  157. data/lib/active_support/test_case.rb +13 -21
  158. data/lib/active_support/testing/assertions.rb +35 -5
  159. data/lib/active_support/testing/deprecation.rb +52 -1
  160. data/lib/active_support/testing/isolation.rb +30 -29
  161. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  162. data/lib/active_support/testing/parallelization/server.rb +4 -0
  163. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  164. data/lib/active_support/testing/parallelization.rb +4 -0
  165. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  166. data/lib/active_support/testing/stream.rb +3 -5
  167. data/lib/active_support/testing/tagged_logging.rb +1 -1
  168. data/lib/active_support/testing/time_helpers.rb +13 -2
  169. data/lib/active_support/time_with_zone.rb +62 -22
  170. data/lib/active_support/values/time_zone.rb +33 -14
  171. data/lib/active_support/version.rb +1 -1
  172. data/lib/active_support/xml_mini/jdom.rb +1 -1
  173. data/lib/active_support/xml_mini/libxml.rb +5 -5
  174. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  175. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  176. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  177. data/lib/active_support/xml_mini/rexml.rb +1 -1
  178. data/lib/active_support/xml_mini.rb +5 -4
  179. data/lib/active_support.rb +16 -0
  180. metadata +28 -26
  181. data/lib/active_support/core_ext/marshal.rb +0 -26
  182. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -120
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/error_reporter"
3
4
  require "active_support/callbacks"
4
5
  require "concurrent/hash"
5
6
 
@@ -65,7 +66,7 @@ module ActiveSupport
65
66
  # Where possible, prefer +wrap+.
66
67
  def self.run!(reset: false)
67
68
  if reset
68
- lost_instance = active.delete(Thread.current)
69
+ lost_instance = IsolatedExecutionState.delete(active_key)
69
70
  lost_instance&.complete!
70
71
  else
71
72
  return Null if active?
@@ -89,28 +90,42 @@ module ActiveSupport
89
90
  instance = run!
90
91
  begin
91
92
  yield
93
+ rescue => error
94
+ error_reporter.report(error, handled: false)
95
+ raise
92
96
  ensure
93
97
  instance.complete!
94
98
  end
95
99
  end
96
100
 
97
- class << self # :nodoc:
98
- attr_accessor :active
101
+ def self.perform # :nodoc:
102
+ instance = new
103
+ instance.run
104
+ begin
105
+ yield
106
+ ensure
107
+ instance.complete
108
+ end
99
109
  end
100
110
 
101
- def self.inherited(other) # :nodoc:
102
- super
103
- other.active = Concurrent::Hash.new
111
+ def self.error_reporter
112
+ @error_reporter ||= ActiveSupport::ErrorReporter.new
104
113
  end
105
114
 
106
- self.active = Concurrent::Hash.new
115
+ def self.active_key # :nodoc:
116
+ @active_key ||= :"active_execution_wrapper_#{object_id}"
117
+ end
107
118
 
108
119
  def self.active? # :nodoc:
109
- @active.key?(Thread.current)
120
+ IsolatedExecutionState.key?(active_key)
110
121
  end
111
122
 
112
123
  def run! # :nodoc:
113
- self.class.active[Thread.current] = self
124
+ IsolatedExecutionState[self.class.active_key] = self
125
+ run
126
+ end
127
+
128
+ def run # :nodoc:
114
129
  run_callbacks(:run)
115
130
  end
116
131
 
@@ -119,9 +134,13 @@ module ActiveSupport
119
134
  #
120
135
  # Where possible, prefer +wrap+.
121
136
  def complete!
122
- run_callbacks(:complete)
137
+ complete
123
138
  ensure
124
- self.class.active.delete Thread.current
139
+ IsolatedExecutionState.delete(self.class.active_key)
140
+ end
141
+
142
+ def complete # :nodoc:
143
+ run_callbacks(:complete)
125
144
  end
126
145
 
127
146
  private
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport::Executor::TestHelper # :nodoc:
4
+ def run(...)
5
+ Rails.application.executor.perform { super }
6
+ end
7
+ end
@@ -2,8 +2,18 @@
2
2
 
3
3
  module ActiveSupport
4
4
  module ForkTracker # :nodoc:
5
+ module ModernCoreExt
6
+ def _fork
7
+ pid = super
8
+ if pid == 0
9
+ ForkTracker.check!
10
+ end
11
+ pid
12
+ end
13
+ end
14
+
5
15
  module CoreExt
6
- def fork(*)
16
+ def fork(...)
7
17
  if block_given?
8
18
  super do
9
19
  ForkTracker.check!
@@ -16,17 +26,11 @@ module ActiveSupport
16
26
  pid
17
27
  end
18
28
  end
19
- ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
20
29
  end
21
30
 
22
31
  module CoreExtPrivate
23
32
  include CoreExt
24
-
25
- private
26
- def fork(*)
27
- super
28
- end
29
- ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
33
+ private :fork
30
34
  end
31
35
 
32
36
  @pid = Process.pid
@@ -34,15 +38,18 @@ module ActiveSupport
34
38
 
35
39
  class << self
36
40
  def check!
37
- if @pid != Process.pid
41
+ new_pid = Process.pid
42
+ if @pid != new_pid
38
43
  @callbacks.each(&:call)
39
- @pid = Process.pid
44
+ @pid = new_pid
40
45
  end
41
46
  end
42
47
 
43
48
  def hook!
44
- if Process.respond_to?(:fork)
45
- ::Object.prepend(CoreExtPrivate)
49
+ if Process.respond_to?(:_fork) # Ruby 3.1+
50
+ ::Process.singleton_class.prepend(ModernCoreExt)
51
+ elsif Process.respond_to?(:fork)
52
+ ::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
46
53
  ::Kernel.prepend(CoreExtPrivate)
47
54
  ::Kernel.singleton_class.prepend(CoreExt)
48
55
  ::Process.singleton_class.prepend(CoreExt)
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- # Returns the version of the currently loaded Active Support as a <tt>Gem::Version</tt>.
4
+ # Returns the currently loaded version of Active Support as a <tt>Gem::Version</tt>.
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
11
- MINOR = 1
12
- TINY = 7
13
- PRE = "2"
10
+ MAJOR = 7
11
+ MINOR = 0
12
+ TINY = 5
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -65,7 +65,7 @@ module ActiveSupport
65
65
  self
66
66
  end
67
67
 
68
- def initialize(constructor = {})
68
+ def initialize(constructor = nil)
69
69
  if constructor.respond_to?(:to_hash)
70
70
  super()
71
71
  update(constructor)
@@ -73,6 +73,8 @@ module ActiveSupport
73
73
  hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
74
74
  self.default = hash.default if hash.default
75
75
  self.default_proc = hash.default_proc if hash.default_proc
76
+ elsif constructor.nil?
77
+ super()
76
78
  else
77
79
  super(constructor)
78
80
  end
@@ -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
@@ -77,7 +77,7 @@ module I18n
77
77
 
78
78
  def self.forward_raise_on_missing_translations_config(app)
79
79
  ActiveSupport.on_load(:action_view) do
80
- self.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
80
+ ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
81
81
  end
82
82
 
83
83
  ActiveSupport.on_load(:action_controller) do
@@ -16,13 +16,13 @@ module ActiveSupport
16
16
  # inflect.plural /^(ox)$/i, '\1\2en'
17
17
  # inflect.singular /^(ox)en/i, '\1'
18
18
  #
19
- # inflect.irregular 'octopus', 'octopi'
19
+ # inflect.irregular 'cactus', 'cacti'
20
20
  #
21
21
  # inflect.uncountable 'equipment'
22
22
  # end
23
23
  #
24
24
  # New rules are added at the top. So in the example above, the irregular
25
- # 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
26
26
  # singularization rules that is runs. This guarantees that your rules run
27
27
  # before any of the rules that may already have been loaded.
28
28
  class Inflections
@@ -64,6 +64,13 @@ module ActiveSupport
64
64
  @__instance__[locale] ||= new
65
65
  end
66
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
+
67
74
  attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
68
75
 
69
76
  attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
@@ -160,7 +167,7 @@ module ActiveSupport
160
167
  # regular expressions. You simply pass the irregular in singular and
161
168
  # plural form.
162
169
  #
163
- # irregular 'octopus', 'octopi'
170
+ # irregular 'cactus', 'cacti'
164
171
  # irregular 'person', 'people'
165
172
  def irregular(singular, plural)
166
173
  @uncountables.delete(singular)
@@ -215,15 +222,24 @@ module ActiveSupport
215
222
  # Clears the loaded inflections within a given scope (default is
216
223
  # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
217
224
  # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
218
- # <tt>:humans</tt>.
225
+ # <tt>:humans</tt>, <tt>:acronyms</tt>.
219
226
  #
220
227
  # clear :all
221
228
  # clear :plurals
222
229
  def clear(scope = :all)
223
230
  case scope
224
231
  when :all
225
- @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
226
- 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
227
243
  instance_variable_set "@#{scope}", []
228
244
  end
229
245
  end
@@ -248,7 +264,7 @@ module ActiveSupport
248
264
  if block_given?
249
265
  yield Inflections.instance(locale)
250
266
  else
251
- Inflections.instance(locale)
267
+ Inflections.instance_or_fallback(locale)
252
268
  end
253
269
  end
254
270
  end
@@ -68,13 +68,17 @@ module ActiveSupport
68
68
  # camelize(underscore('SSLError')) # => "SslError"
69
69
  def camelize(term, uppercase_first_letter = true)
70
70
  string = term.to_s
71
- if uppercase_first_letter
72
- 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 }
73
74
  else
74
- 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
75
81
  end
76
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
77
- string.gsub!("/", "::")
78
82
  string
79
83
  end
80
84
 
@@ -90,7 +94,7 @@ module ActiveSupport
90
94
  #
91
95
  # camelize(underscore('SSLError')) # => "SslError"
92
96
  def underscore(camel_cased_word)
93
- 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)
94
98
  word = camel_cased_word.to_s.gsub("::", "/")
95
99
  word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
96
100
  word.gsub!(/([A-Z])(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
@@ -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.delete_suffix!("_id")
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
@@ -188,8 +196,8 @@ module ActiveSupport
188
196
  end
189
197
 
190
198
  # Creates a class name from a plural table name like Rails does for table
191
- # names to models. Note that this returns a string and not a Class (To
192
- # convert to an actual class follow +classify+ with #constantize).
199
+ # names to models. Note that this returns a string and not a Class. (To
200
+ # convert to an actual class follow +classify+ with #constantize.)
193
201
  #
194
202
  # classify('ham_and_eggs') # => "HamAndEgg"
195
203
  # classify('posts') # => "Post"
@@ -269,38 +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
- if camel_cased_word.blank? || !camel_cased_word.include?("::")
273
- Object.const_get(camel_cased_word)
274
- else
275
- names = camel_cased_word.split("::")
276
-
277
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
278
- Object.const_get(camel_cased_word) if names.empty?
279
-
280
- # Remove the first blank element in case of '::ClassName' notation.
281
- names.shift if names.size > 1 && names.first.empty?
282
-
283
- names.inject(Object) do |constant, name|
284
- if constant == Object
285
- constant.const_get(name)
286
- else
287
- candidate = constant.const_get(name)
288
- next candidate if constant.const_defined?(name, false)
289
- next candidate unless Object.const_defined?(name)
290
-
291
- # Go down the ancestors to check if it is owned directly. The check
292
- # stops when we reach Object or the end of ancestors tree.
293
- constant = constant.ancestors.inject(constant) do |const, ancestor|
294
- break const if ancestor == Object
295
- break ancestor if ancestor.const_defined?(name, false)
296
- const
297
- end
298
-
299
- # owner is in Object, so raise
300
- constant.const_get(name, false)
301
- end
302
- end
303
- end
280
+ Object.const_get(camel_cased_word)
304
281
  end
305
282
 
306
283
  # Tries to find a constant with the name specified in the argument string.
@@ -384,8 +361,8 @@ module ActiveSupport
384
361
  # If passed an optional +locale+ parameter, the uncountables will be
385
362
  # found for that locale.
386
363
  #
387
- # apply_inflections('post', inflections.plurals, :en) # => "posts"
388
- # apply_inflections('posts', inflections.singulars, :en) # => "post"
364
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
365
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
389
366
  def apply_inflections(word, rules, locale = :en)
390
367
  result = word.to_s.dup
391
368
 
@@ -59,7 +59,7 @@ module ActiveSupport
59
59
  # transliterate('Jürgen', locale: :de)
60
60
  # # => "Juergen"
61
61
  #
62
- # Transliteration is restricted to UTF-8, US-ASCII and GB18030 strings
62
+ # Transliteration is restricted to UTF-8, US-ASCII, and GB18030 strings.
63
63
  # Other encodings will raise an ArgumentError.
64
64
  def transliterate(string, replacement = "?", locale: nil)
65
65
  string = string.dup if string.frozen?
@@ -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
@@ -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
@@ -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