activesupport 5.0.7.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 (236) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1013 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/lib/active_support.rb +99 -0
  6. data/lib/active_support/all.rb +3 -0
  7. data/lib/active_support/array_inquirer.rb +44 -0
  8. data/lib/active_support/backtrace_cleaner.rb +103 -0
  9. data/lib/active_support/benchmarkable.rb +49 -0
  10. data/lib/active_support/builder.rb +6 -0
  11. data/lib/active_support/cache.rb +701 -0
  12. data/lib/active_support/cache/file_store.rb +204 -0
  13. data/lib/active_support/cache/mem_cache_store.rb +207 -0
  14. data/lib/active_support/cache/memory_store.rb +167 -0
  15. data/lib/active_support/cache/null_store.rb +41 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +172 -0
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  18. data/lib/active_support/callbacks.rb +791 -0
  19. data/lib/active_support/concern.rb +142 -0
  20. data/lib/active_support/concurrency/latch.rb +26 -0
  21. data/lib/active_support/concurrency/share_lock.rb +226 -0
  22. data/lib/active_support/configurable.rb +148 -0
  23. data/lib/active_support/core_ext.rb +4 -0
  24. data/lib/active_support/core_ext/array.rb +7 -0
  25. data/lib/active_support/core_ext/array/access.rb +90 -0
  26. data/lib/active_support/core_ext/array/conversions.rb +211 -0
  27. data/lib/active_support/core_ext/array/extract_options.rb +29 -0
  28. data/lib/active_support/core_ext/array/grouping.rb +107 -0
  29. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  30. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +46 -0
  32. data/lib/active_support/core_ext/benchmark.rb +14 -0
  33. data/lib/active_support/core_ext/big_decimal.rb +1 -0
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  35. data/lib/active_support/core_ext/class.rb +2 -0
  36. data/lib/active_support/core_ext/class/attribute.rb +128 -0
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -0
  38. data/lib/active_support/core_ext/class/subclasses.rb +41 -0
  39. data/lib/active_support/core_ext/date.rb +5 -0
  40. data/lib/active_support/core_ext/date/acts_like.rb +8 -0
  41. data/lib/active_support/core_ext/date/blank.rb +12 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +143 -0
  43. data/lib/active_support/core_ext/date/conversions.rb +95 -0
  44. data/lib/active_support/core_ext/date/zones.rb +6 -0
  45. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  46. data/lib/active_support/core_ext/date_and_time/compatibility.rb +14 -0
  47. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  48. data/lib/active_support/core_ext/date_time.rb +5 -0
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +14 -0
  50. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +199 -0
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/conversions.rb +105 -0
  54. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  55. data/lib/active_support/core_ext/enumerable.rb +146 -0
  56. data/lib/active_support/core_ext/file.rb +1 -0
  57. data/lib/active_support/core_ext/file/atomic.rb +68 -0
  58. data/lib/active_support/core_ext/hash.rb +9 -0
  59. data/lib/active_support/core_ext/hash/compact.rb +24 -0
  60. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +38 -0
  62. data/lib/active_support/core_ext/hash/except.rb +22 -0
  63. data/lib/active_support/core_ext/hash/indifferent_access.rb +23 -0
  64. data/lib/active_support/core_ext/hash/keys.rb +170 -0
  65. data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -0
  66. data/lib/active_support/core_ext/hash/slice.rb +48 -0
  67. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  68. data/lib/active_support/core_ext/integer.rb +3 -0
  69. data/lib/active_support/core_ext/integer/inflections.rb +29 -0
  70. data/lib/active_support/core_ext/integer/multiple.rb +10 -0
  71. data/lib/active_support/core_ext/integer/time.rb +29 -0
  72. data/lib/active_support/core_ext/kernel.rb +4 -0
  73. data/lib/active_support/core_ext/kernel/agnostics.rb +11 -0
  74. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  75. data/lib/active_support/core_ext/kernel/debugger.rb +3 -0
  76. data/lib/active_support/core_ext/kernel/reporting.rb +43 -0
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +6 -0
  78. data/lib/active_support/core_ext/load_error.rb +31 -0
  79. data/lib/active_support/core_ext/marshal.rb +22 -0
  80. data/lib/active_support/core_ext/module.rb +12 -0
  81. data/lib/active_support/core_ext/module/aliasing.rb +74 -0
  82. data/lib/active_support/core_ext/module/anonymous.rb +28 -0
  83. data/lib/active_support/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  87. data/lib/active_support/core_ext/module/delegation.rb +216 -0
  88. data/lib/active_support/core_ext/module/deprecation.rb +23 -0
  89. data/lib/active_support/core_ext/module/introspection.rb +68 -0
  90. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  91. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  92. data/lib/active_support/core_ext/module/reachable.rb +8 -0
  93. data/lib/active_support/core_ext/module/remove_method.rb +35 -0
  94. data/lib/active_support/core_ext/name_error.rb +31 -0
  95. data/lib/active_support/core_ext/numeric.rb +4 -0
  96. data/lib/active_support/core_ext/numeric/bytes.rb +64 -0
  97. data/lib/active_support/core_ext/numeric/conversions.rb +144 -0
  98. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  99. data/lib/active_support/core_ext/numeric/time.rb +74 -0
  100. data/lib/active_support/core_ext/object.rb +14 -0
  101. data/lib/active_support/core_ext/object/acts_like.rb +10 -0
  102. data/lib/active_support/core_ext/object/blank.rb +143 -0
  103. data/lib/active_support/core_ext/object/conversions.rb +4 -0
  104. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  105. data/lib/active_support/core_ext/object/duplicable.rb +124 -0
  106. data/lib/active_support/core_ext/object/inclusion.rb +27 -0
  107. data/lib/active_support/core_ext/object/instance_variables.rb +28 -0
  108. data/lib/active_support/core_ext/object/json.rb +205 -0
  109. data/lib/active_support/core_ext/object/to_param.rb +1 -0
  110. data/lib/active_support/core_ext/object/to_query.rb +84 -0
  111. data/lib/active_support/core_ext/object/try.rb +146 -0
  112. data/lib/active_support/core_ext/object/with_options.rb +69 -0
  113. data/lib/active_support/core_ext/range.rb +4 -0
  114. data/lib/active_support/core_ext/range/conversions.rb +31 -0
  115. data/lib/active_support/core_ext/range/each.rb +21 -0
  116. data/lib/active_support/core_ext/range/include_range.rb +23 -0
  117. data/lib/active_support/core_ext/range/overlaps.rb +8 -0
  118. data/lib/active_support/core_ext/regexp.rb +5 -0
  119. data/lib/active_support/core_ext/securerandom.rb +23 -0
  120. data/lib/active_support/core_ext/string.rb +13 -0
  121. data/lib/active_support/core_ext/string/access.rb +104 -0
  122. data/lib/active_support/core_ext/string/behavior.rb +6 -0
  123. data/lib/active_support/core_ext/string/conversions.rb +57 -0
  124. data/lib/active_support/core_ext/string/exclude.rb +11 -0
  125. data/lib/active_support/core_ext/string/filters.rb +102 -0
  126. data/lib/active_support/core_ext/string/indent.rb +43 -0
  127. data/lib/active_support/core_ext/string/inflections.rb +244 -0
  128. data/lib/active_support/core_ext/string/inquiry.rb +13 -0
  129. data/lib/active_support/core_ext/string/multibyte.rb +53 -0
  130. data/lib/active_support/core_ext/string/output_safety.rb +260 -0
  131. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -0
  132. data/lib/active_support/core_ext/string/strip.rb +23 -0
  133. data/lib/active_support/core_ext/string/zones.rb +14 -0
  134. data/lib/active_support/core_ext/struct.rb +3 -0
  135. data/lib/active_support/core_ext/time.rb +5 -0
  136. data/lib/active_support/core_ext/time/acts_like.rb +8 -0
  137. data/lib/active_support/core_ext/time/calculations.rb +290 -0
  138. data/lib/active_support/core_ext/time/compatibility.rb +14 -0
  139. data/lib/active_support/core_ext/time/conversions.rb +67 -0
  140. data/lib/active_support/core_ext/time/marshal.rb +3 -0
  141. data/lib/active_support/core_ext/time/zones.rb +111 -0
  142. data/lib/active_support/core_ext/uri.rb +24 -0
  143. data/lib/active_support/dependencies.rb +755 -0
  144. data/lib/active_support/dependencies/autoload.rb +77 -0
  145. data/lib/active_support/dependencies/interlock.rb +55 -0
  146. data/lib/active_support/deprecation.rb +43 -0
  147. data/lib/active_support/deprecation/behaviors.rb +90 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +37 -0
  149. data/lib/active_support/deprecation/method_wrappers.rb +70 -0
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +149 -0
  151. data/lib/active_support/deprecation/reporting.rb +112 -0
  152. data/lib/active_support/descendants_tracker.rb +60 -0
  153. data/lib/active_support/duration.rb +235 -0
  154. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  155. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  156. data/lib/active_support/evented_file_update_checker.rb +199 -0
  157. data/lib/active_support/execution_wrapper.rb +126 -0
  158. data/lib/active_support/executor.rb +6 -0
  159. data/lib/active_support/file_update_checker.rb +157 -0
  160. data/lib/active_support/gem_version.rb +15 -0
  161. data/lib/active_support/gzip.rb +36 -0
  162. data/lib/active_support/hash_with_indifferent_access.rb +329 -0
  163. data/lib/active_support/i18n.rb +13 -0
  164. data/lib/active_support/i18n_railtie.rb +115 -0
  165. data/lib/active_support/inflections.rb +70 -0
  166. data/lib/active_support/inflector.rb +7 -0
  167. data/lib/active_support/inflector/inflections.rb +242 -0
  168. data/lib/active_support/inflector/methods.rb +390 -0
  169. data/lib/active_support/inflector/transliterate.rb +112 -0
  170. data/lib/active_support/json.rb +2 -0
  171. data/lib/active_support/json/decoding.rb +74 -0
  172. data/lib/active_support/json/encoding.rb +127 -0
  173. data/lib/active_support/key_generator.rb +71 -0
  174. data/lib/active_support/lazy_load_hooks.rb +76 -0
  175. data/lib/active_support/locale/en.yml +135 -0
  176. data/lib/active_support/log_subscriber.rb +109 -0
  177. data/lib/active_support/log_subscriber/test_helper.rb +104 -0
  178. data/lib/active_support/logger.rb +106 -0
  179. data/lib/active_support/logger_silence.rb +28 -0
  180. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  181. data/lib/active_support/message_encryptor.rb +114 -0
  182. data/lib/active_support/message_verifier.rb +134 -0
  183. data/lib/active_support/multibyte.rb +21 -0
  184. data/lib/active_support/multibyte/chars.rb +231 -0
  185. data/lib/active_support/multibyte/unicode.rb +413 -0
  186. data/lib/active_support/notifications.rb +212 -0
  187. data/lib/active_support/notifications/fanout.rb +157 -0
  188. data/lib/active_support/notifications/instrumenter.rb +91 -0
  189. data/lib/active_support/number_helper.rb +368 -0
  190. data/lib/active_support/number_helper/number_converter.rb +182 -0
  191. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  192. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  193. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  194. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  195. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  196. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  197. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  198. data/lib/active_support/option_merger.rb +25 -0
  199. data/lib/active_support/ordered_hash.rb +48 -0
  200. data/lib/active_support/ordered_options.rb +81 -0
  201. data/lib/active_support/per_thread_registry.rb +58 -0
  202. data/lib/active_support/proxy_object.rb +13 -0
  203. data/lib/active_support/rails.rb +27 -0
  204. data/lib/active_support/railtie.rb +51 -0
  205. data/lib/active_support/reloader.rb +129 -0
  206. data/lib/active_support/rescuable.rb +173 -0
  207. data/lib/active_support/security_utils.rb +27 -0
  208. data/lib/active_support/string_inquirer.rb +26 -0
  209. data/lib/active_support/subscriber.rb +120 -0
  210. data/lib/active_support/tagged_logging.rb +77 -0
  211. data/lib/active_support/test_case.rb +88 -0
  212. data/lib/active_support/testing/assertions.rb +99 -0
  213. data/lib/active_support/testing/autorun.rb +5 -0
  214. data/lib/active_support/testing/constant_lookup.rb +50 -0
  215. data/lib/active_support/testing/declarative.rb +26 -0
  216. data/lib/active_support/testing/deprecation.rb +36 -0
  217. data/lib/active_support/testing/file_fixtures.rb +34 -0
  218. data/lib/active_support/testing/isolation.rb +115 -0
  219. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  220. data/lib/active_support/testing/setup_and_teardown.rb +50 -0
  221. data/lib/active_support/testing/stream.rb +42 -0
  222. data/lib/active_support/testing/tagged_logging.rb +25 -0
  223. data/lib/active_support/testing/time_helpers.rb +136 -0
  224. data/lib/active_support/time.rb +18 -0
  225. data/lib/active_support/time_with_zone.rb +511 -0
  226. data/lib/active_support/values/time_zone.rb +484 -0
  227. data/lib/active_support/values/unicode_tables.dat +0 -0
  228. data/lib/active_support/version.rb +8 -0
  229. data/lib/active_support/xml_mini.rb +209 -0
  230. data/lib/active_support/xml_mini/jdom.rb +181 -0
  231. data/lib/active_support/xml_mini/libxml.rb +77 -0
  232. data/lib/active_support/xml_mini/libxmlsax.rb +82 -0
  233. data/lib/active_support/xml_mini/nokogiri.rb +81 -0
  234. data/lib/active_support/xml_mini/nokogirisax.rb +85 -0
  235. data/lib/active_support/xml_mini/rexml.rb +128 -0
  236. metadata +350 -0
