activesupport 5.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (268) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +343 -590
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -4
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +11 -5
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +45 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +81 -79
  13. data/lib/active_support/cache/memory_store.rb +69 -41
  14. data/lib/active_support/cache/null_store.rb +11 -4
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +74 -37
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +332 -161
  19. data/lib/active_support/callbacks.rb +657 -586
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +59 -19
  23. data/lib/active_support/configurable.rb +15 -17
  24. data/lib/active_support/configuration_file.rb +46 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +20 -18
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +3 -1
  30. data/lib/active_support/core_ext/array/inquiry.rb +3 -1
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +9 -7
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +6 -6
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -26
  39. data/lib/active_support/core_ext/class.rb +4 -2
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +3 -1
  42. data/lib/active_support/core_ext/date/calculations.rb +16 -13
  43. data/lib/active_support/core_ext/date/conversions.rb +23 -21
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +7 -5
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +82 -53
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -5
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +9 -9
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  51. data/lib/active_support/core_ext/date_time/calculations.rb +23 -11
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +15 -2
  53. data/lib/active_support/core_ext/date_time/conversions.rb +14 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +165 -29
  58. data/lib/active_support/core_ext/file/atomic.rb +7 -5
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +40 -39
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -2
  65. data/lib/active_support/core_ext/hash/keys.rb +9 -36
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +3 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +3 -1
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -4
  77. data/lib/active_support/core_ext/load_error.rb +2 -23
  78. data/lib/active_support/core_ext/marshal.rb +6 -2
  79. data/lib/active_support/core_ext/module/aliasing.rb +5 -48
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +7 -5
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +53 -59
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +31 -24
  84. data/lib/active_support/core_ext/module/concerning.rb +16 -11
  85. data/lib/active_support/core_ext/module/delegation.rb +159 -44
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -26
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +13 -12
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +129 -134
  94. data/lib/active_support/core_ext/numeric/time.rb +18 -26
  95. data/lib/active_support/core_ext/numeric.rb +5 -4
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +14 -2
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +4 -2
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -62
  101. data/lib/active_support/core_ext/object/inclusion.rb +3 -1
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +42 -15
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +20 -8
  107. data/lib/active_support/core_ext/object/with_options.rb +15 -2
  108. data/lib/active_support/core_ext/object.rb +14 -12
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +28 -6
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +47 -4
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +78 -29
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +10 -5
  126. data/lib/active_support/core_ext/string/output_safety.rb +86 -31
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +117 -45
  135. data/lib/active_support/core_ext/time/compatibility.rb +13 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +18 -12
  137. data/lib/active_support/core_ext/time/zones.rb +9 -7
  138. data/lib/active_support/core_ext/time.rb +7 -5
  139. data/lib/active_support/core_ext/uri.rb +12 -7
  140. data/lib/active_support/core_ext.rb +3 -2
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +208 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +7 -1
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  146. data/lib/active_support/dependencies.rb +172 -98
  147. data/lib/active_support/deprecation/behaviors.rb +45 -13
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +32 -17
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +35 -7
  153. data/lib/active_support/deprecation/reporting.rb +61 -16
  154. data/lib/active_support/deprecation.rb +17 -9
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +20 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +67 -66
  158. data/lib/active_support/duration/iso8601_serializer.rb +25 -17
  159. data/lib/active_support/duration.rb +349 -46
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +88 -112
  164. data/lib/active_support/execution_wrapper.rb +25 -13
  165. data/lib/active_support/executor.rb +3 -1
  166. data/lib/active_support/file_update_checker.rb +56 -51
  167. data/lib/active_support/fork_tracker.rb +62 -0
  168. data/lib/active_support/gem_version.rb +4 -2
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +153 -49
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +30 -20
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +28 -15
  175. data/lib/active_support/inflector/methods.rb +120 -109
  176. data/lib/active_support/inflector/transliterate.rb +60 -25
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +30 -29
  179. data/lib/active_support/json/encoding.rb +22 -11
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +6 -36
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +11 -9
  186. data/lib/active_support/log_subscriber.rb +51 -18
  187. data/lib/active_support/logger.rb +9 -22
  188. data/lib/active_support/logger_silence.rb +14 -21
  189. data/lib/active_support/logger_thread_safe_level.rb +55 -8
  190. data/lib/active_support/message_encryptor.rb +170 -53
  191. data/lib/active_support/message_verifier.rb +91 -20
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +24 -78
  196. data/lib/active_support/multibyte/unicode.rb +21 -352
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +121 -19
  199. data/lib/active_support/notifications/instrumenter.rb +78 -14
  200. data/lib/active_support/notifications.rb +80 -12
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -3
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -12
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -13
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -4
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +18 -55
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +45 -16
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +6 -4
  213. data/lib/active_support/ordered_options.rb +23 -9
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +7 -5
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +8 -9
  218. data/lib/active_support/railtie.rb +62 -11
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +20 -11
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +12 -3
  224. data/lib/active_support/subscriber.rb +77 -23
  225. data/lib/active_support/tagged_logging.rb +52 -17
  226. data/lib/active_support/test_case.rb +106 -29
  227. data/lib/active_support/testing/assertions.rb +144 -8
  228. data/lib/active_support/testing/autorun.rb +5 -10
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +4 -2
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +19 -24
  234. data/lib/active_support/testing/method_call_assertions.rb +31 -2
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  239. data/lib/active_support/testing/stream.rb +30 -29
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +125 -24
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +142 -55
  244. data/lib/active_support/values/time_zone.rb +160 -53
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +115 -114
  247. data/lib/active_support/xml_mini/libxml.rb +15 -14
  248. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  249. data/lib/active_support/xml_mini/nokogiri.rb +13 -13
  250. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +44 -42
  253. data/lib/active_support.rb +19 -10
  254. metadata +79 -37
  255. data/lib/active_support/concurrency/latch.rb +0 -19
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/hash/compact.rb +0 -20
  258. data/lib/active_support/core_ext/hash/transform_values.rb +0 -29
  259. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  260. data/lib/active_support/core_ext/kernel/debugger.rb +0 -3
  261. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -3
  262. data/lib/active_support/core_ext/module/qualified_const.rb +0 -70
  263. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  264. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  265. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  266. data/lib/active_support/core_ext/struct.rb +0 -3
  267. data/lib/active_support/core_ext/time/marshal.rb +0 -3
  268. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,7 +1,10 @@
