activesupport 6.0.6.1 → 7.1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +865 -438
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +30 -10
  8. data/lib/active_support/benchmarkable.rb +4 -3
  9. data/lib/active_support/broadcast_logger.rb +250 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +53 -20
  14. data/lib/active_support/cache/mem_cache_store.rb +208 -63
  15. data/lib/active_support/cache/memory_store.rb +120 -38
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +201 -208
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +73 -66
  20. data/lib/active_support/cache.rb +539 -261
  21. data/lib/active_support/callbacks.rb +273 -142
  22. data/lib/active_support/code_generator.rb +65 -0
  23. data/lib/active_support/concern.rb +53 -7
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/concurrency/share_lock.rb +2 -2
  27. data/lib/active_support/configurable.rb +19 -6
  28. data/lib/active_support/configuration_file.rb +51 -0
  29. data/lib/active_support/core_ext/array/access.rb +1 -5
  30. data/lib/active_support/core_ext/array/conversions.rb +15 -13
  31. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  33. data/lib/active_support/core_ext/benchmark.rb +2 -2
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  35. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  36. data/lib/active_support/core_ext/class/subclasses.rb +19 -29
  37. data/lib/active_support/core_ext/date/blank.rb +1 -1
  38. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  39. data/lib/active_support/core_ext/date/conversions.rb +18 -16
  40. data/lib/active_support/core_ext/date_and_time/calculations.rb +27 -4
  41. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  42. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  43. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  44. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  45. data/lib/active_support/core_ext/digest/uuid.rb +30 -13
  46. data/lib/active_support/core_ext/enumerable.rb +146 -72
  47. data/lib/active_support/core_ext/erb/util.rb +196 -0
  48. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  49. data/lib/active_support/core_ext/hash/conversions.rb +3 -4
  50. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  51. data/lib/active_support/core_ext/hash/deep_transform_values.rb +4 -4
  52. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  53. data/lib/active_support/core_ext/hash/keys.rb +5 -5
  54. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  55. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  56. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  57. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  58. data/lib/active_support/core_ext/load_error.rb +1 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +31 -29
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +51 -20
  62. data/lib/active_support/core_ext/module/concerning.rb +14 -8
  63. data/lib/active_support/core_ext/module/delegation.rb +75 -42
  64. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  65. data/lib/active_support/core_ext/module/introspection.rb +1 -26
  66. data/lib/active_support/core_ext/name_error.rb +23 -2
  67. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  68. data/lib/active_support/core_ext/numeric/conversions.rb +82 -73
  69. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  70. data/lib/active_support/core_ext/object/blank.rb +2 -2
  71. data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
  72. data/lib/active_support/core_ext/object/duplicable.rb +15 -4
  73. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  74. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  75. data/lib/active_support/core_ext/object/json.rb +52 -28
  76. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  77. data/lib/active_support/core_ext/object/try.rb +20 -20
  78. data/lib/active_support/core_ext/object/with.rb +44 -0
  79. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  80. data/lib/active_support/core_ext/object.rb +1 -0
  81. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  82. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  83. data/lib/active_support/core_ext/pathname.rb +4 -0
  84. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  85. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  86. data/lib/active_support/core_ext/range/each.rb +1 -1
  87. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  88. data/lib/active_support/core_ext/range.rb +1 -2
  89. data/lib/active_support/core_ext/regexp.rb +8 -1
  90. data/lib/active_support/core_ext/securerandom.rb +25 -13
  91. data/lib/active_support/core_ext/string/access.rb +5 -24
  92. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  93. data/lib/active_support/core_ext/string/filters.rb +21 -15
  94. data/lib/active_support/core_ext/string/indent.rb +1 -1
  95. data/lib/active_support/core_ext/string/inflections.rb +51 -10
  96. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  97. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  98. data/lib/active_support/core_ext/string/output_safety.rb +85 -194
  99. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  100. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  101. data/lib/active_support/core_ext/symbol.rb +3 -0
  102. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  103. data/lib/active_support/core_ext/time/calculations.rb +46 -8
  104. data/lib/active_support/core_ext/time/conversions.rb +16 -13
  105. data/lib/active_support/core_ext/time/zones.rb +12 -28
  106. data/lib/active_support/core_ext.rb +2 -1
  107. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  108. data/lib/active_support/current_attributes.rb +54 -22
  109. data/lib/active_support/deep_mergeable.rb +53 -0
  110. data/lib/active_support/dependencies/autoload.rb +17 -12
  111. data/lib/active_support/dependencies/interlock.rb +10 -18
  112. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  113. data/lib/active_support/dependencies.rb +58 -769
  114. data/lib/active_support/deprecation/behaviors.rb +77 -38
  115. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  116. data/lib/active_support/deprecation/deprecators.rb +104 -0
  117. data/lib/active_support/deprecation/disallowed.rb +54 -0
  118. data/lib/active_support/deprecation/instance_delegator.rb +31 -5
  119. data/lib/active_support/deprecation/method_wrappers.rb +12 -28
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +40 -25
  121. data/lib/active_support/deprecation/reporting.rb +76 -16
  122. data/lib/active_support/deprecation.rb +36 -4
  123. data/lib/active_support/deprecator.rb +7 -0
  124. data/lib/active_support/descendants_tracker.rb +150 -68
  125. data/lib/active_support/digest.rb +5 -3
  126. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  127. data/lib/active_support/duration/iso8601_serializer.rb +24 -12
  128. data/lib/active_support/duration.rb +136 -56
  129. data/lib/active_support/encrypted_configuration.rb +72 -9
  130. data/lib/active_support/encrypted_file.rb +46 -13
  131. data/lib/active_support/environment_inquirer.rb +40 -0
  132. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  133. data/lib/active_support/error_reporter.rb +203 -0
  134. data/lib/active_support/evented_file_update_checker.rb +86 -137
  135. data/lib/active_support/execution_context/test_helper.rb +13 -0
  136. data/lib/active_support/execution_context.rb +53 -0
  137. data/lib/active_support/execution_wrapper.rb +31 -12
  138. data/lib/active_support/executor/test_helper.rb +7 -0
  139. data/lib/active_support/file_update_checker.rb +4 -2
  140. data/lib/active_support/fork_tracker.rb +79 -0
  141. data/lib/active_support/gem_version.rb +5 -5
  142. data/lib/active_support/gzip.rb +2 -0
  143. data/lib/active_support/hash_with_indifferent_access.rb +86 -42
  144. data/lib/active_support/html_safe_translation.rb +53 -0
  145. data/lib/active_support/i18n.rb +2 -1
  146. data/lib/active_support/i18n_railtie.rb +29 -27
  147. data/lib/active_support/inflector/inflections.rb +26 -9
  148. data/lib/active_support/inflector/methods.rb +54 -64
  149. data/lib/active_support/inflector/transliterate.rb +7 -5
  150. data/lib/active_support/isolated_execution_state.rb +76 -0
  151. data/lib/active_support/json/decoding.rb +6 -5
  152. data/lib/active_support/json/encoding.rb +31 -45
  153. data/lib/active_support/key_generator.rb +32 -7
  154. data/lib/active_support/lazy_load_hooks.rb +33 -7
  155. data/lib/active_support/locale/en.yml +10 -4
  156. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  157. data/lib/active_support/log_subscriber.rb +101 -32
  158. data/lib/active_support/logger.rb +9 -60
  159. data/lib/active_support/logger_silence.rb +2 -26
  160. data/lib/active_support/logger_thread_safe_level.rb +24 -25
  161. data/lib/active_support/message_encryptor.rb +205 -58
  162. data/lib/active_support/message_encryptors.rb +141 -0
  163. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  164. data/lib/active_support/message_pack/extensions.rb +292 -0
  165. data/lib/active_support/message_pack/serializer.rb +63 -0
  166. data/lib/active_support/message_pack.rb +50 -0
  167. data/lib/active_support/message_verifier.rb +237 -86
  168. data/lib/active_support/message_verifiers.rb +135 -0
  169. data/lib/active_support/messages/codec.rb +65 -0
  170. data/lib/active_support/messages/metadata.rb +112 -46
  171. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  172. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  173. data/lib/active_support/messages/rotator.rb +35 -32
  174. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  175. data/lib/active_support/multibyte/chars.rb +15 -52
  176. data/lib/active_support/multibyte/unicode.rb +8 -122
  177. data/lib/active_support/multibyte.rb +1 -1
  178. data/lib/active_support/notifications/fanout.rb +310 -105
  179. data/lib/active_support/notifications/instrumenter.rb +113 -48
  180. data/lib/active_support/notifications.rb +56 -29
  181. data/lib/active_support/number_helper/number_converter.rb +15 -8
  182. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  183. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  184. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  185. data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -5
  186. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  187. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  188. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  189. data/lib/active_support/number_helper.rb +379 -304
  190. data/lib/active_support/option_merger.rb +11 -18
  191. data/lib/active_support/ordered_hash.rb +4 -4
  192. data/lib/active_support/ordered_options.rb +23 -3
  193. data/lib/active_support/parameter_filter.rb +104 -75
  194. data/lib/active_support/proxy_object.rb +2 -0
  195. data/lib/active_support/rails.rb +1 -4
  196. data/lib/active_support/railtie.rb +90 -6
  197. data/lib/active_support/reloader.rb +12 -4
  198. data/lib/active_support/rescuable.rb +18 -16
  199. data/lib/active_support/ruby_features.rb +7 -0
  200. data/lib/active_support/secure_compare_rotator.rb +58 -0
  201. data/lib/active_support/security_utils.rb +19 -12
  202. data/lib/active_support/string_inquirer.rb +5 -3
  203. data/lib/active_support/subscriber.rb +23 -47
  204. data/lib/active_support/syntax_error_proxy.rb +70 -0
  205. data/lib/active_support/tagged_logging.rb +84 -23
  206. data/lib/active_support/test_case.rb +166 -27
  207. data/lib/active_support/testing/assertions.rb +73 -20
  208. data/lib/active_support/testing/autorun.rb +0 -2
  209. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  210. data/lib/active_support/testing/deprecation.rb +53 -2
  211. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  212. data/lib/active_support/testing/isolation.rb +30 -29
  213. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  214. data/lib/active_support/testing/parallelization/server.rb +82 -0
  215. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  216. data/lib/active_support/testing/parallelization.rb +16 -95
  217. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  218. data/lib/active_support/testing/stream.rb +4 -6
  219. data/lib/active_support/testing/strict_warnings.rb +39 -0
  220. data/lib/active_support/testing/tagged_logging.rb +1 -1
  221. data/lib/active_support/testing/time_helpers.rb +89 -19
  222. data/lib/active_support/time_with_zone.rb +105 -70
  223. data/lib/active_support/values/time_zone.rb +59 -26
  224. data/lib/active_support/version.rb +1 -1
  225. data/lib/active_support/xml_mini/jdom.rb +4 -11
  226. data/lib/active_support/xml_mini/libxml.rb +5 -5
  227. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  228. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  229. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  230. data/lib/active_support/xml_mini/rexml.rb +9 -2
  231. data/lib/active_support/xml_mini.rb +7 -6
  232. data/lib/active_support.rb +40 -1
  233. metadata +127 -40
  234. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  235. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  236. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  237. data/lib/active_support/core_ext/marshal.rb +0 -24
  238. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  239. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  240. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  241. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -23
  242. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  243. data/lib/active_support/core_ext/uri.rb +0 -25
  244. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
  245. data/lib/active_support/per_thread_registry.rb +0 -60
