activesupport 5.2.4.4 → 6.1.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 +353 -435
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support.rb +14 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache.rb +142 -78
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +32 -20
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +70 -43
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +70 -3
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/enumerable.rb +171 -75
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +14 -2
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +34 -13
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +70 -13
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +6 -1
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/dependencies.rb +109 -34
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/deprecation/behaviors.rb +16 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +22 -4
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +64 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +49 -58
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +50 -6
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/rescuable.rb +4 -4
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +72 -28
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +30 -9
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +51 -0
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +32 -17
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +10 -3
- metadata +58 -32
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -5,215 +5,33 @@ module ActiveSupport
|
|
5
5
|
module Unicode
|
6
6
|
extend self
|
7
7
|
|
8
|
-
# A list of all available normalization forms.
|
9
|
-
# See http://www.unicode.org/reports/tr15/tr15-29.html for more
|
10
|
-
# information about normalization.
|
11
|
-
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
|
12
|
-
|
13
8
|
# The Unicode version that is supported by the implementation
|
14
|
-
UNICODE_VERSION = "
|
15
|
-
|
16
|
-
# The default normalization used for operations that require
|
17
|
-
# normalization. It can be set to any of the normalizations
|
18
|
-
# in NORMALIZATION_FORMS.
|
19
|
-
#
|
20
|
-
# ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
|
21
|
-
attr_accessor :default_normalization_form
|
22
|
-
@default_normalization_form = :kc
|
23
|
-
|
24
|
-
# Hangul character boundaries and properties
|
25
|
-
HANGUL_SBASE = 0xAC00
|
26
|
-
HANGUL_LBASE = 0x1100
|
27
|
-
HANGUL_VBASE = 0x1161
|
28
|
-
HANGUL_TBASE = 0x11A7
|
29
|
-
HANGUL_LCOUNT = 19
|
30
|
-
HANGUL_VCOUNT = 21
|
31
|
-
HANGUL_TCOUNT = 28
|
32
|
-
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
|
33
|
-
HANGUL_SCOUNT = 11172
|
34
|
-
HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
|
35
|
-
|
36
|
-
# Detect whether the codepoint is in a certain character class. Returns
|
37
|
-
# +true+ when it's in the specified character class and +false+ otherwise.
|
38
|
-
# Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
|
39
|
-
# <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
|
40
|
-
#
|
41
|
-
# Primarily used by the grapheme cluster support.
|
42
|
-
def in_char_class?(codepoint, classes)
|
43
|
-
classes.detect { |c| database.boundary[c] === codepoint } ? true : false
|
44
|
-
end
|
45
|
-
|
46
|
-
# Unpack the string at grapheme boundaries. Returns a list of character
|
47
|
-
# lists.
|
48
|
-
#
|
49
|
-
# Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
|
50
|
-
# Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
|
51
|
-
def unpack_graphemes(string)
|
52
|
-
codepoints = string.codepoints.to_a
|
53
|
-
unpacked = []
|
54
|
-
pos = 0
|
55
|
-
marker = 0
|
56
|
-
eoc = codepoints.length
|
57
|
-
while (pos < eoc)
|
58
|
-
pos += 1
|
59
|
-
previous = codepoints[pos - 1]
|
60
|
-
current = codepoints[pos]
|
61
|
-
|
62
|
-
# See http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
|
63
|
-
should_break =
|
64
|
-
if pos == eoc
|
65
|
-
true
|
66
|
-
# GB3. CR X LF
|
67
|
-
elsif previous == database.boundary[:cr] && current == database.boundary[:lf]
|
68
|
-
false
|
69
|
-
# GB4. (Control|CR|LF) ÷
|
70
|
-
elsif previous && in_char_class?(previous, [:control, :cr, :lf])
|
71
|
-
true
|
72
|
-
# GB5. ÷ (Control|CR|LF)
|
73
|
-
elsif in_char_class?(current, [:control, :cr, :lf])
|
74
|
-
true
|
75
|
-
# GB6. L X (L|V|LV|LVT)
|
76
|
-
elsif database.boundary[:l] === previous && in_char_class?(current, [:l, :v, :lv, :lvt])
|
77
|
-
false
|
78
|
-
# GB7. (LV|V) X (V|T)
|
79
|
-
elsif in_char_class?(previous, [:lv, :v]) && in_char_class?(current, [:v, :t])
|
80
|
-
false
|
81
|
-
# GB8. (LVT|T) X (T)
|
82
|
-
elsif in_char_class?(previous, [:lvt, :t]) && database.boundary[:t] === current
|
83
|
-
false
|
84
|
-
# GB9. X (Extend | ZWJ)
|
85
|
-
elsif in_char_class?(current, [:extend, :zwj])
|
86
|
-
false
|
87
|
-
# GB9a. X SpacingMark
|
88
|
-
elsif database.boundary[:spacingmark] === current
|
89
|
-
false
|
90
|
-
# GB9b. Prepend X
|
91
|
-
elsif database.boundary[:prepend] === previous
|
92
|
-
false
|
93
|
-
# GB10. (E_Base | EBG) Extend* X E_Modifier
|
94
|
-
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
|
95
|
-
false
|
96
|
-
# GB11. ZWJ X (Glue_After_Zwj | EBG)
|
97
|
-
elsif database.boundary[:zwj] === previous && in_char_class?(current, [:glue_after_zwj, :e_base_gaz])
|
98
|
-
false
|
99
|
-
# GB12. ^ (RI RI)* RI X RI
|
100
|
-
# GB13. [^RI] (RI RI)* RI X RI
|
101
|
-
elsif codepoints[marker..pos].all? { |c| database.boundary[:regional_indicator] === c } && codepoints[marker..pos].count { |c| database.boundary[:regional_indicator] === c }.even?
|
102
|
-
false
|
103
|
-
# GB999. Any ÷ Any
|
104
|
-
else
|
105
|
-
true
|
106
|
-
end
|
9
|
+
UNICODE_VERSION = RbConfig::CONFIG["UNICODE_VERSION"]
|
107
10
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
113
|
-
unpacked
|
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
|
+
)
|
114
15
|
end
|
115
16
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
unpacked.flatten.pack("U*")
|
121
|
-
end
|
122
|
-
|
123
|
-
# Re-order codepoints so the string becomes canonical.
|
124
|
-
def reorder_characters(codepoints)
|
125
|
-
length = codepoints.length - 1
|
126
|
-
pos = 0
|
127
|
-
while pos < length do
|
128
|
-
cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos + 1]]
|
129
|
-
if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
|
130
|
-
codepoints[pos..pos + 1] = cp2.code, cp1.code
|
131
|
-
pos += (pos > 0 ? -1 : 1)
|
132
|
-
else
|
133
|
-
pos += 1
|
134
|
-
end
|
135
|
-
end
|
136
|
-
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
|
+
)
|
137
21
|
end
|
138
22
|
|
139
23
|
# Decompose composed characters to the decomposed form.
|
140
24
|
def decompose(type, codepoints)
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
ncp = [] # new codepoints
|
146
|
-
ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
|
147
|
-
ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
|
148
|
-
tindex = sindex % HANGUL_TCOUNT
|
149
|
-
ncp << (HANGUL_TBASE + tindex) unless tindex == 0
|
150
|
-
decomposed.concat ncp
|
151
|
-
# if the codepoint is decomposable in with the current decomposition type
|
152
|
-
elsif (ncp = database.codepoints[cp].decomp_mapping) && (!database.codepoints[cp].decomp_type || type == :compatibility)
|
153
|
-
decomposed.concat decompose(type, ncp.dup)
|
154
|
-
else
|
155
|
-
decomposed << cp
|
156
|
-
end
|
25
|
+
if type == :compatibility
|
26
|
+
codepoints.pack("U*").unicode_normalize(:nfkd).codepoints
|
27
|
+
else
|
28
|
+
codepoints.pack("U*").unicode_normalize(:nfd).codepoints
|
157
29
|
end
|
158
30
|
end
|
159
31
|
|
160
32
|
# Compose decomposed characters to the composed form.
|
161
33
|
def compose(codepoints)
|
162
|
-
|
163
|
-
eoa = codepoints.length - 1
|
164
|
-
starter_pos = 0
|
165
|
-
starter_char = codepoints[0]
|
166
|
-
previous_combining_class = -1
|
167
|
-
while pos < eoa
|
168
|
-
pos += 1
|
169
|
-
lindex = starter_char - HANGUL_LBASE
|
170
|
-
# -- Hangul
|
171
|
-
if 0 <= lindex && lindex < HANGUL_LCOUNT
|
172
|
-
vindex = codepoints[starter_pos + 1] - HANGUL_VBASE rescue vindex = -1
|
173
|
-
if 0 <= vindex && vindex < HANGUL_VCOUNT
|
174
|
-
tindex = codepoints[starter_pos + 2] - HANGUL_TBASE rescue tindex = -1
|
175
|
-
if 0 <= tindex && tindex < HANGUL_TCOUNT
|
176
|
-
j = starter_pos + 2
|
177
|
-
eoa -= 2
|
178
|
-
else
|
179
|
-
tindex = 0
|
180
|
-
j = starter_pos + 1
|
181
|
-
eoa -= 1
|
182
|
-
end
|
183
|
-
codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
|
184
|
-
end
|
185
|
-
starter_pos += 1
|
186
|
-
starter_char = codepoints[starter_pos]
|
187
|
-
# -- Other characters
|
188
|
-
else
|
189
|
-
current_char = codepoints[pos]
|
190
|
-
current = database.codepoints[current_char]
|
191
|
-
if current.combining_class > previous_combining_class
|
192
|
-
if ref = database.composition_map[starter_char]
|
193
|
-
composition = ref[current_char]
|
194
|
-
else
|
195
|
-
composition = nil
|
196
|
-
end
|
197
|
-
unless composition.nil?
|
198
|
-
codepoints[starter_pos] = composition
|
199
|
-
starter_char = composition
|
200
|
-
codepoints.delete_at pos
|
201
|
-
eoa -= 1
|
202
|
-
pos -= 1
|
203
|
-
previous_combining_class = -1
|
204
|
-
else
|
205
|
-
previous_combining_class = current.combining_class
|
206
|
-
end
|
207
|
-
else
|
208
|
-
previous_combining_class = current.combining_class
|
209
|
-
end
|
210
|
-
if current.combining_class == 0
|
211
|
-
starter_pos = pos
|
212
|
-
starter_char = codepoints[pos]
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
codepoints
|
34
|
+
codepoints.pack("U*").unicode_normalize(:nfc).codepoints
|
217
35
|
end
|
218
36
|
|
219
37
|
# Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
|
@@ -224,7 +42,7 @@ module ActiveSupport
|
|
224
42
|
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
|
225
43
|
# encoding is entirely CP1252 or ISO-8859-1.
|
226
44
|
def tidy_bytes(string, force = false)
|
227
|
-
return string if string.empty?
|
45
|
+
return string if string.empty? || string.ascii_only?
|
228
46
|
return recode_windows1252_chars(string) if force
|
229
47
|
string.scrub { |bad| recode_windows1252_chars(bad) }
|
230
48
|
end
|
@@ -255,140 +73,10 @@ module ActiveSupport
|
|
255
73
|
end
|
256
74
|
end
|
257
75
|
|
258
|
-
# Returns the KC normalization of the string by default. NFKC is
|
259
|
-
# considered the best normalization form for passing strings to databases
|
260
|
-
# and validations.
|
261
|
-
#
|
262
|
-
# * <tt>string</tt> - The string to perform normalization on.
|
263
|
-
# * <tt>form</tt> - The form you want to normalize in. Should be one of
|
264
|
-
# the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
|
265
|
-
# Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
|
266
|
-
def normalize(string, form = nil)
|
267
|
-
form ||= @default_normalization_form
|
268
|
-
# See http://www.unicode.org/reports/tr15, Table 1
|
269
|
-
codepoints = string.codepoints.to_a
|
270
|
-
case form
|
271
|
-
when :d
|
272
|
-
reorder_characters(decompose(:canonical, codepoints))
|
273
|
-
when :c
|
274
|
-
compose(reorder_characters(decompose(:canonical, codepoints)))
|
275
|
-
when :kd
|
276
|
-
reorder_characters(decompose(:compatibility, codepoints))
|
277
|
-
when :kc
|
278
|
-
compose(reorder_characters(decompose(:compatibility, codepoints)))
|
279
|
-
else
|
280
|
-
raise ArgumentError, "#{form} is not a valid normalization variant", caller
|
281
|
-
end.pack("U*".freeze)
|
282
|
-
end
|
283
|
-
|
284
|
-
def downcase(string)
|
285
|
-
apply_mapping string, :lowercase_mapping
|
286
|
-
end
|
287
|
-
|
288
|
-
def upcase(string)
|
289
|
-
apply_mapping string, :uppercase_mapping
|
290
|
-
end
|
291
|
-
|
292
|
-
def swapcase(string)
|
293
|
-
apply_mapping string, :swapcase_mapping
|
294
|
-
end
|
295
|
-
|
296
|
-
# Holds data about a codepoint in the Unicode database.
|
297
|
-
class Codepoint
|
298
|
-
attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
|
299
|
-
|
300
|
-
# Initializing Codepoint object with default values
|
301
|
-
def initialize
|
302
|
-
@combining_class = 0
|
303
|
-
@uppercase_mapping = 0
|
304
|
-
@lowercase_mapping = 0
|
305
|
-
end
|
306
|
-
|
307
|
-
def swapcase_mapping
|
308
|
-
uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
# Holds static data from the Unicode database.
|
313
|
-
class UnicodeDatabase
|
314
|
-
ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
|
315
|
-
|
316
|
-
attr_writer(*ATTRIBUTES)
|
317
|
-
|
318
|
-
def initialize
|
319
|
-
@codepoints = Hash.new(Codepoint.new)
|
320
|
-
@composition_exclusion = []
|
321
|
-
@composition_map = {}
|
322
|
-
@boundary = {}
|
323
|
-
@cp1252 = {}
|
324
|
-
end
|
325
|
-
|
326
|
-
# Lazy load the Unicode database so it's only loaded when it's actually used
|
327
|
-
ATTRIBUTES.each do |attr_name|
|
328
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
329
|
-
def #{attr_name} # def codepoints
|
330
|
-
load # load
|
331
|
-
@#{attr_name} # @codepoints
|
332
|
-
end # end
|
333
|
-
EOS
|
334
|
-
end
|
335
|
-
|
336
|
-
# Loads the Unicode database and returns all the internal objects of
|
337
|
-
# UnicodeDatabase.
|
338
|
-
def load
|
339
|
-
begin
|
340
|
-
@codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, "rb") { |f| Marshal.load f.read }
|
341
|
-
rescue => e
|
342
|
-
raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
|
343
|
-
end
|
344
|
-
|
345
|
-
# Redefine the === method so we can write shorter rules for grapheme cluster breaks
|
346
|
-
@boundary.each_key do |k|
|
347
|
-
@boundary[k].instance_eval do
|
348
|
-
def ===(other)
|
349
|
-
detect { |i| i === other } ? true : false
|
350
|
-
end
|
351
|
-
end if @boundary[k].kind_of?(Array)
|
352
|
-
end
|
353
|
-
|
354
|
-
# define attr_reader methods for the instance variables
|
355
|
-
class << self
|
356
|
-
attr_reader(*ATTRIBUTES)
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
# Returns the directory in which the data files are stored.
|
361
|
-
def self.dirname
|
362
|
-
File.expand_path("../values", __dir__)
|
363
|
-
end
|
364
|
-
|
365
|
-
# Returns the filename for the data file for this version.
|
366
|
-
def self.filename
|
367
|
-
File.expand_path File.join(dirname, "unicode_tables.dat")
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
76
|
private
|
372
|
-
|
373
|
-
def apply_mapping(string, mapping)
|
374
|
-
database.codepoints
|
375
|
-
string.each_codepoint.map do |codepoint|
|
376
|
-
cp = database.codepoints[codepoint]
|
377
|
-
if cp && (ncp = cp.send(mapping)) && ncp > 0
|
378
|
-
ncp
|
379
|
-
else
|
380
|
-
codepoint
|
381
|
-
end
|
382
|
-
end.pack("U*")
|
383
|
-
end
|
384
|
-
|
385
77
|
def recode_windows1252_chars(string)
|
386
78
|
string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
|
387
79
|
end
|
388
|
-
|
389
|
-
def database
|
390
|
-
@database ||= UnicodeDatabase.new
|
391
|
-
end
|
392
80
|
end
|
393
81
|
end
|
394
82
|
end
|
@@ -34,10 +34,23 @@ module ActiveSupport
|
|
34
34
|
# name # => String, name of the event (such as 'render' from above)
|
35
35
|
# start # => Time, when the instrumented block started execution
|
36
36
|
# finish # => Time, when the instrumented block ended execution
|
37
|
-
# id # => String, unique ID for
|
37
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
38
38
|
# payload # => Hash, the payload
|
39
39
|
# end
|
40
40
|
#
|
41
|
+
# Here, the +start+ and +finish+ values represent wall-clock time. If you are
|
42
|
+
# concerned about accuracy, you can register a monotonic subscriber.
|
43
|
+
#
|
44
|
+
# ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
|
45
|
+
# name # => String, name of the event (such as 'render' from above)
|
46
|
+
# start # => Monotonic time, when the instrumented block started execution
|
47
|
+
# finish # => Monotonic time, when the instrumented block ended execution
|
48
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
49
|
+
# payload # => Hash, the payload
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# The +start+ and +finish+ values above represent monotonic time.
|
53
|
+
#
|
41
54
|
# For instance, let's store all "render" events in an array:
|
42
55
|
#
|
43
56
|
# events = []
|
@@ -59,7 +72,7 @@ module ActiveSupport
|
|
59
72
|
# event.payload # => { extra: :information }
|
60
73
|
#
|
61
74
|
# The block in the <tt>subscribe</tt> call gets the name of the event, start
|
62
|
-
# timestamp, end timestamp, a string with a unique identifier for that event
|
75
|
+
# timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
|
63
76
|
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
|
64
77
|
# that order.
|
65
78
|
#
|
@@ -67,9 +80,12 @@ module ActiveSupport
|
|
67
80
|
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
|
68
81
|
# the name of the exception class, and the exception message.
|
69
82
|
# The <tt>:exception_object</tt> key of the payload will have the exception
|
70
|
-
# itself as the value
|
83
|
+
# itself as the value:
|
71
84
|
#
|
72
|
-
#
|
85
|
+
# event.payload[:exception] # => ["ArgumentError", "Invalid value"]
|
86
|
+
# event.payload[:exception_object] # => #<ArgumentError: Invalid value>
|
87
|
+
#
|
88
|
+
# As the earlier example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
|
73
89
|
# is able to take the arguments as they come and provide an object-oriented
|
74
90
|
# interface to that data.
|
75
91
|
#
|
@@ -132,6 +148,16 @@ module ActiveSupport
|
|
132
148
|
# during the execution of the block. The callback is unsubscribed automatically
|
133
149
|
# after that.
|
134
150
|
#
|
151
|
+
# To record +started+ and +finished+ values with monotonic time,
|
152
|
+
# specify the optional <tt>:monotonic</tt> option to the
|
153
|
+
# <tt>subscribed</tt> method. The <tt>:monotonic</tt> option is set
|
154
|
+
# to +false+ by default.
|
155
|
+
#
|
156
|
+
# callback = lambda {|name, started, finished, unique_id, payload| ... }
|
157
|
+
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do
|
158
|
+
# ...
|
159
|
+
# end
|
160
|
+
#
|
135
161
|
# === Manual Unsubscription
|
136
162
|
#
|
137
163
|
# The +subscribe+ method returns a subscriber object:
|
@@ -150,6 +176,15 @@ module ActiveSupport
|
|
150
176
|
#
|
151
177
|
# ActiveSupport::Notifications.unsubscribe("render")
|
152
178
|
#
|
179
|
+
# Subscribers using a regexp or other pattern-matching object will remain subscribed
|
180
|
+
# to all events that match their original pattern, unless those events match a string
|
181
|
+
# passed to `unsubscribe`:
|
182
|
+
#
|
183
|
+
# subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
|
184
|
+
# ActiveSupport::Notifications.unsubscribe('render_template.action_view')
|
185
|
+
# subscriber.matches?('render_template.action_view') # => false
|
186
|
+
# subscriber.matches?('render_partial.action_view') # => true
|
187
|
+
#
|
153
188
|
# == Default Queue
|
154
189
|
#
|
155
190
|
# Notifications ships with a queue implementation that consumes and publishes events
|
@@ -171,12 +206,41 @@ module ActiveSupport
|
|
171
206
|
end
|
172
207
|
end
|
173
208
|
|
174
|
-
|
175
|
-
|
209
|
+
# Subscribe to a given event name with the passed +block+.
|
210
|
+
#
|
211
|
+
# You can subscribe to events by passing a String to match exact event
|
212
|
+
# names, or by passing a Regexp to match all events that match a pattern.
|
213
|
+
#
|
214
|
+
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
|
215
|
+
# @event = ActiveSupport::Notifications::Event.new(*args)
|
216
|
+
# end
|
217
|
+
#
|
218
|
+
# The +block+ will receive five parameters with information about the event:
|
219
|
+
#
|
220
|
+
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
|
221
|
+
# name # => String, name of the event (such as 'render' from above)
|
222
|
+
# start # => Time, when the instrumented block started execution
|
223
|
+
# finish # => Time, when the instrumented block ended execution
|
224
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
225
|
+
# payload # => Hash, the payload
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# If the block passed to the method only takes one parameter,
|
229
|
+
# it will yield an event object to the block:
|
230
|
+
#
|
231
|
+
# ActiveSupport::Notifications.subscribe(/render/) do |event|
|
232
|
+
# @event = event
|
233
|
+
# end
|
234
|
+
def subscribe(pattern = nil, callback = nil, &block)
|
235
|
+
notifier.subscribe(pattern, callback, monotonic: false, &block)
|
236
|
+
end
|
237
|
+
|
238
|
+
def monotonic_subscribe(pattern = nil, callback = nil, &block)
|
239
|
+
notifier.subscribe(pattern, callback, monotonic: true, &block)
|
176
240
|
end
|
177
241
|
|
178
|
-
def subscribed(callback,
|
179
|
-
subscriber = subscribe(
|
242
|
+
def subscribed(callback, pattern = nil, monotonic: false, &block)
|
243
|
+
subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic)
|
180
244
|
yield
|
181
245
|
ensure
|
182
246
|
unsubscribe(subscriber)
|