1
- require 'active_support/json'
2
- require 'active_support/core_ext/string/access'
3
- require 'active_support/core_ext/string/behavior'
4
- require 'active_support/core_ext/module/delegation'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/json"
4
+ require "active_support/core_ext/string/access"
5
+ require "active_support/core_ext/string/behavior"
6
+ require "active_support/core_ext/symbol/starts_ends_with"
7
+ require "active_support/core_ext/module/delegation"
5
8
 
6
9
  module ActiveSupport #:nodoc:
7
10
  module Multibyte #:nodoc:
@@ -15,7 +18,8 @@ module ActiveSupport #:nodoc:
15
18
  # through the +mb_chars+ method. Methods which would normally return a
16
19
  # String object now return a Chars object so methods can be chained.
17
20
  #
18
- # 'The Perfect String '.mb_chars.downcase.strip.normalize # => "the perfect string"
21
+ # 'The Perfect String '.mb_chars.downcase.strip
22
+ # # => #<ActiveSupport::Multibyte::Chars:0x007fdc434ccc10 @wrapped_string="the perfect string">
19
23
  #
20
24
  # Chars objects are perfectly interchangeable with String objects as long as
21
25
  # no explicit class checks are made. If certain methods do explicitly check
@@ -45,7 +49,7 @@ module ActiveSupport #:nodoc:
45
49
  alias to_s wrapped_string
46
50
  alias to_str wrapped_string
47
51
 
48
- delegate :<=>, :=~, :acts_like_string?, :to => :wrapped_string
52
+ delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string
49
53
 
50
54
  # Creates a new Chars instance by wrapping _string_.
51
55
  def initialize(string)
@@ -56,7 +60,7 @@ module ActiveSupport #:nodoc:
56
60
  # Forward all undefined methods to the wrapped string.
