core_ext 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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