@@ -3,12 +3,14 @@
3
3
  require "active_support/inflections"
4
4
 
5
5
  module ActiveSupport
6
+ # = Active Support \Inflector
7
+ #
6
8
  # The Inflector transforms words from singular to plural, class names to table
7
9
  # names, modularized class names to ones without, and class names to foreign
8
10
  # keys. The default inflections for pluralization, singularization, and
9
11
  # uncountable words are kept in inflections.rb.
10
12
  #
11
- # The Rails core team has stated patches for the inflections library will not
13
+ # The \Rails core team has stated patches for the inflections library will not
12
14
  # be accepted in order to avoid breaking legacy applications which may be
13
15
  # relying on errant inflections. If you discover an incorrect inflection and
14
16
  # require it for your application or wish to define rules for languages other
@@ -67,13 +69,19 @@ module ActiveSupport
67
69
  # camelize(underscore('SSLError')) # => "SslError"
68
70
  def camelize(term, uppercase_first_letter = true)
69
71
  string = term.to_s
70
- if uppercase_first_letter
71
- string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
72
+ # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
73
+ if !uppercase_first_letter || uppercase_first_letter == :lower
74
+ string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
75
+ elsif string.match?(/\A[a-z\d]*\z/)
76
+ return inflections.acronyms[string]&.dup || string.capitalize
72
77
  else
