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.

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +353 -435
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support.rb +14 -1
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/array_inquirer.rb +4 -2
  8. data/lib/active_support/backtrace_cleaner.rb +29 -3
  9. data/lib/active_support/benchmarkable.rb +1 -1
  10. data/lib/active_support/cache.rb +142 -78
  11. data/lib/active_support/cache/file_store.rb +33 -33
  12. data/lib/active_support/cache/mem_cache_store.rb +32 -20
  13. data/lib/active_support/cache/memory_store.rb +59 -33
  14. data/lib/active_support/cache/null_store.rb +8 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +70 -43
  16. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  17. data/lib/active_support/callbacks.rb +81 -64
  18. data/lib/active_support/concern.rb +70 -3
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  20. data/lib/active_support/concurrency/share_lock.rb +0 -1
  21. data/lib/active_support/configurable.rb +10 -14
  22. data/lib/active_support/configuration_file.rb +46 -0
  23. data/lib/active_support/core_ext.rb +1 -1
  24. data/lib/active_support/core_ext/array.rb +1 -1
  25. data/lib/active_support/core_ext/array/access.rb +18 -6
  26. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  30. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  31. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  32. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  35. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  36. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  37. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  38. data/lib/active_support/core_ext/enumerable.rb +171 -75
  39. data/lib/active_support/core_ext/hash.rb +1 -2
  40. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  41. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  42. data/lib/active_support/core_ext/hash/except.rb +2 -2
  43. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  44. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  45. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  46. data/lib/active_support/core_ext/kernel.rb +0 -1
  47. data/lib/active_support/core_ext/load_error.rb +1 -1
  48. data/lib/active_support/core_ext/marshal.rb +2 -0
  49. data/lib/active_support/core_ext/module.rb +0 -1
  50. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  51. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  52. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  53. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  54. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  55. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  56. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  57. data/lib/active_support/core_ext/name_error.rb +29 -2
  58. data/lib/active_support/core_ext/numeric.rb +0 -1
  59. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  60. data/lib/active_support/core_ext/object/blank.rb +1 -2
  61. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  62. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  63. data/lib/active_support/core_ext/object/json.rb +14 -2
  64. data/lib/active_support/core_ext/object/try.rb +17 -7
  65. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  66. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  67. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  68. data/lib/active_support/core_ext/range/each.rb +0 -1
  69. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  70. data/lib/active_support/core_ext/regexp.rb +8 -5
  71. data/lib/active_support/core_ext/securerandom.rb +23 -3
  72. data/lib/active_support/core_ext/string/access.rb +5 -16
  73. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  74. data/lib/active_support/core_ext/string/filters.rb +42 -1
  75. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  76. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  77. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  78. data/lib/active_support/core_ext/string/output_safety.rb +70 -13
  79. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  80. data/lib/active_support/core_ext/string/strip.rb +3 -1
  81. data/lib/active_support/core_ext/symbol.rb +3 -0
  82. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  83. data/lib/active_support/core_ext/time/calculations.rb +50 -3
  84. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  85. data/lib/active_support/core_ext/uri.rb +6 -1
  86. data/lib/active_support/current_attributes.rb +15 -2
  87. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  90. data/lib/active_support/deprecation.rb +6 -1
  91. data/lib/active_support/deprecation/behaviors.rb +16 -3
  92. data/lib/active_support/deprecation/disallowed.rb +56 -0
  93. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  94. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  95. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  96. data/lib/active_support/deprecation/reporting.rb +50 -7
  97. data/lib/active_support/descendants_tracker.rb +59 -9
  98. data/lib/active_support/duration.rb +90 -38
  99. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  100. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  101. data/lib/active_support/encrypted_configuration.rb +0 -4
  102. data/lib/active_support/encrypted_file.rb +22 -4
  103. data/lib/active_support/environment_inquirer.rb +20 -0
  104. data/lib/active_support/evented_file_update_checker.rb +82 -117
  105. data/lib/active_support/execution_wrapper.rb +1 -0
  106. data/lib/active_support/file_update_checker.rb +0 -1
  107. data/lib/active_support/fork_tracker.rb +62 -0
  108. data/lib/active_support/gem_version.rb +4 -4
  109. data/lib/active_support/hash_with_indifferent_access.rb +64 -41
  110. data/lib/active_support/i18n.rb +1 -0
  111. data/lib/active_support/i18n_railtie.rb +15 -8
  112. data/lib/active_support/inflector/inflections.rb +2 -7
  113. data/lib/active_support/inflector/methods.rb +49 -58
  114. data/lib/active_support/inflector/transliterate.rb +47 -18
  115. data/lib/active_support/json/decoding.rb +25 -26
  116. data/lib/active_support/json/encoding.rb +11 -3
  117. data/lib/active_support/key_generator.rb +1 -33
  118. data/lib/active_support/lazy_load_hooks.rb +5 -2
  119. data/lib/active_support/locale/en.rb +33 -0
  120. data/lib/active_support/locale/en.yml +7 -3
  121. data/lib/active_support/log_subscriber.rb +39 -9
  122. data/lib/active_support/logger.rb +2 -17
  123. data/lib/active_support/logger_silence.rb +11 -19
  124. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  125. data/lib/active_support/message_encryptor.rb +8 -13
  126. data/lib/active_support/message_verifier.rb +10 -10
  127. data/lib/active_support/messages/metadata.rb +11 -2
  128. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  129. data/lib/active_support/messages/rotator.rb +10 -9
  130. data/lib/active_support/multibyte/chars.rb +10 -68
  131. data/lib/active_support/multibyte/unicode.rb +15 -327
  132. data/lib/active_support/notifications.rb +72 -8
  133. data/lib/active_support/notifications/fanout.rb +116 -16
  134. data/lib/active_support/notifications/instrumenter.rb +71 -9
  135. data/lib/active_support/number_helper.rb +38 -12
  136. data/lib/active_support/number_helper/number_converter.rb +5 -6
  137. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  138. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  139. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  140. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  141. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  142. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  143. data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
  144. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  145. data/lib/active_support/option_merger.rb +22 -3
  146. data/lib/active_support/ordered_hash.rb +1 -1
  147. data/lib/active_support/ordered_options.rb +13 -3
  148. data/lib/active_support/parameter_filter.rb +133 -0
  149. data/lib/active_support/per_thread_registry.rb +1 -1
  150. data/lib/active_support/rails.rb +1 -10
  151. data/lib/active_support/railtie.rb +23 -1
  152. data/lib/active_support/reloader.rb +4 -5
  153. data/lib/active_support/rescuable.rb +4 -4
  154. data/lib/active_support/secure_compare_rotator.rb +51 -0
  155. data/lib/active_support/security_utils.rb +19 -12
  156. data/lib/active_support/string_inquirer.rb +4 -3
  157. data/lib/active_support/subscriber.rb +72 -28
  158. data/lib/active_support/tagged_logging.rb +42 -8
  159. data/lib/active_support/test_case.rb +91 -0
  160. data/lib/active_support/testing/assertions.rb +30 -9
  161. data/lib/active_support/testing/deprecation.rb +0 -1
  162. data/lib/active_support/testing/file_fixtures.rb +2 -0
  163. data/lib/active_support/testing/isolation.rb +2 -2
  164. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  165. data/lib/active_support/testing/parallelization.rb +51 -0
  166. data/lib/active_support/testing/parallelization/server.rb +78 -0
  167. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  168. data/lib/active_support/testing/stream.rb +1 -2
  169. data/lib/active_support/testing/time_helpers.rb +47 -12
  170. data/lib/active_support/time_with_zone.rb +81 -47
  171. data/lib/active_support/values/time_zone.rb +32 -17
  172. data/lib/active_support/xml_mini.rb +2 -10
  173. data/lib/active_support/xml_mini/jdom.rb +2 -3
  174. data/lib/active_support/xml_mini/libxml.rb +2 -2
  175. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  176. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  177. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  178. data/lib/active_support/xml_mini/rexml.rb +10 -3
  179. metadata +58 -32
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. 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 = "9.0.0"
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
- if should_break
109
- unpacked << codepoints[marker..pos - 1]
110
- marker = pos
111
- end
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
- # Reverse operation of unpack_graphemes.
117
- #
118
- # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
119
- def pack_graphemes(unpacked)
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
- codepoints.inject([]) do |decomposed, cp|
142
- # if it's a hangul syllable starter character
143
- if HANGUL_SBASE <= cp && cp < HANGUL_SLAST
144
- sindex = cp - HANGUL_SBASE
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
- pos = 0
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 this notification
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
- # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
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
- def subscribe(*args, &block)
175
- notifier.subscribe(*args, &block)
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, *args, &block)
179
- subscriber = subscribe(*args, &callback)
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)