activesupport 6.0.6.1 → 7.1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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|