73
- string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
78
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
79
+ end
80
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
81
+ word = $2
82
+ substituted = inflections.acronyms[word] || word.capitalize! || word
83
+ $1 ? "::#{substituted}" : substituted
74
84
  end
75
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
76
- string.gsub!("/", "::")
77
85
  string
78
86
  end
79
87
 
@@ -89,11 +97,10 @@ module ActiveSupport
89
97
  #
90
98
  # camelize(underscore('SSLError')) # => "SslError"
91
99
  def underscore(camel_cased_word)
92
- return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
100
+ return camel_cased_word.to_s.dup unless /[A-Z-]|::/.match?(camel_cased_word)
93
101
  word = camel_cased_word.to_s.gsub("::", "/")
94
102
  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')
103
+ word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
97
104
  word.tr!("-", "_")
98
105
  word.downcase!
99
106
  word
@@ -105,7 +112,7 @@ module ActiveSupport
105
112
  #
106
113
  # * Applies human inflection rules to the argument.
107
114
  # * Deletes leading underscores, if any.
108
- # * Removes a "_id" suffix if present.
115
+ # * Removes an "_id" suffix if present.
109
116
  # * Replaces underscores with spaces, if any.
110
117
  # * Downcases all words except acronyms.
111
118
  # * Capitalizes the first word.