@@ -0,0 +1,413 @@
1
+ module ActiveSupport
2
+ module Multibyte
3
+ module Unicode
4
+
5
+ extend self
6
+
7
+ # A list of all available normalization forms.
8
+ # See http://www.unicode.org/reports/tr15/tr15-29.html for more
9
+ # information about normalization.
10
+ NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
11
+
12
+ # The Unicode version that is supported by the implementation
13
+ UNICODE_VERSION = '8.0.0'
14
+
15
+ # The default normalization used for operations that require
16
+ # normalization. It can be set to any of the normalizations
17
+ # in NORMALIZATION_FORMS.
18
+ #
19
+ # ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
20
+ attr_accessor :default_normalization_form
21
+ @default_normalization_form = :kc
22
+
23
+ # Hangul character boundaries and properties
24
+ HANGUL_SBASE = 0xAC00
25
+ HANGUL_LBASE = 0x1100
26
+ HANGUL_VBASE = 0x1161
27
+ HANGUL_TBASE = 0x11A7
28
+ HANGUL_LCOUNT = 19
29
+ HANGUL_VCOUNT = 21
30
+ HANGUL_TCOUNT = 28
31
+ HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
32
+ HANGUL_SCOUNT = 11172
33
+ HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
34
+ HANGUL_JAMO_FIRST = 0x1100
35
+ HANGUL_JAMO_LAST = 0x11FF
36
+
37
+ # All the unicode whitespace
38
+ WHITESPACE = [
39
+ (0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
40
+ 0x0020, # White_Space # Zs SPACE
41
+ 0x0085, # White_Space # Cc <control-0085>
42
+ 0x00A0, # White_Space # Zs NO-BREAK SPACE
43
+ 0x1680, # White_Space # Zs OGHAM SPACE MARK
44
+ (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
45
+ 0x2028, # White_Space # Zl LINE SEPARATOR
46
+ 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
47
+ 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
48
+ 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
49
+ 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
50
+ ].flatten.freeze
51
+
52
+ # BOM (byte order mark) can also be seen as whitespace, it's a
53
+ # non-rendering character used to distinguish between little and big
54
+ # endian. This is not an issue in utf-8, so it must be ignored.
55
+ LEADERS_AND_TRAILERS = WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
56
+
57
+ # Returns a regular expression pattern that matches the passed Unicode
58
+ # codepoints.
59
+ def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
60
+ array_of_codepoints.collect{ |e| [e].pack 'U*'.freeze }.join('|'.freeze)
61
+ end
62
+ TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
63
+ LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
64
+
65
+ # Detect whether the codepoint is in a certain character class. Returns
66
+ # +true+ when it's in the specified character class and +false+ otherwise.
67
+ # Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
68
+ # <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
69
+ #
70
+ # Primarily used by the grapheme cluster support.
71
+ def in_char_class?(codepoint, classes)
72
+ classes.detect { |c| database.boundary[c] === codepoint } ? true : false
73
+ end
74
+
75
+ # Unpack the string at grapheme boundaries. Returns a list of character
76
+ # lists.
77
+ #
78
+ # Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
79
+ # Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
80
+ def unpack_graphemes(string)
81
+ codepoints = string.codepoints.to_a
82
+ unpacked = []
83
+ pos = 0
84
+ marker = 0
85
+ eoc = codepoints.length
86
+ while(pos < eoc)
87
+ pos += 1
88
+ previous = codepoints[pos-1]
89
+ current = codepoints[pos]
90
+
91
+ should_break =
92
+ # GB3. CR X LF
93
+ if previous == database.boundary[:cr] and current == database.boundary[:lf]
94
+ false
95
+ # GB4. (Control|CR|LF) ÷
96
+ elsif previous and in_char_class?(previous, [:control,:cr,:lf])
97
+ true
98
+ # GB5. ÷ (Control|CR|LF)
99
+ elsif in_char_class?(current, [:control,:cr,:lf])
100
+ true
101
+ # GB6. L X (L|V|LV|LVT)
102
+ elsif database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt])
103
+ false
104
+ # GB7. (LV|V) X (V|T)
105
+ elsif in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t])
106
+ false
107
+ # GB8. (LVT|T) X (T)
108
+ elsif in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current
109
+ false
110
+ # GB8a. Regional_Indicator X Regional_Indicator
111
+ elsif database.boundary[:regional_indicator] === previous and database.boundary[:regional_indicator] === current
112
+ false
113
+ # GB9. X Extend
114
+ elsif database.boundary[:extend] === current
115
+ false
116
+ # GB9a. X SpacingMark
117
+ elsif database.boundary[:spacingmark] === current
118
+ false
119
+ # GB9b. Prepend X
120
+ elsif database.boundary[:prepend] === previous
121
+ false
122
+ # GB10. Any ÷ Any
123
+ else
124
+ true
125
+ end
126
+
127
+ if should_break
128
+ unpacked << codepoints[marker..pos-1]
129
+ marker = pos
130
+ end
131
+ end
132
+ unpacked
133
+ end
134
+
135
+ # Reverse operation of unpack_graphemes.
136
+ #
137
+ # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
138
+ def pack_graphemes(unpacked)
139
+ unpacked.flatten.pack('U*')
140
+ end
141
+
142
+ # Re-order codepoints so the string becomes canonical.
143
+ def reorder_characters(codepoints)
144
+ length = codepoints.length- 1
145
+ pos = 0
146
+ while pos < length do
147
+ cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos+1]]
148
+ if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
149
+ codepoints[pos..pos+1] = cp2.code, cp1.code
150
+ pos += (pos > 0 ? -1 : 1)
151
+ else
152
+ pos += 1
153
+ end
154
+ end
155
+ codepoints
156
+ end
157
+
158
+ # Decompose composed characters to the decomposed form.
159
+ def decompose(type, codepoints)
160
+ codepoints.inject([]) do |decomposed, cp|
161
+ # if it's a hangul syllable starter character
162
+ if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
163
+ sindex = cp - HANGUL_SBASE
164
+ ncp = [] # new codepoints
165
+ ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
166
+ ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
167
+ tindex = sindex % HANGUL_TCOUNT
168
+ ncp << (HANGUL_TBASE + tindex) unless tindex == 0
169
+ decomposed.concat ncp
170
+ # if the codepoint is decomposable in with the current decomposition type
171
+ elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatibility)
172
+ decomposed.concat decompose(type, ncp.dup)
173
+ else
174
+ decomposed << cp
175
+ end
176
+ end
177
+ end
178
+
179
+ # Compose decomposed characters to the composed form.
180
+ def compose(codepoints)
181
+ pos = 0
182
+ eoa = codepoints.length - 1
183
+ starter_pos = 0
184
+ starter_char = codepoints[0]
185
+ previous_combining_class = -1
186
+ while pos < eoa
187
+ pos += 1
188
+ lindex = starter_char - HANGUL_LBASE
189
+ # -- Hangul
190
+ if 0 <= lindex and lindex < HANGUL_LCOUNT
191
+ vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
192
+ if 0 <= vindex and vindex < HANGUL_VCOUNT
193
+ tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
194
+ if 0 <= tindex and tindex < HANGUL_TCOUNT
195
+ j = starter_pos + 2
196
+ eoa -= 2
197
+ else
198
+ tindex = 0
199
+ j = starter_pos + 1
200
+ eoa -= 1
201
+ end
202
+ codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
203
+ end
204
+ starter_pos += 1
205
+ starter_char = codepoints[starter_pos]
206
+ # -- Other characters
207
+ else
208
+ current_char = codepoints[pos]
209
+ current = database.codepoints[current_char]
210
+ if current.combining_class > previous_combining_class
211
+ if ref = database.composition_map[starter_char]
212
+ composition = ref[current_char]
213
+ else
214
+ composition = nil
215
+ end
216
+ unless composition.nil?
217
+ codepoints[starter_pos] = composition
218
+ starter_char = composition
219
+ codepoints.delete_at pos
220
+ eoa -= 1
221
+ pos -= 1
222
+ previous_combining_class = -1
223
+ else
224
+ previous_combining_class = current.combining_class
225
+ end
226
+ else
227
+ previous_combining_class = current.combining_class
228
+ end
229
+ if current.combining_class == 0
230
+ starter_pos = pos
231
+ starter_char = codepoints[pos]
232
+ end
233
+ end
234
+ end
235
+ codepoints
236
+ end
237
+
238
+ # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
239
+ if !defined?(Rubinius)
240
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
241
+ # resulting in a valid UTF-8 string.
242
+ #
243
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's
244
+ # encoding is entirely CP1252 or ISO-8859-1.
245
+ def tidy_bytes(string, force = false)
246
+ return string if string.empty?
247
+ return recode_windows1252_chars(string) if force
248
+ string.scrub { |bad| recode_windows1252_chars(bad) }
249
+ end
250
+ else
251
+ def tidy_bytes(string, force = false)
252
+ return string if string.empty?
253
+ return recode_windows1252_chars(string) if force
254
+
255
+ # We can't transcode to the same format, so we choose a nearly-identical encoding.
256
+ # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
257
+ # CP1252 when we get errors. The final string will be 'converted' back to UTF-8
258
+ # before returning.
259
+ reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
260
+
261
+ source = string.dup
262
+ out = ''.force_encoding(Encoding::UTF_16LE)
263
+
264
+ loop do
265
+ reader.primitive_convert(source, out)
266
+ _, _, _, error_bytes, _ = reader.primitive_errinfo
267
+ break if error_bytes.nil?
268
+ out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace)
269
+ end
270
+
271
+ reader.finish
272
+
273
+ out.encode!(Encoding::UTF_8)
274
+ end
275
+ end
276
+
277
+ # Returns the KC normalization of the string by default. NFKC is
278
+ # considered the best normalization form for passing strings to databases
279
+ # and validations.
280
+ #
281
+ # * <tt>string</tt> - The string to perform normalization on.
282
+ # * <tt>form</tt> - The form you want to normalize in. Should be one of
283
+ # the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
284
+ # Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
285
+ def normalize(string, form=nil)
286
+ form ||= @default_normalization_form
287
+ # See http://www.unicode.org/reports/tr15, Table 1
288
+ codepoints = string.codepoints.to_a
289
+ case form
290
+ when :d
291
+ reorder_characters(decompose(:canonical, codepoints))
292
+ when :c
293
+ compose(reorder_characters(decompose(:canonical, codepoints)))
294
+ when :kd
295
+ reorder_characters(decompose(:compatibility, codepoints))
296
+ when :kc
297
+ compose(reorder_characters(decompose(:compatibility, codepoints)))
298
+ else
299
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
300
+ end.pack('U*'.freeze)
301
+ end
302
+
303
+ def downcase(string)
304
+ apply_mapping string, :lowercase_mapping
305
+ end
306
+
307
+ def upcase(string)
308
+ apply_mapping string, :uppercase_mapping
309
+ end
310
+
311
+ def swapcase(string)
312
+ apply_mapping string, :swapcase_mapping
313
+ end
314
+
315
+ # Holds data about a codepoint in the Unicode database.
316
+ class Codepoint
317
+ attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
318
+
319
+ # Initializing Codepoint object with default values
320
+ def initialize
321
+ @combining_class = 0
322
+ @uppercase_mapping = 0
323
+ @lowercase_mapping = 0
324
+ end
325
+
326
+ def swapcase_mapping
327
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
328
+ end
329
+ end
330
+
331
+ # Holds static data from the Unicode database.
332
+ class UnicodeDatabase
333
+ ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
334
+
335
+ attr_writer(*ATTRIBUTES)
336
+
337
+ def initialize
338
+ @codepoints = Hash.new(Codepoint.new)
339
+ @composition_exclusion = []
340
+ @composition_map = {}
341
+ @boundary = {}
342
+ @cp1252 = {}
343
+ end
344
+
345
+ # Lazy load the Unicode database so it's only loaded when it's actually used
346
+ ATTRIBUTES.each do |attr_name|
347
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
348
+ def #{attr_name} # def codepoints
349
+ load # load
350
+ @#{attr_name} # @codepoints
351
+ end # end
352
+ EOS
353
+ end
354
+
355
+ # Loads the Unicode database and returns all the internal objects of
356
+ # UnicodeDatabase.
357
+ def load
358
+ begin
359
+ @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
360
+ rescue => e
361
+ raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
362
+ end
363
+
364
+ # Redefine the === method so we can write shorter rules for grapheme cluster breaks
365
+ @boundary.each_key do |k|
366
+ @boundary[k].instance_eval do
367
+ def ===(other)
368
+ detect { |i| i === other } ? true : false
369
+ end
370
+ end if @boundary[k].kind_of?(Array)
371
+ end
372
+
373
+ # define attr_reader methods for the instance variables
374
+ class << self
375
+ attr_reader(*ATTRIBUTES)
376
+ end
377
+ end
378
+
379
+ # Returns the directory in which the data files are stored.
380
+ def self.dirname
381
+ File.dirname(__FILE__) + '/../values/'
382
+ end
383
+
384
+ # Returns the filename for the data file for this version.
385
+ def self.filename
386
+ File.expand_path File.join(dirname, "unicode_tables.dat")
387
+ end
388
+ end
389
+
390
+ private
391
+
392
+ def apply_mapping(string, mapping) #:nodoc:
393
+ database.codepoints
394
+ string.each_codepoint.map do |codepoint|
395
+ cp = database.codepoints[codepoint]
396
+ if cp and (ncp = cp.send(mapping)) and ncp > 0
397
+ ncp
398
+ else
399
+ codepoint
400
+ end
401
+ end.pack('U*')
402
+ end
403
+
404
+ def recode_windows1252_chars(string)
405
+ string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
406
+ end
407
+
408
+ def database
409
+ @database ||= UnicodeDatabase.new
410
+ end
411
+ end
412
+ end
413
+ end
@@ -0,0 +1,212 @@
1
+ require 'active_support/notifications/instrumenter'
2
+ require 'active_support/notifications/fanout'
3
+ require 'active_support/per_thread_registry'
4
+
5
+ module ActiveSupport
6
+ # = Notifications
7
+ #
8
+ # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
9
+ # Ruby.
10
+ #
11
+ # == Instrumenters
12
+ #
13
+ # To instrument an event you just need to do:
14
+ #
15
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
16
+ # render text: 'Foo'
17
+ # end
18
+ #
19
+ # That first executes the block and then notifies all subscribers once done.
20
+ #
21
+ # In the example above +render+ is the name of the event, and the rest is called
22
+ # the _payload_. The payload is a mechanism that allows instrumenters to pass
23
+ # extra information to subscribers. Payloads consist of a hash whose contents
24
+ # are arbitrary and generally depend on the event.
25
+ #
26
+ # == Subscribers
27
+ #
28
+ # You can consume those events and the information they provide by registering
29
+ # a subscriber.
30
+ #
31
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
32
+ # name # => String, name of the event (such as 'render' from above)
33
+ # start # => Time, when the instrumented block started execution
34
+ # finish # => Time, when the instrumented block ended execution
35
+ # id # => String, unique ID for this notification
36
+ # payload # => Hash, the payload
37
+ # end
38
+ #
39
+ # For instance, let's store all "render" events in an array:
40
+ #
41
+ # events = []
42
+ #
43
+ # ActiveSupport::Notifications.subscribe('render') do |*args|
44
+ # events << ActiveSupport::Notifications::Event.new(*args)
45
+ # end
46
+ #
47
+ # That code returns right away, you are just subscribing to "render" events.
48
+ # The block is saved and will be called whenever someone instruments "render":
49
+ #
50
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
51
+ # render text: 'Foo'
52
+ # end
53
+ #
54
+ # event = events.first
55
+ # event.name # => "render"
56
+ # event.duration # => 10 (in milliseconds)
57
+ # event.payload # => { extra: :information }
58
+ #
59
+ # The block in the <tt>subscribe</tt> call gets the name of the event, start
60
+ # timestamp, end timestamp, a string with a unique identifier for that event
61
+ # (something like "535801666f04d0298cd6"), and a hash with the payload, in
62
+ # that order.
63
+ #
64
+ # If an exception happens during that particular instrumentation the payload will
65
+ # have a key <tt>:exception</tt> with an array of two elements as value: a string with
66
+ # the name of the exception class, and the exception message.
67
+ #
68
+ # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
69
+ # is able to take the arguments as they come and provide an object-oriented
70
+ # interface to that data.
71
+ #
72
+ # It is also possible to pass an object which responds to <tt>call</tt> method
73
+ # as the second parameter to the <tt>subscribe</tt> method instead of a block:
74
+ #
75
+ # module ActionController
76
+ # class PageRequest
77
+ # def call(name, started, finished, unique_id, payload)
78
+ # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
79
+ # end
80
+ # end
81
+ # end
82
+ #
83
+ # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
84
+ #
85
+ # resulting in the following output within the logs including a hash with the payload:
86
+ #
87
+ # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
88
+ # controller: "Devise::SessionsController",
89
+ # action: "new",
90
+ # params: {"action"=>"new", "controller"=>"devise/sessions"},
91
+ # format: :html,
92
+ # method: "GET",
93
+ # path: "/login/sign_in",
94
+ # status: 200,
95
+ # view_runtime: 279.3080806732178,
96
+ # db_runtime: 40.053
97
+ # }
98
+ #
99
+ # You can also subscribe to all events whose name matches a certain regexp:
100
+ #
101
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
102
+ # ...
103
+ # end
104
+ #
105
+ # and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
106
+ # to all events.
107
+ #
108
+ # == Temporary Subscriptions
109
+ #
110
+ # Sometimes you do not want to subscribe to an event for the entire life of
111
+ # the application. There are two ways to unsubscribe.
112
+ #
113
+ # WARNING: The instrumentation framework is designed for long-running subscribers,
114
+ # use this feature sparingly because it wipes some internal caches and that has
115
+ # a negative impact on performance.
116
+ #
117
+ # === Subscribe While a Block Runs
118
+ #
119
+ # You can subscribe to some event temporarily while some block runs. For
120
+ # example, in
121
+ #
122
+ # callback = lambda {|*args| ... }
123
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
124
+ # ...
125
+ # end
126
+ #
127
+ # the callback will be called for all "sql.active_record" events instrumented
128
+ # during the execution of the block. The callback is unsubscribed automatically
129
+ # after that.
130
+ #
131
+ # === Manual Unsubscription
132
+ #
133
+ # The +subscribe+ method returns a subscriber object:
134
+ #
135
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
136
+ # ...
137
+ # end
138
+ #
139
+ # To prevent that block from being called anymore, just unsubscribe passing
140
+ # that reference:
141
+ #
142
+ # ActiveSupport::Notifications.unsubscribe(subscriber)
143
+ #
144
+ # You can also unsubscribe by passing the name of the subscriber object. Note
145
+ # that this will unsubscribe all subscriptions with the given name:
146
+ #
147
+ # ActiveSupport::Notifications.unsubscribe("render")
148
+ #
149
+ # == Default Queue
150
+ #
151
+ # Notifications ships with a queue implementation that consumes and publishes events
152
+ # to all log subscribers. You can use any queue implementation you want.
153
+ #
154
+ module Notifications
155
+ class << self
156
+ attr_accessor :notifier
157
+
158
+ def publish(name, *args)
159
+ notifier.publish(name, *args)
160
+ end
161
+
162
+ def instrument(name, payload = {})
163
+ if notifier.listening?(name)
164
+ instrumenter.instrument(name, payload) { yield payload if block_given? }
165
+ else
166
+ yield payload if block_given?
167
+ end
168
+ end
169
+
170
+ def subscribe(*args, &block)
171
+ notifier.subscribe(*args, &block)
172
+ end
173
+
174
+ def subscribed(callback, *args, &block)
175
+ subscriber = subscribe(*args, &callback)
176
+ yield
177
+ ensure
178
+ unsubscribe(subscriber)
179
+ end
180
+
181
+ def unsubscribe(subscriber_or_name)
182
+ notifier.unsubscribe(subscriber_or_name)
183
+ end
184
+
185
+ def instrumenter
186
+ InstrumentationRegistry.instance.instrumenter_for(notifier)
187
+ end
188
+ end
189
+
190
+ # This class is a registry which holds all of the +Instrumenter+ objects
191
+ # in a particular thread local. To access the +Instrumenter+ object for a
192
+ # particular +notifier+, you can call the following method:
193
+ #
194
+ # InstrumentationRegistry.instrumenter_for(notifier)
195
+ #
196
+ # The instrumenters for multiple notifiers are held in a single instance of
197
+ # this class.
198
+ class InstrumentationRegistry # :nodoc:
199
+ extend ActiveSupport::PerThreadRegistry
200
+
201
+ def initialize
202
+ @registry = {}
203
+ end
204
+
205
+ def instrumenter_for(notifier)
206
+ @registry[notifier] ||= Instrumenter.new(notifier)
207
+ end
208
+ end
209
+
210
+ self.notifier = Fanout.new
211
+ end
212
+ end