57
61
  def method_missing(method, *args, &block)
58
62
  result = @wrapped_string.__send__(method, *args, &block)
59
- if method.to_s =~ /!$/
63
+ if method.end_with?("!")
60
64
  self if result
61
65
  else
62
66
  result.kind_of?(String) ? chars(result) : result
@@ -70,12 +74,6 @@ module ActiveSupport #:nodoc:
70
74
  @wrapped_string.respond_to?(method, include_private)
71
75
  end
72
76
 
73
- # Returns +true+ when the proxy class can handle the string. Returns
74
- # +false+ otherwise.
75
- def self.consumes?(string)
76
- string.encoding == Encoding::UTF_8
77
- end
78
-
79
77
  # Works just like <tt>String#split</tt>, with the exception that the items
80
78
  # in the resulting list are Chars instances instead of String. This makes
81
79
  # chaining methods easier.
@@ -86,7 +84,7 @@ module ActiveSupport #:nodoc:
86
84
  end
87
85
 
88
86
  # Works like <tt>String#slice!</tt>, but returns an instance of
89
- # Chars, or nil if the string was not modified. The string will not be
87
+ # Chars, or +nil+ if the string was not modified. The string will not be
90
88
  # modified if the range given is out of bounds
91
89
  #
92
90
  # string = 'Welcome'
@@ -105,7 +103,7 @@ module ActiveSupport #:nodoc:
105
103
  #
106
104
  # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
107
105
  def reverse
108
- chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*'))
106
+ chars(@wrapped_string.scan(/\X/).reverse.join)
109
107
  end
110
108
 
111
109
  # Limits the byte size of the string to a number of bytes without breaking
@@ -114,63 +112,24 @@ module ActiveSupport #:nodoc:
114
112
  #
115
113
  # 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
116
114
  def limit(limit)
117
- slice(0...translate_offset(limit))
118
- end
119
-
120
- # Converts characters in the string to uppercase.
121
- #
122
- # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
123
- def upcase
124
- chars Unicode.upcase(@wrapped_string)
125
- end
126
-
127
- # Converts characters in the string to lowercase.
128
- #
129
- # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
130
- def downcase
131
- chars Unicode.downcase(@wrapped_string)
132
- end
133
-
134
- # Converts characters in the string to the opposite case.
135
- #
136
- # 'El Cañón".mb_chars.swapcase.to_s # => "eL cAÑÓN"
137
- def swapcase
138
- chars Unicode.swapcase(@wrapped_string)
139
- end
140
-
141
- # Converts the first character to uppercase and the remainder to lowercase.
142
- #
143
- # 'über'.mb_chars.capitalize.to_s # => "Über"
144
- def capitalize
145
- (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
115
+ chars(@wrapped_string.truncate_bytes(limit, omission: nil))
146
116
  end
147
117
 
148
118
  # Capitalizes the first letter of every word, when possible.
149
119
  #
150
- # "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
151
- # "日本語".mb_chars.titleize # => "日本語"
120
+ # "ÉL QUE SE ENTERÓ".mb_chars.titleize.to_s # => "Él Que Se Enteró"
121
+ # "日本語".mb_chars.titleize.to_s # => "日本語"
152
122
  def titleize
153
- chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1)})
123
+ chars(downcase.to_s.gsub(/\b('?\S)/u) { $1.upcase })
154
124
  end
155
125
  alias_method :titlecase, :titleize
156
126
 
157
- # Returns the KC normalization of the string by default. NFKC is
158
- # considered the best normalization form for passing strings to databases
159
- # and validations.
160
- #
161
- # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
162
- # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
163
- # ActiveSupport::Multibyte::Unicode.default_normalization_form
164
- def normalize(form = nil)
165
- chars(Unicode.normalize(@wrapped_string, form))
166
- end
167
-
168
127
  # Performs canonical decomposition on all the characters.
169
128
  #
170
129
  # 'é'.length # => 2
171
130
  # 'é'.mb_chars.decompose.to_s.length # => 3
172
131
  def decompose
173
- chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack('U*'))
132
+ chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack("U*"))
174
133
  end
175
134
 
176
135
  # Performs composition on all the characters.
@@ -178,7 +137,7 @@ module ActiveSupport #:nodoc:
178
137
  # 'é'.length # => 3
179
138
  # 'é'.mb_chars.compose.to_s.length # => 2
180
139
  def compose
181
- chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack('U*'))
140
+ chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack("U*"))
182
141
  end
