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