phony 1.0.0

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.
@@ -0,0 +1,67 @@
1
+ # Splits a national number using a prefix code.
2
+ #
3
+ # DSL:
4
+ # length: the length range of the dcs. For austria it is 1..4.
5
+ # format: the format of the dcs and rest. For austria it is '%s %s', default is '%s %s %s %s'.
6
+ # ndcs: an array of the prefix codes. For germany it starts with ['10', '11', '12', '13', '30'…
7
+ #
8
+ module Phony
9
+ module NDC
10
+ class Prefix < Splitter
11
+
12
+ # Splits the number into ndc and rest.
13
+ #
14
+ def self.split_ndc(number)
15
+ number = number.dup
16
+ presumed_code = ''
17
+ presumed_code << number.slice!(0..@min_ndc_length-2) if @min_ndc_length && @min_ndc_length > 1
18
+ (@min_ndc_length || 1).upto(@max_ndc_length) do |i|
19
+ presumed_code << number.slice!(0..0)
20
+ sized_ndcs = @ndcs[i]
21
+ break unless sized_ndcs && !sized_ndcs.include?(presumed_code)
22
+ end
23
+ return [presumed_code, number]
24
+ end
25
+
26
+ protected
27
+
28
+ # Define the length range for the country's ndcs.
29
+ #
30
+ def self.length(range)
31
+ @min_ndc_length, @max_ndc_length = range.min, range.max
32
+ end
33
+
34
+ # Define the list of NDCs.
35
+ #
36
+ # Note: For optimization, if the country has a max ndc length of 4,
37
+ # only enter ndcs up to the length of 3 letters.
38
+ # If the algorithm fails to match an ndc, it will assume it is a 4 digit ndc.
39
+ #
40
+ # e.g. We have NDCs '1', '22', '333', and '4444'.
41
+ # Only set
42
+ # ndcs '1', '22', '333'
43
+ # and
44
+ # length 1..4
45
+ # '4444' will be "recognized" because the algorithm does not find a shorter ndc (assuming it
46
+ # is a correct prefix code) and stop at the max ndc length of 4, given by the length method.
47
+ #
48
+ def self.ndcs(*ndcs_ary)
49
+ @ndcs = optimize(ndcs_ary)
50
+ end
51
+
52
+ private
53
+
54
+ # Optimizes and restructures the given ndcs array.
55
+ #
56
+ def self.optimize(ndcs_ary)
57
+ ndcs = {}
58
+ ndcs_ary.each do |ndc|
59
+ ndcs[ndc.length] ||= []
60
+ ndcs[ndc.length] << ndc
61
+ end
62
+ ndcs
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,81 @@
1
+ # Splits a national number into a fixed size NDC and rest.
2
+ #
3
+ module Phony
4
+ module NDC
5
+ class Splitter
6
+
7
+ # Sets the local grouping format.
8
+ #
9
+ # Examples
10
+ # * local 3, 2, 2 # Switzerland, 364 35 33, thus: 3-2-2.
11
+ # * local 2, 2, 2, 2 # France, 12 34 56 78, thus: 2-2-2-2.
12
+ #
13
+ def self.local *split_sizes
14
+ @split_sizes = split_sizes.flatten
15
+ format((['%s']*@split_sizes.size).join('%s'))
16
+ end
17
+
18
+ # Define a format for the country's local format.
19
+ #
20
+ # Examples
21
+ # * format '%s%s%s%s%s' # Switzerland, spaces between the groups, e.g. 364˽35˽32
22
+ #
23
+ def self.format format
24
+ @format = format
25
+ @format_with_ndc = ndc_format + @format
26
+ end
27
+
28
+ # Define a format for the country's national format.
29
+ #
30
+ # Default is NN followed by a space character.
31
+ #
32
+ def self.ndc_format
33
+ '%s%s'
34
+ end
35
+
36
+ # Split an E164 number without country code into its NDC-Local parts.
37
+ #
38
+ # Examples
39
+ # * split '443643533' # => ['44', '364', '35', '33'] # (Switzerland)
40
+ #
41
+ def self.split number
42
+ ndc_part = split_ndc(number)
43
+ [ndc_part, split_local(ndc_part.pop)].flatten
44
+ end
45
+
46
+ # Split a local number according to an assumed country specific format.
47
+ #
48
+ # Examples
49
+ # * split '3643533' # => ['364', '35', '33'] # (Switzerland)
50
+ #
51
+ def self.split_local number
52
+ local = []
53
+ @split_sizes.each do |size|
54
+ local << number.slice!(0..size-1)
55
+ break if number.empty?
56
+ end
57
+ local
58
+ end
59
+
60
+ # Formats the given E164 Number (NDC-Local without CC) according to the country specific format / ndcs splitting.
61
+ #
62
+ def self.formatted number, space = ' '
63
+ formatted_with_spaces @format_with_ndc, split(number), space
64
+ end
65
+
66
+ # Formats the given local number according to the country specific format.
67
+ #
68
+ def self.locally_formatted number, space = ' '
69
+ formatted_with_spaces @format, split_local(number), space
70
+ end
71
+
72
+ def self.formatted_with_spaces format, split_number, space
73
+ space ||= ' '
74
+ number_and_spaces = (split_number).zip([space]*split_number.size).flatten
75
+ format % number_and_spaces
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,496 @@
1
+ begin
2
+ gem 'activesupport', '~> 3.0'
3
+ rescue LoadError
4
+ puts "phony requires activesupport ~> 3.0"
5
+ end
6
+
7
+ require 'active_support/core_ext/object/blank'
8
+
9
+ # Framework.
10
+ #
11
+ require File.expand_path '../ndc/splitter', __FILE__
12
+ require File.expand_path '../ndc/fixed_size', __FILE__
13
+ require File.expand_path '../ndc/prefix', __FILE__
14
+
15
+ # Prefix code countries
16
+ #
17
+ require File.expand_path '../ndc/austria', __FILE__
18
+ require File.expand_path '../ndc/germany', __FILE__
19
+
20
+ # require 'Phony'
21
+
22
+ module Phony
23
+
24
+ include NDC
25
+
26
+ # Returns a fixed ndc splitter.
27
+ #
28
+ def self.fixed national_code_length = 2, options = {}
29
+ NDC.fixed national_code_length, options
30
+ end
31
+
32
+ # prefix length =>
33
+ # country code => national prefix length
34
+ @@country_codes = {
35
+ 1 => {
36
+ '0' => fixed(0), # reserved
37
+ '1' => fixed(3, :local => [3, 10]), # USA, Canada, etc.
38
+ '7' => fixed(3), # Kazakhstan (Republic of) & Russian Federation
39
+ },
40
+ 2 => {
41
+ '20' => fixed(2), # Egypt
42
+ '27' => fixed(2), # South Africa
43
+
44
+ '30' => fixed(2), # Greece
45
+ '31' => fixed(2), # Netherlands
46
+ '32' => fixed(2), # Belgium
47
+ '33' => fixed(1, :local => [2, 2, 2, 2]), # France
48
+ '34' => fixed(2), # Spain
49
+ '36' => fixed(2), # Hungary
50
+ '39' => fixed(3), # Italy
51
+
52
+ '40' => fixed(2), # Romania
53
+ '41' => fixed(2, :local => [3, 2, 2], :special_ndcs => ['800', '840', '842', '844', '848']), # Switzerland (Confederation of)
54
+ '43' => Austria,
55
+ '44' => fixed(2), # United Kingdom of Great Britain and Northern Ireland
56
+ '45' => fixed(2), # Denmark
57
+ '46' => fixed(2), # Sweden
58
+ '47' => fixed(2), # Norway
59
+ '48' => fixed(2), # Poland (Republic of)
60
+ '49' => Germany,
61
+
62
+ '51' => fixed(2), # Peru
63
+ '52' => fixed(2), # Mexico
64
+ '53' => fixed(2), # Cuba
65
+ '54' => fixed(2), # Argentine Republic
66
+ '55' => fixed(2), # Brazil (Federative Republic of)
67
+ '56' => fixed(2), # Chile
68
+ '57' => fixed(2), # Colombia (Republic of)
69
+ '58' => fixed(2), # Venezuela (Bolivarian Republic of)
70
+
71
+ '60' => fixed(2), # Malaysia
72
+ '61' => fixed(2), # Australia
73
+ '62' => fixed(2), # Indonesia (Republic of)
74
+ '63' => fixed(2), # Philippines (Republic of the)
75
+ '64' => fixed(1, :local => [3, 4]), # New Zealand
76
+ '65' => fixed(2), # Singapore (Republic of)
77
+ '66' => fixed(2), # Thailand
78
+
79
+ '81' => fixed(2), # Japan
80
+ '82' => fixed(2), # Korea (Republic of)
81
+ '84' => fixed(2), # Viet Nam (Socialist Republic of)
82
+ '86' => fixed(2), # China (People's Republic of)
83
+
84
+ '90' => fixed(2), # Turkey
85
+ '91' => fixed(2), # India (Republic of)
86
+ '92' => fixed(2), # Pakistan (Islamic Republic of)
87
+ '93' => fixed(2), # Afghanistan
88
+ '94' => fixed(2), # Sri Lanka (Democratic Socialist Republic of)
89
+ '95' => fixed(2), # Myanmar (Union of)
90
+ '98' => fixed(2), #Iran (Islamic Republic of)
91
+ },
92
+ 3 => {
93
+ '210' => fixed(2), # -
94
+ '211' => fixed(2), # -
95
+ '212' => fixed(2), # Morocco
96
+ '213' => fixed(2), # Algeria
97
+ '214' => fixed(2), # -
98
+ '215' => fixed(2), # -
99
+ '216' => fixed(2), # Tunisia
100
+ '217' => fixed(2), # -
101
+ '218' => fixed(2), # Lybia
102
+ '219' => fixed(2), # -
103
+
104
+ '220' => fixed(2), # Gambia
105
+ '221' => fixed(2), # Senegal
106
+ '222' => fixed(2), # Mauritania
107
+ '223' => fixed(2), # Mali
108
+ '224' => fixed(2), # Guinea
109
+ '225' => fixed(2), # Côte d'Ivoire
110
+ '226' => fixed(2), # Burkina Faso
111
+ '227' => fixed(2), # Niger
112
+ '228' => fixed(2), # Togolese Republic
113
+ '229' => fixed(2), # Benin
114
+
115
+ '230' => fixed(2), # Mauritius
116
+ '231' => fixed(2), # Liberia
117
+ '232' => fixed(2), # Sierra Leone
118
+ '233' => fixed(2), # Ghana
119
+ '234' => fixed(2), # Nigeria
120
+ '235' => fixed(2), # Chad
121
+ '236' => fixed(2), # Central African Republic
122
+ '237' => fixed(2), # Cameroon
123
+ '238' => fixed(2), # Cape Verde
124
+ '239' => fixed(2), # Sao Tome and Principe
125
+
126
+ '240' => fixed(2), # Equatorial Guinea
127
+ '241' => fixed(2), # Gabonese Republic
128
+ '242' => fixed(2), # Congo
129
+ '243' => fixed(2), # Democratic Republic of the Congo
130
+ '244' => fixed(2), # Angola
131
+ '245' => fixed(2), # Guinea-Bissau
132
+ '246' => fixed(2), # Diego Garcia
133
+ '247' => fixed(2), # Ascension
134
+ '248' => fixed(2), # Seychelles
135
+ '249' => fixed(2), # Sudan
136
+
137
+ '250' => fixed(2), # Rwanda
138
+ '251' => fixed(2), # Ethiopia
139
+ '252' => fixed(2), # Somali Democratic Republic
140
+ '253' => fixed(2), # Djibouti
141
+ '254' => fixed(2), # Kenya
142
+ '255' => fixed(2), # Tanzania
143
+ '256' => fixed(2), # Uganda
144
+ '257' => fixed(2), # Burundi
145
+ '258' => fixed(2), # Mozambique
146
+ '259' => fixed(2), # -
147
+
148
+ '260' => fixed(2), # Zambia
149
+ '261' => fixed(2), # Madagascar
150
+ '262' => fixed(3), # Reunion / Mayotte (new)
151
+ '263' => fixed(2), # Zimbabwe
152
+ '264' => fixed(2), # Namibia
153
+ '265' => fixed(2), # Malawi
154
+ '266' => fixed(2), # Lesotho
155
+ '267' => fixed(2), # Botswana
156
+ '268' => fixed(2), # Swaziland
157
+ '269' => fixed(2), # Comoros
158
+
159
+ '280' => fixed(2), # -
160
+ '281' => fixed(2), # -
161
+ '282' => fixed(2), # -
162
+ '283' => fixed(2), # -
163
+ '284' => fixed(2), # -
164
+ '285' => fixed(2), # -
165
+ '286' => fixed(2), # -
166
+ '287' => fixed(2), # -
167
+ '288' => fixed(2), # -
168
+ '289' => fixed(2), # -
169
+ '290' => fixed(2), # Saint Helena
170
+
171
+ '291' => fixed(2), # Eritrea
172
+ '292' => fixed(2), # -
173
+ '293' => fixed(2), # -
174
+ '294' => fixed(2), # -
175
+ '295' => fixed(2), # -
176
+ '296' => fixed(2), # -
177
+ '297' => fixed(2), # Aruba
178
+ '298' => fixed(2), # Faroe Islands
179
+ '299' => fixed(2), # Greenland
180
+
181
+ '350' => fixed(2), # Gibraltar
182
+ '351' => fixed(2), # Portugal
183
+ '352' => fixed(2), # Luxembourg
184
+ '353' => fixed(2), # Ireland
185
+ '354' => fixed(2), # Iceland
186
+ '355' => fixed(2), # Albania
187
+ '356' => fixed(2), # Malta
188
+ '357' => fixed(2), # Cyprus
189
+ '358' => fixed(2), # Finland
190
+ '359' => fixed(2), # Bulgaria
191
+
192
+ '370' => fixed(2), # Lithuania
193
+ '371' => fixed(2), # Latvia
194
+ '372' => fixed(2), # Estonia
195
+ '373' => fixed(2), # Moldova
196
+ '374' => fixed(2), # Armenia
197
+ '375' => fixed(2), # Belarus
198
+ '376' => fixed(2), # Andorra
199
+ '377' => fixed(2), # Monaco
200
+ '378' => fixed(2), # San Marino
201
+ '379' => fixed(2), # Vatican City State
202
+
203
+ '380' => fixed(2), # Ukraine
204
+ '381' => fixed(2), # Serbia and Montenegro
205
+ '382' => fixed(2), # -
206
+ '383' => fixed(2), # -
207
+ '384' => fixed(2), # -
208
+ '385' => fixed(2), # Croatia
209
+ '386' => fixed(2), # Slovenia
210
+ '387' => fixed(2), # Bosnia and Herzegovina
211
+ '388' => fixed(2), # Group of countries, shared code
212
+ '389' => fixed(2), # The Former Yugoslav Republic of Macedonia
213
+
214
+ '420' => fixed(2), # Czech Republic
215
+ '421' => fixed(2), # Slovak Republic
216
+ '422' => fixed(2), # Spare code
217
+ '423' => fixed(0, :local => [3, 2, 2]), # Liechtenstein (Principality of)
218
+ '424' => fixed(2), # -
219
+ '425' => fixed(2), # -
220
+ '426' => fixed(2), # -
221
+ '427' => fixed(2), # -
222
+ '428' => fixed(2), # -
223
+ '429' => fixed(2), # -
224
+
225
+ '500' => fixed(2), # Falkland Islands (Malvinas)
226
+ '501' => fixed(2), # Belize
227
+ '502' => fixed(2), # Guatemala (Republic of)
228
+ '503' => fixed(2), # El Salvador (Republic of)
229
+ '504' => fixed(2), # Honduras (Republic of)
230
+ '505' => fixed(2), # Nicaragua
231
+ '506' => fixed(2), # Costa Rica
232
+ '507' => fixed(2), # Panama (Republic of)
233
+ '508' => fixed(2), # Saint Pierre and Miquelon (Collectivité territoriale de la République française)
234
+ '509' => fixed(2), # Haiti (Republic of)
235
+
236
+ '590' => fixed(3), # Guadeloupe (French Department of)
237
+ '591' => fixed(2), # Bolivia (Republic of)
238
+ '592' => fixed(2), # Guyana
239
+ '593' => fixed(2), # Ecuador
240
+ '594' => fixed(3), # French Guiana (French Department of)
241
+ '595' => fixed(2), # Paraguay (Republic of)
242
+ '596' => fixed(3), # Martinique (French Department of)
243
+ '597' => fixed(2), # Suriname (Republic of)
244
+ '598' => fixed(2), # Uruguay (Eastern Republic of)
245
+ '599' => fixed(2), # Netherlands Antilles
246
+
247
+ '670' => fixed(2), # Democratic Republic of Timor-Leste
248
+ '671' => fixed(2), # Spare code
249
+ '672' => fixed(2), # Australian External Territories
250
+ '673' => fixed(2), # Brunei Darussalam
251
+ '674' => fixed(2), # Nauru (Republic of)
252
+ '675' => fixed(2), # Papua New Guinea
253
+ '676' => fixed(2), # Tonga (Kingdom of)
254
+ '677' => fixed(2), # Solomon Islands
255
+ '678' => fixed(2), # Vanuatu (Republic of)
256
+ '679' => fixed(2), # Fiji (Republic of)
257
+
258
+ '680' => fixed(2), # Palau (Republic of)
259
+ '681' => fixed(2), # Wallis and Futuna (Territoire français d'outre-mer)
260
+ '682' => fixed(2), # Cook Islands
261
+ '683' => fixed(2), # Niue
262
+ '684' => fixed(2), # -
263
+ '685' => fixed(2), # Samoa (Independent State of)
264
+ '686' => fixed(2), # Kiribati (Republic of)
265
+ '687' => fixed(2), # New Caledonia (Territoire français d'outre-mer)
266
+ '688' => fixed(2), # Tuvalu
267
+ '689' => fixed(2), # French Polynesia (Territoire français d'outre-mer)
268
+
269
+ '690' => fixed(2), # Tokelau
270
+ '691' => fixed(2), # Micronesia (Federated States of)
271
+ '692' => fixed(2), # Marshall Islands (Republic of the)
272
+ '693' => fixed(2), # -
273
+ '694' => fixed(2), # -
274
+ '695' => fixed(2), # -
275
+ '696' => fixed(2), # -
276
+ '697' => fixed(2), # -
277
+ '698' => fixed(2), # -
278
+ '699' => fixed(2), # -
279
+
280
+ '800' => fixed(2), # International Freephone Service
281
+ '801' => fixed(2), # -
282
+ '802' => fixed(2), # -
283
+ '803' => fixed(2), # -
284
+ '804' => fixed(2), # -
285
+ '805' => fixed(2), # -
286
+ '806' => fixed(2), # -
287
+ '807' => fixed(2), # -
288
+ '808' => fixed(2), # International Shared Cost Service (ISCS)
289
+ '809' => fixed(2), # -
290
+
291
+ '830' => fixed(2), # -
292
+ '831' => fixed(2), # -
293
+ '832' => fixed(2), # -
294
+ '833' => fixed(2), # -
295
+ '834' => fixed(2), # -
296
+ '835' => fixed(2), # -
297
+ '836' => fixed(2), # -
298
+ '837' => fixed(2), # -
299
+ '838' => fixed(2), # -
300
+ '839' => fixed(2), # -
301
+
302
+ '850' => fixed(2), # Democratic People's Republic of Korea
303
+ '851' => fixed(2), # Spare code
304
+ '852' => fixed(2), # Hong Kong, China
305
+ '853' => fixed(2), # Macao, China
306
+ '854' => fixed(2), # Spare code
307
+ '855' => fixed(2), # Cambodia (Kingdom of)
308
+ '856' => fixed(2), # Lao People's Democratic Republic
309
+ '857' => fixed(2), # Spare code
310
+ '858' => fixed(2), # Spare code
311
+ '859' => fixed(2), # Spare code
312
+
313
+ '870' => fixed(2), # Inmarsat SNAC
314
+ '871' => fixed(2), # Inmarsat (Atlantic Ocean-East)
315
+ '872' => fixed(2), # Inmarsat (Pacific Ocean)
316
+ '873' => fixed(2), # Inmarsat (Indian Ocean)
317
+ '874' => fixed(2), # Inmarsat (Atlantic Ocean-West)
318
+ '875' => fixed(2), # Reserved - Maritime Mobile Service Applications
319
+ '876' => fixed(2), # Reserved - Maritime Mobile Service Applications
320
+ '877' => fixed(2), # Reserved - Maritime Mobile Service Applications
321
+ '878' => fixed(2), # Universal Personal Telecommunication Service (UPT)
322
+ '879' => fixed(2), # Reserved for national non-commercial purposes
323
+
324
+ '880' => fixed(2), # Bangladesh (People's Republic of)
325
+ '881' => fixed(2), # International Mobile, shared code
326
+ '882' => fixed(2), # International Networks, shared code
327
+ '883' => fixed(2), # -
328
+ '884' => fixed(2), # -
329
+ '885' => fixed(2), # -
330
+ '886' => fixed(2), # Reserved
331
+ '887' => fixed(2), # -
332
+ '888' => fixed(2), # Reserved for future global service
333
+ '889' => fixed(2), # -
334
+
335
+ '890' => fixed(2), # -
336
+ '891' => fixed(2), # -
337
+ '892' => fixed(2), # -
338
+ '893' => fixed(2), # -
339
+ '894' => fixed(2), # -
340
+ '895' => fixed(2), # -
341
+ '896' => fixed(2), # -
342
+ '897' => fixed(2), # -
343
+ '898' => fixed(2), # -
344
+ '899' => fixed(2), # -
345
+
346
+ '960' => fixed(2), # Maldives (Republic of)
347
+ '961' => fixed(2), # Lebanon
348
+ '962' => fixed(2), # Jordan (Hashemite Kingdom of)
349
+ '963' => fixed(2), # Syrian Arab Republic
350
+ '964' => fixed(2), # Iraq (Republic of)
351
+ '965' => fixed(2), # Kuwait (State of)
352
+ '966' => fixed(2), # Saudi Arabia (Kingdom of)
353
+ '967' => fixed(2), # Yemen (Republic of)
354
+ '968' => fixed(2), # Oman (Sultanate of)
355
+ '969' => fixed(2), # Reserved - reservation currently under investigation
356
+
357
+ '970' => fixed(2), # Reserved
358
+ '971' => fixed(2), # United Arab Emirates
359
+ '972' => fixed(2), # Israel (State of)
360
+ '973' => fixed(2), # Bahrain (Kingdom of)
361
+ '974' => fixed(2), # Qatar (State of)
362
+ '975' => fixed(2), # Bhutan (Kingdom of)
363
+ '976' => fixed(2), # Mongolia
364
+ '977' => fixed(2), # Nepal
365
+ '978' => fixed(2), # -
366
+ '979' => fixed(2), # International Premium Rate Service (IPRS)
367
+
368
+ '990' => fixed(2), # Spare code
369
+ '991' => fixed(2), # Trial of a proposed new international telecommunication public correspondence service, shared code
370
+ '992' => fixed(2), # Tajikistan (Republic of)
371
+ '993' => fixed(2), # Turkmenistan
372
+ '994' => fixed(2), # Azerbaijani Republic
373
+ '995' => fixed(2), # Georgia
374
+ '996' => fixed(2), # Kyrgyz Republic
375
+ '997' => fixed(2), # Spare code
376
+ '998' => fixed(2), # Uzbekistan (Republic of)
377
+ '999' => fixed(2), # Reserved for possible future use within the Telecommunications for Disaster Relief (TDR) concept
378
+ }
379
+ }
380
+
381
+ # Splits the phone number into pieces according to the country codes above.
382
+ #
383
+ def self.split(phone_number)
384
+ splitter_or_number, country_code, ndc, local = split_internal(phone_number) do |splitter, cc, ndc_local|
385
+ [splitter, cc, splitter.split_ndc(ndc_local)].flatten
386
+ end
387
+ return splitter_or_number if local.nil?
388
+
389
+ [country_code, ndc, splitter_or_number.split_local(local)].flatten
390
+ end
391
+
392
+ # Formats a E164 number according to local customs.
393
+ #
394
+ def self.formatted(phone_number, options = {})
395
+ splitter_or_number, cc, ndc, local = split_internal(phone_number) do |splitter, cc, ndc_local|
396
+ [splitter, cc, splitter.split_ndc(ndc_local)].flatten
397
+ end
398
+ return splitter_or_number if local.nil?
399
+
400
+ space = options[:spaces] || ' '
401
+ formatted_cc_ndc(cc, ndc, options[:format], space) + splitter_or_number.locally_formatted(local, space)
402
+ end
403
+
404
+ # Normalizes
405
+ #
406
+ def self.normalize(phone_number)
407
+ phone_number = phone_number.dup
408
+ phone_number.gsub!(/\D*/, '').gsub!(/^0+/, '') # Remove zeros at the beginning and non-digit chars
409
+ remove_relative_zeros! phone_number
410
+ end
411
+
412
+ # Returns true if the number starts with 4 digits and ends with 6-12 characters.
413
+ #
414
+ def self.vanity_number?(number)
415
+ (number.gsub(' ', '') =~ /^\d{4}\w{6,12}$/) ? true : false
416
+ end
417
+
418
+ # Converts any character in the vanity_number to it's numeric representation.
419
+ # Does not check if the passed number is a valid vanity_number, simply does replacement.
420
+ #
421
+ def self.vanity_to_number(vanity_number)
422
+ Vanity.replace(vanity_number)
423
+ end
424
+
425
+ module Vanity
426
+
427
+ @@mapping = [
428
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
429
+ '2223334445556667777888999922233344455566677778889999'
430
+ ]
431
+ def self.mapping
432
+ @@mapping
433
+ end
434
+
435
+ # Replaces vanity characters of passed number with correct digits.
436
+ # Does not check for validity of vanity_number. Simply replaces all characters in the number
437
+ #
438
+ def self.replace number
439
+ number.tr *mapping
440
+ end
441
+
442
+ end
443
+
444
+
445
+ private
446
+
447
+ # Removes 0s from partially normalized numbers such as:
448
+ # 410443643533
449
+ #
450
+ # Example:
451
+ # 410443643533 -> 41443643533
452
+ def self.remove_relative_zeros!(phone_number, format = nil)
453
+ '%s%s' % split_cc(phone_number).collect! { |code| code.gsub(/^0+/, '') }
454
+ end
455
+
456
+ # Formats country code and national destination code.
457
+ #
458
+ def self.formatted_cc_ndc(cc, ndc, format, space = nil)
459
+ space ||= ' '
460
+ format, split_phone_number = case format
461
+ when nil, :international_absolute, :international, :+
462
+ [ndc.blank? ? '+%s%s' : '+%s%s%s%s', [cc, space, ndc, space]]
463
+ when :international_relative
464
+ [ndc.blank? ? '00%s%s' : '00%s%s%s%s', [cc, space, ndc, space]]
465
+ when :national
466
+ [ndc.blank? ? '' : '0%s%s', [ndc, space]]
467
+ when :local
468
+ ['', []]
469
+ end
470
+ format % split_phone_number
471
+ end
472
+
473
+ def self.split_cc(phone_number)
474
+ split_internal(phone_number) do |splitter, cc, ndc_local|
475
+ [cc, ndc_local]
476
+ end
477
+ end
478
+
479
+ def self.split_cc_ndc(phone_number)
480
+ split_internal(phone_number) do |splitter, cc, ndc_local|
481
+ splitter ? [cc, splitter.split_ndc(ndc_local)].flatten : [cc, ndc_local, '']
482
+ end
483
+ end
484
+
485
+ def self.split_internal(phone_number)
486
+ number = phone_number.dup
487
+ presumed_code = ''
488
+ 1.upto(3) do |i|
489
+ presumed_code << number.slice!(0..0)
490
+ splitter = @@country_codes[i][presumed_code]
491
+ return yield(splitter, presumed_code, number) if splitter
492
+ end
493
+ return yield(nil, '', '')
494
+ end
495
+
496
+ end