183
142
 
184
143
  # Returns the number of grapheme clusters in the string.
@@ -186,7 +145,7 @@ module ActiveSupport #:nodoc:
186
145
  # 'क्षि'.mb_chars.length # => 4
187
146
  # 'क्षि'.mb_chars.grapheme_length # => 3
188
147
  def grapheme_length
189
- Unicode.unpack_graphemes(@wrapped_string).length
148
+ @wrapped_string.scan(/\X/).length
190
149
  end
191
150
 
192
151
  # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
@@ -202,28 +161,15 @@ module ActiveSupport #:nodoc:
202
161
  to_s.as_json(options)
203
162
  end
204
163
 
205
- %w(capitalize downcase reverse tidy_bytes upcase).each do |method|
164
+ %w(reverse tidy_bytes).each do |method|
206
165
  define_method("#{method}!") do |*args|
207
- @wrapped_string = send(method, *args).to_s
166
+ @wrapped_string = public_send(method, *args).to_s
208
167
  self
209
168
  end
210
169
  end
211
170
 
212
- protected
213
-
214
- def translate_offset(byte_offset) #:nodoc:
215
- return nil if byte_offset.nil?
216
- return 0 if @wrapped_string == ''
217
-
218
- begin
219
- @wrapped_string.byteslice(0...byte_offset).unpack('U*').length
220
- rescue ArgumentError
221
- byte_offset -= 1
222
- retry
223
- end
224
- end
225
-
226
- def chars(string) #:nodoc:
171
+ private
172
+ def chars(string)
227
173
  self.class.new(string)
228
174
  end
229
175
  end
@@ -1,238 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  module Multibyte
3
5
  module Unicode
4
-
5
6
  extend self
6
7
 
7
- # A list of all available normalization forms.
8
- # See http://www.unicode.org/reports/tr15/tr15-29.html for more
9
- # information about normalization.
10
- NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
11
-
12
8
  # The Unicode version that is supported by the implementation