@@ -119,7 +126,7 @@ module ActiveSupport
119
126
  # humanize('author_id') # => "Author"
120
127
  # humanize('author_id', capitalize: false) # => "author"
121
128
  # humanize('_id') # => "Id"
122
- # humanize('author_id', keep_id_suffix: true) # => "Author Id"
129
+ # humanize('author_id', keep_id_suffix: true) # => "Author id"
123
130
  #
124
131
  # If "SSL" was defined to be an acronym:
125
132
  #
@@ -130,54 +137,65 @@ module ActiveSupport
130
137
 
131
138
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
132
139
 
133
- result.sub!(/\A_+/, "")
134
- unless keep_id_suffix
135
- result.sub!(/_id\z/, "")
136
- end
137
140
  result.tr!("_", " ")
141
+ result.lstrip!
142
+ if !keep_id_suffix && lower_case_and_underscored_word&.end_with?("_id")
143
+ result.delete_suffix!(" id")
144
+ end
138
145
 
139
- result.gsub!(/([a-z\d]*)/i) do |match|
140
- "#{inflections.acronyms[match.downcase] || match.downcase}"
146
+ result.gsub!(/([a-z\d]+)/i) do |match|
147
+ match.downcase!
148
+ inflections.acronyms[match] || match
141
149
  end
142
150
 
143
151
  if capitalize
144
- result.sub!(/\A\w/) { |match| match.upcase }
152
+ result.sub!(/\A\w/) do |match|
153
+ match.upcase!
154
+ match
155
+ end
145
156
  end
146
157
 
147
158
  result
148
159
  end
149
160
 
150
- # Converts just the first character to uppercase.
161
+ # Converts the first character in the string to uppercase.
151
162
  #
152
163
  # upcase_first('what a Lovely Day') # => "What a Lovely Day"
153
164
  # upcase_first('w') # => "W"
154
165
  # upcase_first('') # => ""
155
166
  def upcase_first(string)
156
- string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
167
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
168
+ end
169
+
170
+ # Converts the first character in the string to lowercase.
171
+ #
172
+ # downcase_first('If they enjoyed The Matrix') # => "if they enjoyed The Matrix"
173
+ # downcase_first('I') # => "i"
174
+ # downcase_first('') # => ""
175
+ def downcase_first(string)
176
+ string.length > 0 ? string[0].downcase.concat(string[1..-1]) : +""
157
177
  end
158
178
 
159
179
  # Capitalizes all the words and replaces some characters in the string to
160
180
  # create a nicer looking title. +titleize+ is meant for creating pretty
161
- # output. It is not used in the Rails internals.
181
+ # output. It is not used in the \Rails internals.
162
182
  #
163
183
  # The trailing '_id','Id'.. can be kept and capitalized by setting the
164
184
  # optional parameter +keep_id_suffix+ to true.
165
185
  # By default, this parameter is false.
166
186
  #
167
- # +titleize+ is also aliased as +titlecase+.
168
- #
169
187
  # titleize('man from the boondocks') # => "Man From The Boondocks"
170
188
  # titleize('x-men: the last stand') # => "X Men: The Last Stand"
171
189
  # titleize('TheManWithoutAPast') # => "The Man Without A Past"
172
190
  # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
173
191
  # titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
174
192
  def titleize(word, keep_id_suffix: false)
