core_ext 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -0
  3. data/lib/core_ext/array/access.rb +76 -0
  4. data/lib/core_ext/array/conversions.rb +211 -0
  5. data/lib/core_ext/array/extract_options.rb +29 -0
  6. data/lib/core_ext/array/grouping.rb +116 -0
  7. data/lib/core_ext/array/inquiry.rb +17 -0
  8. data/lib/core_ext/array/prepend_and_append.rb +7 -0
  9. data/lib/core_ext/array/wrap.rb +46 -0
  10. data/lib/core_ext/array.rb +7 -0
  11. data/lib/core_ext/array_inquirer.rb +44 -0
  12. data/lib/core_ext/benchmark.rb +14 -0
  13. data/lib/core_ext/benchmarkable.rb +49 -0
  14. data/lib/core_ext/big_decimal/conversions.rb +14 -0
  15. data/lib/core_ext/big_decimal.rb +1 -0
  16. data/lib/core_ext/builder.rb +6 -0
  17. data/lib/core_ext/callbacks.rb +770 -0
  18. data/lib/core_ext/class/attribute.rb +128 -0
  19. data/lib/core_ext/class/attribute_accessors.rb +4 -0
  20. data/lib/core_ext/class/subclasses.rb +42 -0
  21. data/lib/core_ext/class.rb +2 -0
  22. data/lib/core_ext/concern.rb +142 -0
  23. data/lib/core_ext/configurable.rb +148 -0
  24. data/lib/core_ext/date/acts_like.rb +8 -0
  25. data/lib/core_ext/date/blank.rb +12 -0
  26. data/lib/core_ext/date/calculations.rb +143 -0
  27. data/lib/core_ext/date/conversions.rb +93 -0
  28. data/lib/core_ext/date/zones.rb +6 -0
  29. data/lib/core_ext/date.rb +5 -0
  30. data/lib/core_ext/date_and_time/calculations.rb +328 -0
  31. data/lib/core_ext/date_and_time/zones.rb +40 -0
  32. data/lib/core_ext/date_time/acts_like.rb +14 -0
  33. data/lib/core_ext/date_time/blank.rb +12 -0
  34. data/lib/core_ext/date_time/calculations.rb +177 -0
  35. data/lib/core_ext/date_time/conversions.rb +104 -0
  36. data/lib/core_ext/date_time/zones.rb +6 -0
  37. data/lib/core_ext/date_time.rb +5 -0
  38. data/lib/core_ext/deprecation/behaviors.rb +86 -0
  39. data/lib/core_ext/deprecation/instance_delegator.rb +24 -0
  40. data/lib/core_ext/deprecation/method_wrappers.rb +70 -0
  41. data/lib/core_ext/deprecation/proxy_wrappers.rb +149 -0
  42. data/lib/core_ext/deprecation/reporting.rb +105 -0
  43. data/lib/core_ext/deprecation.rb +43 -0
  44. data/lib/core_ext/digest/uuid.rb +51 -0
  45. data/lib/core_ext/duration.rb +157 -0
  46. data/lib/core_ext/enumerable.rb +106 -0
  47. data/lib/core_ext/file/atomic.rb +68 -0
  48. data/lib/core_ext/file.rb +1 -0
  49. data/lib/core_ext/hash/compact.rb +20 -0
  50. data/lib/core_ext/hash/conversions.rb +261 -0
  51. data/lib/core_ext/hash/deep_merge.rb +38 -0
  52. data/lib/core_ext/hash/except.rb +22 -0
  53. data/lib/core_ext/hash/indifferent_access.rb +23 -0
  54. data/lib/core_ext/hash/keys.rb +170 -0
  55. data/lib/core_ext/hash/reverse_merge.rb +22 -0
  56. data/lib/core_ext/hash/slice.rb +48 -0
  57. data/lib/core_ext/hash/transform_values.rb +29 -0
  58. data/lib/core_ext/hash.rb +9 -0
  59. data/lib/core_ext/hash_with_indifferent_access.rb +298 -0
  60. data/lib/core_ext/inflections.rb +70 -0
  61. data/lib/core_ext/inflector/inflections.rb +244 -0
  62. data/lib/core_ext/inflector/methods.rb +381 -0
  63. data/lib/core_ext/inflector/transliterate.rb +112 -0
  64. data/lib/core_ext/inflector.rb +7 -0
  65. data/lib/core_ext/integer/inflections.rb +29 -0
  66. data/lib/core_ext/integer/multiple.rb +10 -0
  67. data/lib/core_ext/integer/time.rb +29 -0
  68. data/lib/core_ext/integer.rb +3 -0
  69. data/lib/core_ext/json/decoding.rb +67 -0
  70. data/lib/core_ext/json/encoding.rb +127 -0
  71. data/lib/core_ext/json.rb +2 -0
  72. data/lib/core_ext/kernel/agnostics.rb +11 -0
  73. data/lib/core_ext/kernel/concern.rb +10 -0
  74. data/lib/core_ext/kernel/reporting.rb +41 -0
  75. data/lib/core_ext/kernel/singleton_class.rb +6 -0
  76. data/lib/core_ext/kernel.rb +4 -0
  77. data/lib/core_ext/load_error.rb +30 -0
  78. data/lib/core_ext/logger.rb +57 -0
  79. data/lib/core_ext/logger_silence.rb +24 -0
  80. data/lib/core_ext/marshal.rb +19 -0
  81. data/lib/core_ext/module/aliasing.rb +74 -0
  82. data/lib/core_ext/module/anonymous.rb +28 -0
  83. data/lib/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/core_ext/module/concerning.rb +135 -0
  86. data/lib/core_ext/module/delegation.rb +218 -0
  87. data/lib/core_ext/module/deprecation.rb +23 -0
  88. data/lib/core_ext/module/introspection.rb +62 -0
  89. data/lib/core_ext/module/method_transplanting.rb +3 -0
  90. data/lib/core_ext/module/qualified_const.rb +52 -0
  91. data/lib/core_ext/module/reachable.rb +8 -0
  92. data/lib/core_ext/module/remove_method.rb +35 -0
  93. data/lib/core_ext/module.rb +11 -0
  94. data/lib/core_ext/multibyte/chars.rb +231 -0
  95. data/lib/core_ext/multibyte/unicode.rb +388 -0
  96. data/lib/core_ext/multibyte.rb +21 -0
  97. data/lib/core_ext/name_error.rb +31 -0
  98. data/lib/core_ext/numeric/bytes.rb +64 -0
  99. data/lib/core_ext/numeric/conversions.rb +132 -0
  100. data/lib/core_ext/numeric/inquiry.rb +26 -0
  101. data/lib/core_ext/numeric/time.rb +74 -0
  102. data/lib/core_ext/numeric.rb +4 -0
  103. data/lib/core_ext/object/acts_like.rb +10 -0
  104. data/lib/core_ext/object/blank.rb +140 -0
  105. data/lib/core_ext/object/conversions.rb +4 -0
  106. data/lib/core_ext/object/deep_dup.rb +53 -0
  107. data/lib/core_ext/object/duplicable.rb +98 -0
  108. data/lib/core_ext/object/inclusion.rb +27 -0
  109. data/lib/core_ext/object/instance_variables.rb +28 -0
  110. data/lib/core_ext/object/json.rb +199 -0
  111. data/lib/core_ext/object/to_param.rb +1 -0
  112. data/lib/core_ext/object/to_query.rb +84 -0
  113. data/lib/core_ext/object/try.rb +146 -0
  114. data/lib/core_ext/object/with_options.rb +69 -0
  115. data/lib/core_ext/object.rb +14 -0
  116. data/lib/core_ext/option_merger.rb +25 -0
  117. data/lib/core_ext/ordered_hash.rb +48 -0
  118. data/lib/core_ext/ordered_options.rb +81 -0
  119. data/lib/core_ext/range/conversions.rb +34 -0
  120. data/lib/core_ext/range/each.rb +21 -0
  121. data/lib/core_ext/range/include_range.rb +23 -0
  122. data/lib/core_ext/range/overlaps.rb +8 -0
  123. data/lib/core_ext/range.rb +4 -0
  124. data/lib/core_ext/regexp.rb +5 -0
  125. data/lib/core_ext/rescuable.rb +119 -0
  126. data/lib/core_ext/securerandom.rb +23 -0
  127. data/lib/core_ext/security_utils.rb +20 -0
  128. data/lib/core_ext/string/access.rb +104 -0
  129. data/lib/core_ext/string/behavior.rb +6 -0
  130. data/lib/core_ext/string/conversions.rb +56 -0
  131. data/lib/core_ext/string/exclude.rb +11 -0
  132. data/lib/core_ext/string/filters.rb +102 -0
  133. data/lib/core_ext/string/indent.rb +43 -0
  134. data/lib/core_ext/string/inflections.rb +235 -0
  135. data/lib/core_ext/string/inquiry.rb +13 -0
  136. data/lib/core_ext/string/multibyte.rb +53 -0
  137. data/lib/core_ext/string/output_safety.rb +261 -0
  138. data/lib/core_ext/string/starts_ends_with.rb +4 -0
  139. data/lib/core_ext/string/strip.rb +23 -0
  140. data/lib/core_ext/string/zones.rb +14 -0
  141. data/lib/core_ext/string.rb +13 -0
  142. data/lib/core_ext/string_inquirer.rb +26 -0
  143. data/lib/core_ext/tagged_logging.rb +78 -0
  144. data/lib/core_ext/test_case.rb +88 -0
  145. data/lib/core_ext/testing/assertions.rb +99 -0
  146. data/lib/core_ext/testing/autorun.rb +12 -0
  147. data/lib/core_ext/testing/composite_filter.rb +54 -0
  148. data/lib/core_ext/testing/constant_lookup.rb +50 -0
  149. data/lib/core_ext/testing/declarative.rb +26 -0
  150. data/lib/core_ext/testing/deprecation.rb +36 -0
  151. data/lib/core_ext/testing/file_fixtures.rb +34 -0
  152. data/lib/core_ext/testing/isolation.rb +115 -0
  153. data/lib/core_ext/testing/method_call_assertions.rb +41 -0
  154. data/lib/core_ext/testing/setup_and_teardown.rb +50 -0
  155. data/lib/core_ext/testing/stream.rb +42 -0
  156. data/lib/core_ext/testing/tagged_logging.rb +25 -0
  157. data/lib/core_ext/testing/time_helpers.rb +134 -0
  158. data/lib/core_ext/time/acts_like.rb +8 -0
  159. data/lib/core_ext/time/calculations.rb +284 -0
  160. data/lib/core_ext/time/conversions.rb +66 -0
  161. data/lib/core_ext/time/zones.rb +95 -0
  162. data/lib/core_ext/time.rb +20 -0
  163. data/lib/core_ext/time_with_zone.rb +503 -0
  164. data/lib/core_ext/time_zone.rb +464 -0
  165. data/lib/core_ext/uri.rb +25 -0
  166. data/lib/core_ext/version.rb +3 -0
  167. data/lib/core_ext/xml_mini/jdom.rb +181 -0
  168. data/lib/core_ext/xml_mini/libxml.rb +79 -0
  169. data/lib/core_ext/xml_mini/libxmlsax.rb +85 -0
  170. data/lib/core_ext/xml_mini/nokogiri.rb +83 -0
  171. data/lib/core_ext/xml_mini/nokogirisax.rb +87 -0
  172. data/lib/core_ext/xml_mini/rexml.rb +130 -0
  173. data/lib/core_ext/xml_mini.rb +194 -0
  174. data/lib/core_ext.rb +3 -0
  175. metadata +310 -0