13
- UNICODE_VERSION = '8.0.0'
14
-
15
- # The default normalization used for operations that require
16
- # normalization. It can be set to any of the normalizations
17
- # in NORMALIZATION_FORMS.
18
- #
19
- # ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
20
- attr_accessor :default_normalization_form
21
- @default_normalization_form = :kc
22
-
23
- # Hangul character boundaries and properties
24
- HANGUL_SBASE = 0xAC00
25
- HANGUL_LBASE = 0x1100
26
- HANGUL_VBASE = 0x1161
27
- HANGUL_TBASE = 0x11A7
28
- HANGUL_LCOUNT = 19
29
- HANGUL_VCOUNT = 21
30
- HANGUL_TCOUNT = 28
31
- HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
32
- HANGUL_SCOUNT = 11172
33
- HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
34
- HANGUL_JAMO_FIRST = 0x1100
35
- HANGUL_JAMO_LAST = 0x11FF
36
-
37
- # All the unicode whitespace
38
- WHITESPACE = [
39
- (0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
40
- 0x0020, # White_Space # Zs SPACE
41
- 0x0085, # White_Space # Cc <control-0085>
42
- 0x00A0, # White_Space # Zs NO-BREAK SPACE
43
- 0x1680, # White_Space # Zs OGHAM SPACE MARK
44
- (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
45
- 0x2028, # White_Space # Zl LINE SEPARATOR
46
- 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
47
- 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
48
- 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
49
- 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
50
- ].flatten.freeze
51
-
52
- # BOM (byte order mark) can also be seen as whitespace, it's a
53
- # non-rendering character used to distinguish between little and big
54
- # endian. This is not an issue in utf-8, so it must be ignored.
55
- LEADERS_AND_TRAILERS = WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
56
-
57
- # Returns a regular expression pattern that matches the passed Unicode
58
- # codepoints.
59
- def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
60
- array_of_codepoints.collect{ |e| [e].pack 'U*'.freeze }.join('|'.freeze)
61
- end
62
- TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
63
- LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
64
-
65
- # Detect whether the codepoint is in a certain character class. Returns
66
- # +true+ when it's in the specified character class and +false+ otherwise.
67
- # Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
68
- # <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
69
- #
70
- # Primarily used by the grapheme cluster support.
71
- def in_char_class?(codepoint, classes)
72
- classes.detect { |c| database.boundary[c] === codepoint } ? true : false
73
- end
74
-
75
- # Unpack the string at grapheme boundaries. Returns a list of character
76
- # lists.
77
- #
78
- # Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
79
- # Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
80
- def unpack_graphemes(string)
81
- codepoints = string.codepoints.to_a
82
- unpacked = []
83
- pos = 0
84
- marker = 0
85
- eoc = codepoints.length
86
- while(pos < eoc)
87
- pos += 1
88
- previous = codepoints[pos-1]
89
- current = codepoints[pos]
90
-
91
- should_break =
92
- # GB3. CR X LF
93
- if previous == database.boundary[:cr] and current == database.boundary[:lf]
94
- false
95
- # GB4. (Control|CR|LF) ÷
96
- elsif previous and in_char_class?(previous, [:control,:cr,:lf])
97
- true
98
- # GB5. ÷ (Control|CR|LF)
99
- elsif in_char_class?(current, [:control,:cr,:lf])
100
- true
101
- # GB6. L X (L|V|LV|LVT)
102
- elsif database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt])
103
- false
104
- # GB7. (LV|V) X (V|T)
105
- elsif in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t])
106
- false
107
- # GB8. (LVT|T) X (T)
108
- elsif in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current
109
- false
110
- # GB8a. Regional_Indicator X Regional_Indicator
111
- elsif database.boundary[:regional_indicator] === previous and database.boundary[:regional_indicator] === current
112
- false
113
- # GB9. X Extend
114
- elsif database.boundary[:extend] === current
115
- false
116
- # GB9a. X SpacingMark
117
- elsif database.boundary[:spacingmark] === current
118
- false
119
- # GB9b. Prepend X
120
- elsif database.boundary[:prepend] === previous
121
- false
122
- # GB10. Any ÷ Any
123
- else
124
- true
125
- end
126
-
127
- if should_break
128
- unpacked << codepoints[marker..pos-1]
129
- marker = pos
130
- end
131
- end
132
- unpacked
133
- end
9
+ UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"]
134
10
 
135
- # Reverse operation of unpack_graphemes.
136
- #
137
- # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
138
- def pack_graphemes(unpacked)
139
- unpacked.flatten.pack('U*')
11
+ def default_normalization_form
12
+ ActiveSupport::Deprecation.warn(
13
+ "ActiveSupport::Multibyte::Unicode.default_normalization_form is deprecated and will be removed in Rails 6.2."
14
+ )
140
15
  end
141
16
 
142
- # Re-order codepoints so the string becomes canonical.
143
- def reorder_characters(codepoints)
144
- length = codepoints.length- 1
145
- pos = 0
146
- while pos < length do
147
- cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos+1]]
148
- if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
149
- codepoints[pos..pos+1] = cp2.code, cp1.code
150
- pos += (pos > 0 ? -1 : 1)
151
- else
152
- pos += 1
153
- end
154
- end
155
- codepoints
17
+ def default_normalization_form=(_)
18
+ ActiveSupport::Deprecation.warn(
19
+ "ActiveSupport::Multibyte::Unicode.default_normalization_form= is deprecated and will be removed in Rails 6.2."
20
+ )
156
21
  end
157
22
 
158
23
  # Decompose composed characters to the decomposed form.
159
24
  def decompose(type, codepoints)