175
- humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`])[a-z]/) do |match|
193
+ humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])[a-z]/) do |match|
176
194
  match.capitalize
177
195
  end
178
196
  end
179
197
 
180
- # Creates the name of a table like Rails does for models to table names.
198
+ # Creates the name of a table like \Rails does for models to table names.
181
199
  # This method uses the #pluralize method on the last word in the string.
182
200
  #
183
201
  # tableize('RawScaledScorer') # => "raw_scaled_scorers"
@@ -187,9 +205,9 @@ module ActiveSupport
187
205
  pluralize(underscore(class_name))
188
206
  end
189
207
 
190
- # 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).
208
+ # Creates a class name from a plural table name like \Rails does for table
209
+ # names to models. Note that this returns a string and not a Class. (To
210
+ # convert to an actual class follow +classify+ with #constantize.)
193
211
  #
194
212
  # classify('ham_and_eggs') # => "HamAndEgg"
195
213
  # classify('posts') # => "Post"
@@ -220,7 +238,7 @@ module ActiveSupport
220
238
  def demodulize(path)
221
239
  path = path.to_s
222
240
  if i = path.rindex("::")
223
- path[(i + 2)..-1]
241
+ path[(i + 2), path.length]
224
242
  else
225
243
  path
226
244
  end
@@ -269,34 +287,7 @@ module ActiveSupport
269
287
  # NameError is raised when the name is not in CamelCase or the constant is
270
288
  # unknown.
271
289
  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
290
+ Object.const_get(camel_cased_word)
300
291
  end
301
292
 
302
293
  # Tries to find a constant with the name specified in the argument string.
@@ -326,10 +317,9 @@ module ActiveSupport
326
317
  rescue NameError => e
327
318
  raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
328
319
  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
320
  rescue LoadError => e
332
- raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
321
+ message = e.respond_to?(:original_message) ? e.original_message : e.message
322
+ raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message)
333
323
  end
334
324
 
335
325
  # Returns the suffix that should be added to a number to denote the position
@@ -367,11 +357,11 @@ module ActiveSupport
367
357
  def const_regexp(camel_cased_word)
368
358
  parts = camel_cased_word.split("::")
369
359
 
370
- return Regexp.escape(camel_cased_word) if parts.blank?
360
+ return Regexp.escape(camel_cased_word) if parts.empty?
371
361
 
372
362
  last = parts.pop
373
363
 
374
- parts.reverse.inject(last) do |acc, part|
364
+ parts.reverse!.inject(last) do |acc, part|
375
365
  part.empty? ? acc : "#{part}(::#{acc})?"
376
366
  end
377
367
  end
@@ -381,8 +371,8 @@ module ActiveSupport
381
371
  # If passed an optional +locale+ parameter, the uncountables will be
382
372
  # found for that locale.
383
373
  #
384
- # apply_inflections('post', inflections.plurals, :en) # => "posts"
385
- # apply_inflections('posts', inflections.singulars, :en) # => "post"
374
+ # apply_inflections('post', inflections.plurals, :en) # => "posts"
375
+ # apply_inflections('posts', inflections.singulars, :en) # => "post"
386
376
  def apply_inflections(word, rules, locale = :en)
387
377
  result = word.to_s.dup
388
378
 
@@ -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
  #
@@ -57,14 +59,14 @@ module ActiveSupport
57
59
  # transliterate('Jürgen', locale: :de)
58
60
  # # => "Juergen"
59
61
  #
60
- # Transliteration is restricted to UTF-8, US-ASCII and GB18030 strings
62
+ # Transliteration is restricted to UTF-8, US-ASCII, and GB18030 strings.
61
63
  # Other encodings will raise an ArgumentError.
62
64
  def transliterate(string, replacement = "?", locale: nil)
63
- string = string.dup if string.frozen?
64
65
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
66
+ raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
65
67
 
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)
68
+ return string.dup if string.ascii_only?
69
+ string = string.dup if string.frozen?
68
70
 
69
71
  input_encoding = string.encoding
70
72
 
@@ -117,7 +119,7 @@ module ActiveSupport
117
119
  # If the optional parameter +locale+ is specified,
118
120
  # the word will be parameterized as a word of that language.
119
121
  # By default, this parameter is set to <tt>nil</tt> and it will use
120
- # the configured <tt>I18n.locale<tt>.
122
+ # the configured <tt>I18n.locale</tt>.
121
123
  def parameterize(string, separator: "-", preserve_case: false, locale: nil)
122
124
  # Replace accented chars with their ASCII equivalents.
123
125
  parameterized_string = transliterate(string, locale: locale)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fiber"
4
+
5
+ module ActiveSupport
6
+ module IsolatedExecutionState # :nodoc:
7
+ @isolation_level = nil
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, :scope
14
+
15
+ def isolation_level=(level)
16
+ return if level == @isolation_level
17
+
18
+ unless %i(thread fiber).include?(level)
19
+ raise ArgumentError, "isolation_level must be `:thread` or `:fiber`, got: `#{level.inspect}`"
20
+ end
21
+
22
+ clear if @isolation_level
23
+
24
+ @scope =
25
+ case level
26
+ when :thread; Thread
27
+ when :fiber; Fiber
28
+ end
29
+
30
+ @isolation_level = level
31
+ end
32
+
33
+ def unique_id
34
+ self[:__id__] ||= Object.new
35
+ end
36
+
37
+ def [](key)
38
+ state[key]
39
+ end
40
+
41
+ def []=(key, value)
42
+ state[key] = value
43
+ end
44
+
45
+ def key?(key)
46
+ state.key?(key)
47
+ end
48
+
49
+ def delete(key)
50
+ state.delete(key)
51
+ end
52
+
53
+ def clear
54
+ state.clear
55
+ end
56
+
57
+ def context
58
+ scope.current
59
+ end
60
+
61
+ def share_with(other)
62
+ # Action Controller streaming spawns a new thread and copy thread locals.
63
+ # We do the same here for backward compatibility, but this is very much a hack
64
+ # and streaming should be rethought.
65
+ context.active_support_execution_state = other.active_support_execution_state.dup
66
+ end
67
+
68
+ private
69
+ def state
70
+ context.active_support_execution_state ||= {}
71
+ end
72
+ end
73
+
74
+ self.isolation_level = :thread
75
+ end
76
+ end
@@ -5,13 +5,13 @@ require "active_support/core_ext/module/delegation"
5
5
  require "json"