@@ -0,0 +1,388 @@
1
+ module CoreExt
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
+ # CoreExt::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
+ if (
91
+ # CR X LF
92
+ ( previous == database.boundary[:cr] and current == database.boundary[:lf] ) or
93
+ # L X (L|V|LV|LVT)
94
+ ( database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
95
+ # (LV|V) X (V|T)
96
+ ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
97
+ # (LVT|T) X (T)
98
+ ( in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current ) or
99
+ # X Extend
100
+ (database.boundary[:extend] === current)
101
+ )
102
+ else
103
+ unpacked << codepoints[marker..pos-1]
104
+ marker = pos
105
+ end
106
+ end
107
+ unpacked
108
+ end
109
+
110
+ # Reverse operation of unpack_graphemes.
111
+ #
112
+ # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
113
+ def pack_graphemes(unpacked)
114
+ unpacked.flatten.pack('U*')
115
+ end
116
+
117
+ # Re-order codepoints so the string becomes canonical.
118
+ def reorder_characters(codepoints)
119
+ length = codepoints.length- 1
120
+ pos = 0
121
+ while pos < length do
122
+ cp1, cp2 = database.codepoints[codepoints[pos]], database.codepoints[codepoints[pos+1]]
123
+ if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
124
+ codepoints[pos..pos+1] = cp2.code, cp1.code
125
+ pos += (pos > 0 ? -1 : 1)
126
+ else
127
+ pos += 1
128
+ end
129
+ end
130
+ codepoints
131
+ end
132
+
133
+ # Decompose composed characters to the decomposed form.
134
+ def decompose(type, codepoints)
135
+ codepoints.inject([]) do |decomposed, cp|
136
+ # if it's a hangul syllable starter character
137
+ if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
138
+ sindex = cp - HANGUL_SBASE
139
+ ncp = [] # new codepoints
140
+ ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
141
+ ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
142
+ tindex = sindex % HANGUL_TCOUNT
143
+ ncp << (HANGUL_TBASE + tindex) unless tindex == 0
144
+ decomposed.concat ncp
145
+ # if the codepoint is decomposable in with the current decomposition type
146
+ elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatibility)
147
+ decomposed.concat decompose(type, ncp.dup)
148
+ else
149
+ decomposed << cp
150
+ end
151
+ end
152
+ end
153
+
154
+ # Compose decomposed characters to the composed form.
155
+ def compose(codepoints)
156
+ pos = 0
157
+ eoa = codepoints.length - 1
158
+ starter_pos = 0
159
+ starter_char = codepoints[0]
160
+ previous_combining_class = -1
161
+ while pos < eoa
162
+ pos += 1
163
+ lindex = starter_char - HANGUL_LBASE
164
+ # -- Hangul
165
+ if 0 <= lindex and lindex < HANGUL_LCOUNT
166
+ vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
167
+ if 0 <= vindex and vindex < HANGUL_VCOUNT
168
+ tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
169
+ if 0 <= tindex and tindex < HANGUL_TCOUNT
170
+ j = starter_pos + 2
171
+ eoa -= 2
172
+ else
173
+ tindex = 0
174
+ j = starter_pos + 1
175
+ eoa -= 1
176
+ end
177
+ codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
178
+ end
179
+ starter_pos += 1
180
+ starter_char = codepoints[starter_pos]
181
+ # -- Other characters
182
+ else
183
+ current_char = codepoints[pos]
184
+ current = database.codepoints[current_char]
185
+ if current.combining_class > previous_combining_class
186
+ if ref = database.composition_map[starter_char]
187
+ composition = ref[current_char]
188
+ else
189
+ composition = nil
190
+ end
191
+ unless composition.nil?
192
+ codepoints[starter_pos] = composition
193
+ starter_char = composition
194
+ codepoints.delete_at pos
195
+ eoa -= 1
196
+ pos -= 1
197
+ previous_combining_class = -1
198
+ else
199
+ previous_combining_class = current.combining_class
200
+ end
201
+ else
202
+ previous_combining_class = current.combining_class
203
+ end
204
+ if current.combining_class == 0
205
+ starter_pos = pos
206
+ starter_char = codepoints[pos]
207
+ end
208
+ end
209
+ end
210
+ codepoints
211
+ end
212
+
213
+ # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
214
+ if !defined?(Rubinius)
215
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
216
+ # resulting in a valid UTF-8 string.
217
+ #
218
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's
219
+ # encoding is entirely CP1252 or ISO-8859-1.
220
+ def tidy_bytes(string, force = false)
221
+ return string if string.empty?
222
+ return recode_windows1252_chars(string) if force
223
+ string.scrub { |bad| recode_windows1252_chars(bad) }
224
+ end
225
+ else
226
+ def tidy_bytes(string, force = false)
227
+ return string if string.empty?
228
+ return recode_windows1252_chars(string) if force
229
+
230
+ # We can't transcode to the same format, so we choose a nearly-identical encoding.
231
+ # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
232
+ # CP1252 when we get errors. The final string will be 'converted' back to UTF-8
233
+ # before returning.
234
+ reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
235
+
236
+ source = string.dup
237
+ out = ''.force_encoding(Encoding::UTF_16LE)
238
+
239
+ loop do
240
+ reader.primitive_convert(source, out)
241
+ _, _, _, error_bytes, _ = reader.primitive_errinfo
242
+ break if error_bytes.nil?
243
+ out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace)
244
+ end
245
+
246
+ reader.finish
247
+
248
+ out.encode!(Encoding::UTF_8)
249
+ end
250
+ end
251
+
252
+ # Returns the KC normalization of the string by default. NFKC is
253
+ # considered the best normalization form for passing strings to databases
254
+ # and validations.
255
+ #
256
+ # * <tt>string</tt> - The string to perform normalization on.
257
+ # * <tt>form</tt> - The form you want to normalize in. Should be one of
258
+ # the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
259
+ # Default is CoreExt::Multibyte::Unicode.default_normalization_form.
260
+ def normalize(string, form=nil)
261
+ form ||= @default_normalization_form
262
+ # See http://www.unicode.org/reports/tr15, Table 1
263
+ codepoints = string.codepoints.to_a
264
+ case form
265
+ when :d
266
+ reorder_characters(decompose(:canonical, codepoints))
267
+ when :c
268
+ compose(reorder_characters(decompose(:canonical, codepoints)))
269
+ when :kd
270
+ reorder_characters(decompose(:compatibility, codepoints))
271
+ when :kc
272
+ compose(reorder_characters(decompose(:compatibility, codepoints)))
273
+ else
274
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
275
+ end.pack('U*'.freeze)
276
+ end
277
+
278
+ def downcase(string)
279
+ apply_mapping string, :lowercase_mapping
280
+ end
281
+
282
+ def upcase(string)
283
+ apply_mapping string, :uppercase_mapping
284
+ end
285
+
286
+ def swapcase(string)
287
+ apply_mapping string, :swapcase_mapping
288
+ end
289
+
290
+ # Holds data about a codepoint in the Unicode database.
291
+ class Codepoint
292
+ attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
293
+
294
+ # Initializing Codepoint object with default values
295
+ def initialize
296
+ @combining_class = 0
297
+ @uppercase_mapping = 0
298
+ @lowercase_mapping = 0
299
+ end
300
+
301
+ def swapcase_mapping
302
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
303
+ end
304
+ end
305
+
306
+ # Holds static data from the Unicode database.
307
+ class UnicodeDatabase
308
+ ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
309
+
310
+ attr_writer(*ATTRIBUTES)
311
+
312
+ def initialize
313
+ @codepoints = Hash.new(Codepoint.new)
314
+ @composition_exclusion = []
315
+ @composition_map = {}
316
+ @boundary = {}
317
+ @cp1252 = {}
318
+ end
319
+
320
+ # Lazy load the Unicode database so it's only loaded when it's actually used
321
+ ATTRIBUTES.each do |attr_name|
322
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
323
+ def #{attr_name} # def codepoints
324
+ load # load
325
+ @#{attr_name} # @codepoints
326
+ end # end
327
+ EOS
328
+ end
329
+
330
+ # Loads the Unicode database and returns all the internal objects of
331
+ # UnicodeDatabase.
332
+ def load
333
+ begin
334
+ @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
335
+ rescue => e
336
+ raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), CoreExt::Multibyte is unusable")
337
+ end
338
+
339
+ # Redefine the === method so we can write shorter rules for grapheme cluster breaks
340
+ @boundary.each_key do |k|
341
+ @boundary[k].instance_eval do
342
+ def ===(other)
343
+ detect { |i| i === other } ? true : false
344
+ end
345
+ end if @boundary[k].kind_of?(Array)
346
+ end
347
+
348
+ # define attr_reader methods for the instance variables
349
+ class << self
350
+ attr_reader(*ATTRIBUTES)
351
+ end
352
+ end
353
+
354
+ # Returns the directory in which the data files are stored.
355
+ def self.dirname
356
+ File.dirname(__FILE__) + '/../values/'
357
+ end
358
+
359
+ # Returns the filename for the data file for this version.
360
+ def self.filename
361
+ File.expand_path File.join(dirname, "unicode_tables.dat")
362
+ end
363
+ end
364
+
365
+ private
366
+
367
+ def apply_mapping(string, mapping) #:nodoc:
368
+ database.codepoints
369
+ string.each_codepoint.map do |codepoint|
370
+ cp = database.codepoints[codepoint]
371
+ if cp and (ncp = cp.send(mapping)) and ncp > 0
372
+ ncp
373
+ else
374
+ codepoint
375
+ end
376
+ end.pack('U*')
377
+ end
378
+
379
+ def recode_windows1252_chars(string)
380
+ string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
381
+ end
382
+
383
+ def database
384
+ @database ||= UnicodeDatabase.new
385
+ end
386
+ end
387
+ end
388
+ end
@@ -0,0 +1,21 @@
1
+ module CoreExt #:nodoc:
2
+ module Multibyte
3
+ require 'core_ext/multibyte/chars'
4
+ require 'core_ext/multibyte/unicode'
5
+
6
+ # The proxy class returned when calling mb_chars. You can use this accessor
7
+ # to configure your own proxy class so you can support other encodings. See
8
+ # the CoreExt::Multibyte::Chars implementation for an example how to
9
+ # do this.
10
+ #
11
+ # CoreExt::Multibyte.proxy_class = CharsForUTF32
12
+ def self.proxy_class=(klass)
13
+ @proxy_class = klass
14
+ end
15
+
16
+ # Returns the current proxy class.
17
+ def self.proxy_class
18
+ @proxy_class ||= CoreExt::Multibyte::Chars
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ class NameError
2
+ # Extract the name of the missing constant from the exception message.
3
+ #
4
+ # begin
5
+ # HelloWorld
6
+ # rescue NameError => e
7
+ # e.missing_name
8
+ # end
9
+ # # => "HelloWorld"
10
+ def missing_name
11
+ if /undefined local variable or method/ !~ message
12
+ $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
13
+ end
14
+ end
15
+
16
+ # Was this exception raised because the given name was missing?
17
+ #
18
+ # begin
19
+ # HelloWorld
20
+ # rescue NameError => e
21
+ # e.missing_name?("HelloWorld")
22
+ # end
23
+ # # => true
24
+ def missing_name?(name)
25
+ if name.is_a? Symbol
26
+ self.name == name
27
+ else
28
+ missing_name == name.to_s
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,64 @@
1
+ class Numeric
2
+ KILOBYTE = 1024
3
+ MEGABYTE = KILOBYTE * 1024
4
+ GIGABYTE = MEGABYTE * 1024
5
+ TERABYTE = GIGABYTE * 1024
6
+ PETABYTE = TERABYTE * 1024
7
+ EXABYTE = PETABYTE * 1024
8
+
9
+ # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
10
+ #
11
+ # 2.bytes # => 2
12
+ def bytes
13
+ self
14
+ end
15
+ alias :byte :bytes
16
+
17
+ # Returns the number of bytes equivalent to the kilobytes provided.
18
+ #
19
+ # 2.kilobytes # => 2048
20
+ def kilobytes
21
+ self * KILOBYTE
22
+ end
23
+ alias :kilobyte :kilobytes
24
+
25
+ # Returns the number of bytes equivalent to the megabytes provided.
26
+ #
27
+ # 2.megabytes # => 2_097_152
28
+ def megabytes
29
+ self * MEGABYTE
30
+ end
31
+ alias :megabyte :megabytes
32
+
33
+ # Returns the number of bytes equivalent to the gigabytes provided.
34
+ #
35
+ # 2.gigabytes # => 2_147_483_648
36
+ def gigabytes
37
+ self * GIGABYTE
38
+ end
39
+ alias :gigabyte :gigabytes
40
+
41
+ # Returns the number of bytes equivalent to the terabytes provided.
42
+ #
43
+ # 2.terabytes # => 2_199_023_255_552
44
+ def terabytes
45
+ self * TERABYTE
46
+ end
47
+ alias :terabyte :terabytes
48
+
49
+ # Returns the number of bytes equivalent to the petabytes provided.
50
+ #
51
+ # 2.petabytes # => 2_251_799_813_685_248
52
+ def petabytes
53
+ self * PETABYTE
54
+ end
55
+ alias :petabyte :petabytes
56
+
57
+ # Returns the number of bytes equivalent to the exabytes provided.
58
+ #
59
+ # 2.exabytes # => 2_305_843_009_213_693_952
60
+ def exabytes
61
+ self * EXABYTE
62
+ end
63
+ alias :exabyte :exabytes
64
+ end
@@ -0,0 +1,132 @@
1
+ require 'core_ext/big_decimal/conversions'
2
+ require 'core_ext/deprecation'
3
+
4
+ module CoreExt::NumericWithFormat
5
+
6
+ # Provides options for converting numbers into formatted strings.
7
+ # Options are provided for phone numbers, currency, percentage,
8
+ # precision, positional notation, file size and pretty printing.
9
+ #
10
+ # ==== Options
11
+ #
12
+ # For details on which formats use which options, see CoreExt::NumberHelper
13
+ #
14
+ # ==== Examples
15
+ #
16
+ # Phone Numbers:
17
+ # 5551234.to_s(:phone) # => 555-1234
18
+ # 1235551234.to_s(:phone) # => 123-555-1234
19
+ # 1235551234.to_s(:phone, area_code: true) # => (123) 555-1234
20
+ # 1235551234.to_s(:phone, delimiter: ' ') # => 123 555 1234
21
+ # 1235551234.to_s(:phone, area_code: true, extension: 555) # => (123) 555-1234 x 555
22
+ # 1235551234.to_s(:phone, country_code: 1) # => +1-123-555-1234
23
+ # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
24
+ # # => +1.123.555.1234 x 1343
25
+ #
26
+ # Currency:
27
+ # 1234567890.50.to_s(:currency) # => $1,234,567,890.50
28
+ # 1234567890.506.to_s(:currency) # => $1,234,567,890.51
29
+ # 1234567890.506.to_s(:currency, precision: 3) # => $1,234,567,890.506
30
+ # 1234567890.506.to_s(:currency, locale: :fr) # => 1 234 567 890,51 €
31
+ # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
32
+ # # => ($1,234,567,890.50)
33
+ # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
34
+ # # => &pound;1234567890,50
35
+ # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
36
+ # # => 1234567890,50 &pound;
37
+ #
38
+ # Percentage:
39
+ # 100.to_s(:percentage) # => 100.000%
40
+ # 100.to_s(:percentage, precision: 0) # => 100%
41
+ # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => 1.000,000%
42
+ # 302.24398923423.to_s(:percentage, precision: 5) # => 302.24399%
43
+ # 1000.to_s(:percentage, locale: :fr) # => 1 000,000%
44
+ # 100.to_s(:percentage, format: '%n %') # => 100.000 %
45
+ #
46
+ # Delimited:
47
+ # 12345678.to_s(:delimited) # => 12,345,678
48
+ # 12345678.05.to_s(:delimited) # => 12,345,678.05
49
+ # 12345678.to_s(:delimited, delimiter: '.') # => 12.345.678
50
+ # 12345678.to_s(:delimited, delimiter: ',') # => 12,345,678
51
+ # 12345678.05.to_s(:delimited, separator: ' ') # => 12,345,678 05
52
+ # 12345678.05.to_s(:delimited, locale: :fr) # => 12 345 678,05
53
+ # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
54
+ # # => 98 765 432,98
55
+ #
56
+ # Rounded:
57
+ # 111.2345.to_s(:rounded) # => 111.235
58
+ # 111.2345.to_s(:rounded, precision: 2) # => 111.23
59
+ # 13.to_s(:rounded, precision: 5) # => 13.00000
60
+ # 389.32314.to_s(:rounded, precision: 0) # => 389
61
+ # 111.2345.to_s(:rounded, significant: true) # => 111
62
+ # 111.2345.to_s(:rounded, precision: 1, significant: true) # => 100
63
+ # 13.to_s(:rounded, precision: 5, significant: true) # => 13.000
64
+ # 111.234.to_s(:rounded, locale: :fr) # => 111,234
65
+ # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
66
+ # # => 13
67
+ # 389.32314.to_s(:rounded, precision: 4, significant: true) # => 389.3
68
+ # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
69
+ # # => 1.111,23
70
+ #
71
+ # Human-friendly size in Bytes:
72
+ # 123.to_s(:human_size) # => 123 Bytes
73
+ # 1234.to_s(:human_size) # => 1.21 KB
74
+ # 12345.to_s(:human_size) # => 12.1 KB
75
+ # 1234567.to_s(:human_size) # => 1.18 MB
76
+ # 1234567890.to_s(:human_size) # => 1.15 GB
77
+ # 1234567890123.to_s(:human_size) # => 1.12 TB
78
+ # 1234567.to_s(:human_size, precision: 2) # => 1.2 MB
79
+ # 483989.to_s(:human_size, precision: 2) # => 470 KB
80
+ # 1234567.to_s(:human_size, precision: 2, separator: ',') # => 1,2 MB
81
+ # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB"
82
+ # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
83
+ #
84
+ # Human-friendly format:
85
+ # 123.to_s(:human) # => "123"
86
+ # 1234.to_s(:human) # => "1.23 Thousand"
87
+ # 12345.to_s(:human) # => "12.3 Thousand"
88
+ # 1234567.to_s(:human) # => "1.23 Million"
89
+ # 1234567890.to_s(:human) # => "1.23 Billion"
90
+ # 1234567890123.to_s(:human) # => "1.23 Trillion"
91
+ # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
92
+ # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
93
+ # 489939.to_s(:human, precision: 2) # => "490 Thousand"
94
+ # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
95
+ # 1234567.to_s(:human, precision: 4,
96
+ # significant: false) # => "1.2346 Million"
97
+ # 1234567.to_s(:human, precision: 1,
98
+ # separator: ',',
99
+ # significant: false) # => "1,2 Million"
100
+ def to_s(*args)
101
+ format, options = args
102
+ options ||= {}
103
+
104
+ case format
105
+ when :phone
106
+ return CoreExt::NumberHelper.number_to_phone(self, options)
107
+ when :currency
108
+ return CoreExt::NumberHelper.number_to_currency(self, options)
109
+ when :percentage
110
+ return CoreExt::NumberHelper.number_to_percentage(self, options)
111
+ when :delimited
112
+ return CoreExt::NumberHelper.number_to_delimited(self, options)
113
+ when :rounded
114
+ return CoreExt::NumberHelper.number_to_rounded(self, options)
115
+ when :human
116
+ return CoreExt::NumberHelper.number_to_human(self, options)
117
+ when :human_size
118
+ return CoreExt::NumberHelper.number_to_human_size(self, options)
119
+ else
120
+ super
121
+ end
122
+ end
123
+
124
+ def to_formatted_s(*args)
125
+ to_s(*args)
126
+ end
127
+ deprecate to_formatted_s: :to_s
128
+ end
129
+
130
+ [Fixnum, Bignum, Float, BigDecimal].each do |klass|
131
+ klass.prepend(CoreExt::NumericWithFormat)
132
+ end
@@ -0,0 +1,26 @@
1
+ unless 1.respond_to?(:positive?) # TODO: Remove this file when we drop support to ruby < 2.3
2
+ class Numeric
3
+ # Returns true if the number is positive.
4
+ #
5
+ # 1.positive? # => true
6
+ # 0.positive? # => false
7
+ # -1.positive? # => false
8
+ def positive?
9
+ self > 0
10
+ end
11
+
12
+ # Returns true if the number is negative.
13
+ #
14
+ # -1.negative? # => true
15
+ # 0.negative? # => false
16
+ # 1.negative? # => false
17
+ def negative?
18
+ self < 0
19
+ end
20
+ end
21
+
22
+ class Complex
23
+ undef :positive?
24
+ undef :negative?
25
+ end
26
+ end