160
- codepoints.inject([]) do |decomposed, cp|
161
- # if it's a hangul syllable starter character
162
- if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
163
- sindex = cp - HANGUL_SBASE
164
- ncp = [] # new codepoints
165
- ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
166
- ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
167
- tindex = sindex % HANGUL_TCOUNT
168
- ncp << (HANGUL_TBASE + tindex) unless tindex == 0
169
- decomposed.concat ncp
170
- # if the codepoint is decomposable in with the current decomposition type
171
- elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatibility)
172
- decomposed.concat decompose(type, ncp.dup)
173
- else
174
- decomposed << cp
175
- end
25
+ if type == :compatibility
26
+ codepoints.pack("U*").unicode_normalize(:nfkd).codepoints
27
+ else
28
+ codepoints.pack("U*").unicode_normalize(:nfd).codepoints
176
29
  end
177
30
  end
178
31
 
179
32
  # Compose decomposed characters to the composed form.
180
33
  def compose(codepoints)
181
- pos = 0
182
- eoa = codepoints.length - 1
183
- starter_pos = 0
184
- starter_char = codepoints[0]
185
- previous_combining_class = -1
186
- while pos < eoa
187
- pos += 1
188
- lindex = starter_char - HANGUL_LBASE
189
- # -- Hangul
190
- if 0 <= lindex and lindex < HANGUL_LCOUNT
191
- vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
192
- if 0 <= vindex and vindex < HANGUL_VCOUNT
193
- tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
194
- if 0 <= tindex and tindex < HANGUL_TCOUNT
195
- j = starter_pos + 2
196
- eoa -= 2
197
- else
198
- tindex = 0
199
- j = starter_pos + 1
200
- eoa -= 1
201
- end
202
- codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
203
- end
204
- starter_pos += 1
205
- starter_char = codepoints[starter_pos]
206
- # -- Other characters
207
- else
208
- current_char = codepoints[pos]
209
- current = database.codepoints[current_char]
210
- if current.combining_class > previous_combining_class
211
- if ref = database.composition_map[starter_char]
212
- composition = ref[current_char]
213
- else
214
- composition = nil
215
- end
216
- unless composition.nil?
217
- codepoints[starter_pos] = composition
218
- starter_char = composition
219
- codepoints.delete_at pos
220
- eoa -= 1
221
- pos -= 1
222
- previous_combining_class = -1
223
- else
224
- previous_combining_class = current.combining_class
225
- end
226
- else
227
- previous_combining_class = current.combining_class
228
- end
229
- if current.combining_class == 0
230
- starter_pos = pos
231
- starter_char = codepoints[pos]
232
- end
233
- end
234
- end
235
- codepoints
34
+ codepoints.pack("U*").unicode_normalize(:nfc).codepoints
236
35
  end
237
36
 
238
37
  # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
@@ -243,7 +42,7 @@ module ActiveSupport
243
42
  # Passing +true+ will forcibly tidy all bytes, assuming that the string's
244
43
  # encoding is entirely CP1252 or ISO-8859-1.
245
44
  def tidy_bytes(string, force = false)
246
- return string if string.empty?
45
+ return string if string.empty? || string.ascii_only?
247
46
  return recode_windows1252_chars(string) if force
248
47
  string.scrub { |bad| recode_windows1252_chars(bad) }
249
48
  end
@@ -259,7 +58,7 @@ module ActiveSupport
259
58
  reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
260
59
 
261
60
  source = string.dup
262
- out = ''.force_encoding(Encoding::UTF_16LE)
61
+ out = "".force_encoding(Encoding::UTF_16LE)
263
62
 
264
63
  loop do
265
64
  reader.primitive_convert(source, out)
@@ -274,140 +73,10 @@ module ActiveSupport
274
73
  end
275
74
  end
276
75
 