6
6
 
7
7
  module ActiveSupport
8
- # Look for and parse json strings that look like ISO 8601 times.
8
+ # Look for and parse JSON strings that look like ISO 8601 times.
9
9
  mattr_accessor :parse_json_times
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.
@@ -28,6 +28,7 @@ module ActiveSupport
28
28
  data
29
29
  end
30
30
  end
31
+ alias_method :load, :decode
31
32
 
32
33
  # Returns the class of the error that will be raised when there is an
33
34
  # error in decoding JSON. Using this method means you won't directly
@@ -63,8 +64,8 @@ module ActiveSupport
63
64
  when Array
64
65
  data.map! { |d| convert_dates_from(d) }
65
66
  when Hash
66
- data.each do |key, value|
67
- data[key] = convert_dates_from(value)
67
+ data.transform_values! do |value|
68
+ convert_dates_from(value)
68
69
  end
69
70
  else
70
71
  data
@@ -18,12 +18,15 @@ module ActiveSupport
18
18
  #
19
19
  # ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
20
20
  # # => "{\"team\":\"rails\",\"players\":\"36\"}"
21
- def self.encode(value, options = nil)
22
- Encoding.json_encoder.new(options).encode(value)
21
+ class << self
22
+ def encode(value, options = nil)
23
+ Encoding.json_encoder.new(options).encode(value)
24
+ end
25
+ alias_method :dump, :encode
23
26
  end
24
27
 
25
- module Encoding #:nodoc:
26
- class JSONGemEncoder #:nodoc:
28
+ module Encoding # :nodoc:
29
+ class JSONGemEncoder # :nodoc:
27
30
  attr_reader :options
28
31
 
29
32
  def initialize(options = nil)
@@ -32,49 +35,27 @@ module ActiveSupport
32
35
 
33
36
  # Encode the given object into a JSON string
34
37
  def encode(value)
35
- stringify jsonify value.as_json(options.dup)
36
- end
38
+ unless options.empty?
39
+ value = value.as_json(options.dup)
40
+ end
41
+ json = stringify(jsonify(value))
37
42
 
38
- private
39
43
  # Rails does more escaping than the JSON gem natively does (we
40
44
  # escape \u2028 and \u2029 and optionally >, <, & to work around
41
45
  # certain browser problems).
42
- ESCAPED_CHARS = {
43
- "\u2028" => '\u2028',
44
- "\u2029" => '\u2029',
45
- ">" => '\u003e',
46
- "<" => '\u003c',
47
- "&" => '\u0026',
48
- }
49
-
50
- ESCAPE_REGEX_WITH_HTML_ENTITIES = /[\u2028\u2029><&]/u
51
- ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
52
-
53
- # This class wraps all the strings we see and does the extra escaping
54
- class EscapedString < String #:nodoc:
55
- def to_json(*)
56
- if Encoding.escape_html_entities_in_json
57
- s = super
58
- s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
59
- s
60
- else
61
- s = super
62
- s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
63
- s
64
- end
65
- end
66
-
67
- def to_s
68
- self
69
- end
46
+ if Encoding.escape_html_entities_in_json
47
+ json.gsub!(">", '\u003e')
48
+ json.gsub!("<", '\u003c')
49
+ json.gsub!("&", '\u0026')
70
50
  end
51
+ json.gsub!("\u2028", '\u2028')
52
+ json.gsub!("\u2029", '\u2029')
53
+ json
54
+ end
71
55
 
72
- # Mark these as private so we don't leak encoding-specific constructs
73
- private_constant :ESCAPED_CHARS, :ESCAPE_REGEX_WITH_HTML_ENTITIES,
74
- :ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString
75
-
56
+ private
76
57
  # Convert an object into a "JSON-ready" representation composed of
77
- # primitives like Hash, Array, String, Numeric,
58
+ # primitives like Hash, Array, String, Symbol, Numeric,
78
59
  # and +true+/+false+/+nil+.
79
60
  # Recursively calls #as_json to the object to recursively build a
80
61
  # fully JSON-ready object.
@@ -88,12 +69,17 @@ module ActiveSupport
88
69
  # calls.
89
70
  def jsonify(value)
90
71
  case value
91
- when String
92
- EscapedString.new(value)
93
- when Numeric, NilClass, TrueClass, FalseClass
72
+ when String, Integer, Symbol, nil, true, false
73
+ value
74
+ when Numeric
94
75
  value.as_json
95
76
  when Hash
96
- Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
77
+ result = {}
78
+ value.each do |k, v|
79
+ k = k.to_s unless String === k
80
+ result[k] = jsonify(v)
81
+ end
82
+ result
97
83
  when Array
98
84
  value.map { |v| jsonify(v) }
99
85
  else
@@ -120,7 +106,7 @@ module ActiveSupport
120
106
  # Defaults to 3 (equivalent to millisecond precision)
121
107
  attr_accessor :time_precision
122
108
 
123
- # Sets the encoder used by Rails to encode Ruby objects into JSON strings
109
+ # Sets the encoder used by \Rails to encode Ruby objects into JSON strings
124
110
  # in +Object#to_json+ and +ActiveSupport::JSON.encode+.
125
111
  attr_accessor :json_encoder
126
112
  end
@@ -4,29 +4,54 @@ require "concurrent/map"
4
4
  require "openssl"
5
5
 
6
6
  module ActiveSupport
7
+ # = Key Generator
8
+ #
7
9
  # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2.
8
10
  # It can be used to derive a number of keys for various purposes from a given secret.
9
- # This lets Rails applications have a single secure secret, but avoid reusing that
11
+ # This lets \Rails applications have a single secure secret, but avoid reusing that
10
12
  # key in multiple incompatible contexts.
11
13
  class KeyGenerator
14
+ class << self
15
+ def hash_digest_class=(klass)
16
+ if klass.kind_of?(Class) && klass < OpenSSL::Digest
17
+ @hash_digest_class = klass
18
+ else
19
+ raise ArgumentError, "#{klass} is expected to be an OpenSSL::Digest subclass"
20
+ end
21
+ end
22
+
23
+ def hash_digest_class
24
+ @hash_digest_class ||= OpenSSL::Digest::SHA1
25
+ end
26
+ end
27
+
12
28
  def initialize(secret, options = {})
13
29
  @secret = secret
14
30
  # The default iterations are higher than required for our key derivation uses
15
31
  # on the off chance someone uses this for password storage
16
32
  @iterations = options[:iterations] || 2**16
33
+ # Also allow configuration here so people can use this to build a rotation
34
+ # scheme when switching the digest class.
35
+ @hash_digest_class = options[:hash_digest_class] || self.class.hash_digest_class
17
36
  end
18
37
 