277
- # Returns the KC normalization of the string by default. NFKC is
278
- # considered the best normalization form for passing strings to databases
279
- # and validations.
280
- #
281
- # * <tt>string</tt> - The string to perform normalization on.
282
- # * <tt>form</tt> - The form you want to normalize in. Should be one of
283
- # the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
284
- # Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
285
- def normalize(string, form=nil)
286
- form ||= @default_normalization_form
287
- # See http://www.unicode.org/reports/tr15, Table 1
288
- codepoints = string.codepoints.to_a
289
- case form
290
- when :d
291
- reorder_characters(decompose(:canonical, codepoints))
292
- when :c
293
- compose(reorder_characters(decompose(:canonical, codepoints)))
294
- when :kd
295
- reorder_characters(decompose(:compatibility, codepoints))
296
- when :kc
297
- compose(reorder_characters(decompose(:compatibility, codepoints)))
298
- else
299
- raise ArgumentError, "#{form} is not a valid normalization variant", caller
300
- end.pack('U*'.freeze)
301
- end
302
-
303
- def downcase(string)
304
- apply_mapping string, :lowercase_mapping
305
- end
306
-
307
- def upcase(string)
308
- apply_mapping string, :uppercase_mapping
309
- end
310
-
311
- def swapcase(string)
312
- apply_mapping string, :swapcase_mapping
313
- end
314
-
315
- # Holds data about a codepoint in the Unicode database.
316
- class Codepoint
317
- attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
318
-
319
- # Initializing Codepoint object with default values
320
- def initialize
321
- @combining_class = 0
322
- @uppercase_mapping = 0
323
- @lowercase_mapping = 0
324
- end
325
-
326
- def swapcase_mapping
327
- uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
328
- end
329
- end
330
-
331
- # Holds static data from the Unicode database.
332
- class UnicodeDatabase
333
- ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
334
-
335
- attr_writer(*ATTRIBUTES)
336
-
337
- def initialize
338
- @codepoints = Hash.new(Codepoint.new)
339
- @composition_exclusion = []
340
- @composition_map = {}
341
- @boundary = {}
342
- @cp1252 = {}
343
- end
344
-
345
- # Lazy load the Unicode database so it's only loaded when it's actually used
346
- ATTRIBUTES.each do |attr_name|
347
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
348
- def #{attr_name} # def codepoints
349
- load # load
350
- @#{attr_name} # @codepoints
351
- end # end
352
- EOS
353
- end
354
-
355
- # Loads the Unicode database and returns all the internal objects of
356
- # UnicodeDatabase.
357
- def load
358
- begin
359
- @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
360
- rescue => e
361
- raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
362
- end
363
-
364
- # Redefine the === method so we can write shorter rules for grapheme cluster breaks
365
- @boundary.each_key do |k|
366
- @boundary[k].instance_eval do
367
- def ===(other)
368
- detect { |i| i === other } ? true : false
369
- end
370
- end if @boundary[k].kind_of?(Array)
371
- end
372
-
373
- # define attr_reader methods for the instance variables
374
- class << self
375
- attr_reader(*ATTRIBUTES)
376
- end
377
- end
378
-
379
- # Returns the directory in which the data files are stored.
380
- def self.dirname
381
- File.dirname(__FILE__) + '/../values/'
382
- end
383
-
384
- # Returns the filename for the data file for this version.
385
- def self.filename
386
- File.expand_path File.join(dirname, "unicode_tables.dat")
387
- end
388
- end
389
-
390
76
  private
391
-
392
- def apply_mapping(string, mapping) #:nodoc:
393
- database.codepoints
394
- string.each_codepoint.map do |codepoint|
395
- cp = database.codepoints[codepoint]
396
- if cp and (ncp = cp.send(mapping)) and ncp > 0
397
- ncp
398
- else
399
- codepoint
400
- end
401
- end.pack('U*')
402
- end
403
-
404
- def recode_windows1252_chars(string)
405
- string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
406
- end
407
-
408
- def database
409
- @database ||= UnicodeDatabase.new
410
- end
77
+ def recode_windows1252_chars(string)
78
+ string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
79
+ end
411
80
  end
412
81
  end
413
82
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport #:nodoc:
2
4
  module Multibyte
3
- autoload :Chars, 'active_support/multibyte/chars'
4
- autoload :Unicode, 'active_support/multibyte/unicode'
5
+ autoload :Chars, "active_support/multibyte/chars"
6
+ autoload :Unicode, "active_support/multibyte/unicode"
5
7
 
6
8
  # The proxy class returned when calling mb_chars. You can use this accessor
7
9
  # to configure your own proxy class so you can support other encodings. See