19
- # Returns a derived key suitable for use. The default key_size is chosen
38
+ # Returns a derived key suitable for use. The default +key_size+ is chosen
20
39
  # to be compatible with the default settings of ActiveSupport::MessageVerifier.
21
- # i.e. OpenSSL::Digest::SHA1#block_length
40
+ # i.e. <tt>OpenSSL::Digest::SHA1#block_length</tt>
22
41
  def generate_key(salt, key_size = 64)
23
- OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
42
+ OpenSSL::PKCS5.pbkdf2_hmac(@secret, salt, @iterations, key_size, @hash_digest_class.new)
43
+ end
44
+
45
+ def inspect # :nodoc:
46
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
24
47
  end
25
48
  end
26
49
 
50
+ # = Caching Key Generator
51
+ #
27
52
  # 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.
53
+ # re-executing the key generation process when it's called using the same +salt+ and
54
+ # +key_size+.
30
55
  class CachingKeyGenerator
31
56
  def initialize(key_generator)
32
57
  @key_generator = key_generator
@@ -35,7 +60,7 @@ module ActiveSupport
35
60
 
36
61
  # Returns a derived key suitable for use.
37
62
  def generate_key(*args)
38
- @cache_keys[args.join] ||= @key_generator.generate_key(*args)
63
+ @cache_keys[args.join("|")] ||= @key_generator.generate_key(*args)
39
64
  end
40
65
  end
41
66
  end
@@ -1,14 +1,16 @@
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
+ # = Lazy Load Hooks
5
+ #
6
+ # LazyLoadHooks allows \Rails to lazily load a lot of components and thus
5
7
  # making the app boot faster. Because of this feature now there is no need to
6
- # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
8
+ # require +ActiveRecord::Base+ at boot time purely to apply
7
9
  # configuration. Instead a hook is registered that applies configuration once
8
- # <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
10
+ # +ActiveRecord::Base+ is loaded. Here +ActiveRecord::Base+ is
9
11
  # used as example but this feature can be applied elsewhere too.
10
12
  #
11
- # Here is an example where +on_load+ method is called to register a hook.
13
+ # Here is an example where on_load method is called to register a hook.
12
14
  #
13
15
  # initializer 'active_record.initialize_timezone' do
14
16
  # ActiveSupport.on_load(:active_record) do
@@ -18,10 +20,26 @@ module ActiveSupport
18
20
  # end
19
21
  #
20
22
  # When the entirety of +ActiveRecord::Base+ has been
21
- # evaluated then +run_load_hooks+ is invoked. The very last line of
23
+ # evaluated then run_load_hooks is invoked. The very last line of
22
24
  # +ActiveRecord::Base+ is:
23
25
  #
24
26
  # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
27
+ #
28
+ # run_load_hooks will then execute all the hooks that were registered
29
+ # with the on_load method. In the case of the above example, it will
30
+ # execute the block of code that is in the +initializer+.
31
+ #
32
+ # Registering a hook that has already run results in that hook executing
33
+ # immediately. This allows hooks to be nested for code that relies on
34
+ # multiple lazily loaded components:
35
+ #
36
+ # initializer "action_text.renderer" do
37
+ # ActiveSupport.on_load(:action_controller_base) do
38
+ # ActiveSupport.on_load(:action_text_content) do
39
+ # self.default_renderer = Class.new(ActionController::Base).renderer
40
+ # end
41
+ # end
42
+ # end
25
43
  module LazyLoadHooks
26
44
  def self.extended(base) # :nodoc:
27
45
  base.class_eval do
@@ -31,8 +49,9 @@ module ActiveSupport
31
49
  end
32
50
  end
33
51
 
34
- # Declares a block that will be executed when a Rails component is fully
35
- # loaded.
52
+ # Declares a block that will be executed when a \Rails component is fully
53
+ # loaded. If the component has already loaded, the block is executed
54
+ # immediately.
36
55
  #
37
56
  # Options:
38
57
  #
@@ -46,6 +65,13 @@ module ActiveSupport
46
65
  @load_hooks[name] << [block, options]
47
66
  end
48
67
 
68
+ # Executes all blocks registered to +name+ via on_load, using +base+ as the
69
+ # evaluation context.
70
+ #
71
+ # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
72
+ #
73
+ # In the case of the above example, it will execute all hooks registered
74
+ # for +:active_record+ within the class +ActiveRecord::Base+.
49
75
  def run_load_hooks(name, base = Object)
50
76
  @loaded[name] << base
51
77
  @load_hooks[name].each do |hook, options|