phony 1.9.0 → 2.19.14
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.
- checksums.yaml +7 -0
- data/README.textile +64 -112
- data/lib/phony/config.rb +91 -0
- data/lib/phony/countries/argentina.rb +355 -0
- data/lib/phony/countries/austria.rb +56 -22
- data/lib/phony/countries/bangladesh.rb +57 -0
- data/lib/phony/countries/belarus.rb +133 -0
- data/lib/phony/countries/brazil.rb +101 -95
- data/lib/phony/countries/cambodia.rb +131 -0
- data/lib/phony/countries/china.rb +13 -6
- data/lib/phony/countries/croatia.rb +23 -0
- data/lib/phony/countries/georgia.rb +94 -0
- data/lib/phony/countries/germany.rb +66 -42
- data/lib/phony/countries/guinea.rb +46 -0
- data/lib/phony/countries/india.rb +50 -0
- data/lib/phony/countries/indonesia.rb +55 -0
- data/lib/phony/countries/ireland.rb +35 -28
- data/lib/phony/countries/italy.rb +272 -166
- data/lib/phony/countries/japan.rb +468 -0
- data/lib/phony/countries/kyrgyzstan.rb +120 -0
- data/lib/phony/countries/latvia.rb +43 -0
- data/lib/phony/countries/libya.rb +116 -0
- data/lib/phony/countries/malaysia.rb +31 -7
- data/lib/phony/countries/moldova.rb +53 -0
- data/lib/phony/countries/montenegro.rb +30 -0
- data/lib/phony/countries/myanmar.rb +55 -0
- data/lib/phony/countries/namibia.rb +37 -0
- data/lib/phony/countries/nepal.rb +73 -0
- data/lib/phony/countries/netherlands.rb +17 -5
- data/lib/phony/countries/pakistan.rb +121 -0
- data/lib/phony/countries/paraguay.rb +147 -0
- data/lib/phony/countries/{russia_kazakhstan_abhasia_south_osetia.rb → russia_kazakhstan_abkhasia_south_ossetia.rb} +35 -24
- data/lib/phony/countries/saudi_arabia.rb +40 -0
- data/lib/phony/countries/serbia.rb +47 -0
- data/lib/phony/countries/somalia.rb +26 -0
- data/lib/phony/countries/south_korea.rb +19 -10
- data/lib/phony/countries/sweden.rb +58 -38
- data/lib/phony/countries/taiwan.rb +28 -0
- data/lib/phony/countries/tajikistan.rb +79 -0
- data/lib/phony/countries/turkmenistan.rb +76 -0
- data/lib/phony/countries/ukraine.rb +630 -0
- data/lib/phony/countries/united_kingdom.rb +639 -44
- data/lib/phony/countries/uruguay.rb +53 -0
- data/lib/phony/countries/vietnam.rb +133 -0
- data/lib/phony/countries/zimbabwe.rb +39 -0
- data/lib/phony/countries.rb +901 -301
- data/lib/phony/country.rb +177 -20
- data/lib/phony/country_codes.rb +119 -101
- data/lib/phony/dsl.rb +113 -68
- data/lib/phony/local_splitters/fixed.rb +25 -1
- data/lib/phony/local_splitters/regex.rb +16 -2
- data/lib/phony/national_code.rb +7 -7
- data/lib/phony/national_splitters/default.rb +35 -3
- data/lib/phony/national_splitters/dsl.rb +12 -7
- data/lib/phony/national_splitters/fixed.rb +7 -1
- data/lib/phony/national_splitters/none.rb +7 -3
- data/lib/phony/national_splitters/regex.rb +6 -0
- data/lib/phony/national_splitters/variable.rb +13 -9
- data/lib/phony/trunk_code.rb +57 -0
- data/lib/phony/vanity.rb +3 -3
- data/lib/phony.rb +239 -55
- data/spec/functional/config_spec.rb +44 -0
- data/spec/functional/plausibility_spec.rb +656 -0
- data/spec/lib/phony/countries_spec.rb +1207 -119
- data/spec/lib/phony/country_codes_spec.rb +99 -81
- data/spec/lib/phony/country_spec.rb +54 -14
- data/spec/lib/phony/dsl_spec.rb +2 -2
- data/spec/lib/phony/local_splitters/fixed_spec.rb +4 -4
- data/spec/lib/phony/local_splitters/regex_spec.rb +50 -2
- data/spec/lib/phony/national_code_spec.rb +34 -34
- data/spec/lib/phony/national_splitters/default_spec.rb +34 -0
- data/spec/lib/phony/national_splitters/fixed_spec.rb +12 -6
- data/spec/lib/phony/national_splitters/none_spec.rb +13 -3
- data/spec/lib/phony/national_splitters/regex_spec.rb +1 -1
- data/spec/lib/phony/national_splitters/variable_spec.rb +11 -5
- data/spec/lib/phony/trunk_code_spec.rb +85 -0
- data/spec/lib/phony/vanity_spec.rb +15 -19
- data/spec/lib/phony_spec.rb +59 -277
- metadata +67 -34
- data/lib/phony/validator.rb +0 -26
- data/lib/phony/validators.rb +0 -88
- data/spec/lib/phony/validations_spec.rb +0 -109
data/lib/phony.rb
CHANGED
@@ -1,83 +1,270 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE We use Kernel.load here, as it's possible to redefine Phony via Phony::Config.
|
4
|
+
|
1
5
|
# Framework.
|
2
6
|
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
load File.expand_path '../phony/config.rb', __FILE__
|
8
|
+
load File.expand_path '../phony/vanity.rb', __FILE__
|
9
|
+
load File.expand_path '../phony/local_splitters/fixed.rb', __FILE__
|
10
|
+
load File.expand_path '../phony/local_splitters/regex.rb', __FILE__
|
11
|
+
load File.expand_path '../phony/national_splitters/dsl.rb', __FILE__
|
12
|
+
load File.expand_path '../phony/national_splitters/fixed.rb', __FILE__
|
13
|
+
load File.expand_path '../phony/national_splitters/variable.rb', __FILE__
|
14
|
+
load File.expand_path '../phony/national_splitters/regex.rb', __FILE__
|
15
|
+
load File.expand_path '../phony/national_splitters/default.rb', __FILE__
|
16
|
+
load File.expand_path '../phony/national_splitters/none.rb', __FILE__
|
17
|
+
load File.expand_path '../phony/national_code.rb', __FILE__
|
18
|
+
load File.expand_path '../phony/country.rb', __FILE__
|
19
|
+
load File.expand_path '../phony/trunk_code.rb', __FILE__
|
20
|
+
load File.expand_path '../phony/country_codes.rb', __FILE__
|
21
|
+
load File.expand_path '../phony/dsl.rb', __FILE__
|
18
22
|
|
19
23
|
# Countries.
|
20
24
|
#
|
21
25
|
# The ones that need more space to define.
|
22
26
|
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
load File.expand_path '../phony/countries/argentina.rb', __FILE__
|
28
|
+
load File.expand_path '../phony/countries/austria.rb', __FILE__
|
29
|
+
load File.expand_path '../phony/countries/bangladesh.rb', __FILE__
|
30
|
+
load File.expand_path '../phony/countries/belarus.rb', __FILE__
|
31
|
+
load File.expand_path '../phony/countries/brazil.rb', __FILE__
|
32
|
+
load File.expand_path '../phony/countries/cambodia.rb', __FILE__
|
33
|
+
load File.expand_path '../phony/countries/croatia.rb', __FILE__
|
34
|
+
load File.expand_path '../phony/countries/china.rb', __FILE__
|
35
|
+
load File.expand_path '../phony/countries/georgia.rb', __FILE__
|
36
|
+
load File.expand_path '../phony/countries/germany.rb', __FILE__
|
37
|
+
load File.expand_path '../phony/countries/guinea.rb', __FILE__
|
38
|
+
load File.expand_path '../phony/countries/india.rb', __FILE__
|
39
|
+
load File.expand_path '../phony/countries/indonesia.rb', __FILE__
|
40
|
+
load File.expand_path '../phony/countries/ireland.rb', __FILE__
|
41
|
+
load File.expand_path '../phony/countries/italy.rb', __FILE__
|
42
|
+
load File.expand_path '../phony/countries/japan.rb', __FILE__
|
43
|
+
load File.expand_path '../phony/countries/kyrgyzstan.rb', __FILE__
|
44
|
+
load File.expand_path '../phony/countries/latvia.rb', __FILE__
|
45
|
+
load File.expand_path '../phony/countries/libya.rb', __FILE__
|
46
|
+
load File.expand_path '../phony/countries/malaysia.rb', __FILE__
|
47
|
+
load File.expand_path '../phony/countries/moldova.rb', __FILE__
|
48
|
+
load File.expand_path '../phony/countries/montenegro.rb', __FILE__
|
49
|
+
load File.expand_path '../phony/countries/myanmar.rb', __FILE__
|
50
|
+
load File.expand_path '../phony/countries/namibia.rb', __FILE__
|
51
|
+
load File.expand_path '../phony/countries/nepal.rb', __FILE__
|
52
|
+
load File.expand_path '../phony/countries/netherlands.rb', __FILE__
|
53
|
+
load File.expand_path '../phony/countries/pakistan.rb', __FILE__
|
54
|
+
load File.expand_path '../phony/countries/paraguay.rb', __FILE__
|
55
|
+
load File.expand_path '../phony/countries/russia_kazakhstan_abkhasia_south_ossetia.rb', __FILE__
|
56
|
+
load File.expand_path '../phony/countries/saudi_arabia.rb', __FILE__
|
57
|
+
load File.expand_path '../phony/countries/serbia.rb', __FILE__
|
58
|
+
load File.expand_path '../phony/countries/somalia.rb', __FILE__
|
59
|
+
load File.expand_path '../phony/countries/south_korea.rb', __FILE__
|
60
|
+
load File.expand_path '../phony/countries/sweden.rb', __FILE__
|
61
|
+
load File.expand_path '../phony/countries/taiwan.rb', __FILE__
|
62
|
+
load File.expand_path '../phony/countries/tajikistan.rb', __FILE__
|
63
|
+
load File.expand_path '../phony/countries/turkmenistan.rb', __FILE__
|
64
|
+
load File.expand_path '../phony/countries/vietnam.rb', __FILE__
|
65
|
+
load File.expand_path '../phony/countries/ukraine.rb', __FILE__
|
66
|
+
load File.expand_path '../phony/countries/united_kingdom.rb', __FILE__
|
67
|
+
load File.expand_path '../phony/countries/uruguay.rb', __FILE__
|
68
|
+
load File.expand_path '../phony/countries/zimbabwe.rb', __FILE__
|
69
|
+
|
36
70
|
# All other countries.
|
37
71
|
#
|
38
|
-
|
72
|
+
load File.expand_path '../phony/countries.rb', __FILE__
|
39
73
|
|
74
|
+
# Phony is the main module and is generally used to process
|
75
|
+
# E164 phone numbers directly.
|
76
|
+
#
|
40
77
|
module Phony
|
41
78
|
|
79
|
+
# Raised in case Phony can't normalize a given number.
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# Phony.normalize("Fnork!") # Raises a Phony::NormalizationError.
|
83
|
+
#
|
84
|
+
class NormalizationError < ArgumentError
|
85
|
+
def initialize
|
86
|
+
super %Q{Phony could not normalize the given number. Is it a phone number?}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Raised in case Phony can't split a given number.
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# Phony.split("Fnork!") # Raises a Phony::SplittingError.
|
94
|
+
#
|
95
|
+
class SplittingError < ArgumentError
|
96
|
+
def initialize number
|
97
|
+
super %Q{Phony could not split the given number. Is #{(number.nil? || number == '') ? 'it' : number.inspect} a phone number?}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Raised in case Phony can't format a given number.
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# Phony.format("Fnork!") # Raises a Phony::FormattingError.
|
105
|
+
#
|
106
|
+
class FormattingError < ArgumentError
|
107
|
+
def initialize
|
108
|
+
super %Q{Phony could not format the given number. Is it a phone number?}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
42
112
|
# Phony uses a single country codes instance.
|
43
113
|
#
|
44
|
-
@codes
|
45
|
-
@validators = Validators.instance
|
114
|
+
@codes = CountryCodes.instance
|
46
115
|
|
47
116
|
class << self
|
48
117
|
|
49
|
-
#
|
118
|
+
# Get the Country for the given CC.
|
119
|
+
#
|
120
|
+
# @param [String] cc A valid country code.
|
121
|
+
#
|
122
|
+
# @return [Country] for the given CC.
|
123
|
+
#
|
124
|
+
# @example Country for the NANP (includes the US)
|
125
|
+
# nanp = Phony['1']
|
126
|
+
# normalized_number = nanp.normalize number
|
127
|
+
#
|
128
|
+
def [] cc
|
129
|
+
@codes[cc]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Normalizes the given number into a digits-only String.
|
50
133
|
#
|
51
134
|
# Useful before inserting the number into a database.
|
52
135
|
#
|
53
|
-
|
136
|
+
# @param [String] phone_number An E164 number.
|
137
|
+
# @param [Hash] options An options hash (With :cc as the only used key).
|
138
|
+
#
|
139
|
+
# @return [String] A normalized E164 number.
|
140
|
+
#
|
141
|
+
# @raise [Phony::NormalizationError] If phony can't normalize the given number.
|
142
|
+
#
|
143
|
+
# @example Normalize a Swiss number.
|
144
|
+
# Phony.normalize("+41 (044) 123 45 67") # => "41441234567"
|
145
|
+
#
|
146
|
+
# @example Normalize a phone number assuming it's a NANP number.
|
147
|
+
# Phony.normalize("301 555 0100", cc: '1') # => "13015550100"
|
148
|
+
#
|
149
|
+
def normalize phone_number, options = {}
|
54
150
|
raise ArgumentError, "Phone number cannot be nil. Use e.g. number && Phony.normalize(number)." unless phone_number
|
55
|
-
|
151
|
+
|
152
|
+
normalize! phone_number.dup, options
|
56
153
|
end
|
57
|
-
|
58
|
-
|
154
|
+
# A destructive version of {#normalize}.
|
155
|
+
#
|
156
|
+
# @see #normalize
|
157
|
+
#
|
158
|
+
# @param [String] phone_number An E164 number.
|
159
|
+
# @param [Hash] options An options hash (With :cc as the only used key).
|
160
|
+
#
|
161
|
+
# @return [String] The normalized E164 number.
|
162
|
+
#
|
163
|
+
# @raise [Phony::NormalizationError] If phony can't normalize the given number.
|
164
|
+
#
|
165
|
+
# @example Normalize a Swiss number.
|
166
|
+
# Phony.normalize!("+41 (044) 123 45 67") # => "41441234567"
|
167
|
+
#
|
168
|
+
# @example Normalize a phone number assuming it's a NANP number.
|
169
|
+
# Phony.normalize!("301 555 0100", cc: '1') # => "13015550100"
|
170
|
+
#
|
171
|
+
def normalize! phone_number, options = {}
|
172
|
+
@codes.normalize phone_number, options
|
173
|
+
rescue
|
174
|
+
raise NormalizationError.new
|
59
175
|
end
|
60
176
|
|
61
177
|
# Splits the phone number into pieces according to the country codes.
|
62
178
|
#
|
179
|
+
# Useful for manually processing the CC, NDC, and local pieces.
|
180
|
+
#
|
181
|
+
# @param [String] phone_number An E164 number.
|
182
|
+
#
|
183
|
+
# @return [Array<String>] The pieces of a phone number.
|
184
|
+
#
|
185
|
+
# @example Split a Swiss number.
|
186
|
+
# Phony.split("41441234567") # => ["41", "44", "123", "45", "67"]
|
187
|
+
#
|
188
|
+
# @example Split a NANP number.
|
189
|
+
# Phony.split("13015550100") # => ["1", "301", "555", "0100"]
|
190
|
+
#
|
63
191
|
def split phone_number
|
64
192
|
raise ArgumentError, "Phone number cannot be nil. Use e.g. number && Phony.split(number)." unless phone_number
|
65
|
-
|
193
|
+
|
194
|
+
split! phone_number.dup, phone_number
|
66
195
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
196
|
+
# A destructive version of {#split}.
|
197
|
+
#
|
198
|
+
# @see #split
|
199
|
+
#
|
200
|
+
# @param [String] phone_number An E164 number.
|
201
|
+
#
|
202
|
+
# @return [Array<String>] The pieces of the phone number.
|
203
|
+
#
|
204
|
+
# @example Split a Swiss number.
|
205
|
+
# Phony.split!("41441234567") # => ["41", "44", "123", "45", "67"]
|
206
|
+
#
|
207
|
+
# @example Split a NANP number.
|
208
|
+
# Phony.split!("13015550100") # => ["1", "301", "555", "0100"]
|
209
|
+
#
|
210
|
+
def split! phone_number, error_number = nil
|
211
|
+
@codes.split phone_number
|
212
|
+
rescue
|
213
|
+
# NB The error_number (reference) is used because phone_number is destructively handled.
|
214
|
+
raise SplittingError.new(error_number)
|
71
215
|
end
|
72
216
|
|
73
|
-
# Formats a E164 number according to
|
217
|
+
# Formats a normalized E164 number according to a country's formatting scheme.
|
218
|
+
#
|
219
|
+
# Absolutely needs a normalized E164 number.
|
220
|
+
#
|
221
|
+
# @param [String] phone_number A normalized E164 number.
|
222
|
+
# @param [Hash] options See the README for a list of options.
|
223
|
+
#
|
224
|
+
# @return [Array<String>] The pieces of a phone number.
|
225
|
+
#
|
226
|
+
# @example Format a Swiss number.
|
227
|
+
# Phony.format("41441234567") # => "+41 44 123 45 67"
|
228
|
+
#
|
229
|
+
# @example Format a NANP number.
|
230
|
+
# Phony.format("13015550100") # => "+1 301 555 0100"
|
231
|
+
#
|
232
|
+
# @example Format a NANP number in local format.
|
233
|
+
# Phony.format("13015550100", :format => :local) # => "555 0100"
|
234
|
+
#
|
235
|
+
# @example Format a NANP number in a specific format.
|
236
|
+
# Phony.format("13015550100", :format => '%{cc} (%{trunk}%{ndc}) %{local}') # => "555 0100"
|
74
237
|
#
|
75
238
|
def format phone_number, options = {}
|
76
239
|
raise ArgumentError, "Phone number cannot be nil. Use e.g. number && Phony.format(number)." unless phone_number
|
77
|
-
|
240
|
+
format! phone_number.dup, options
|
78
241
|
end
|
242
|
+
# A destructive version of {#format}.
|
243
|
+
#
|
244
|
+
# @see #format
|
245
|
+
#
|
246
|
+
# Formats a normalized E164 number according to a country's formatting scheme.
|
247
|
+
#
|
248
|
+
# Absolutely needs a normalized E164 number.
|
249
|
+
#
|
250
|
+
# @param [String] phone_number A normalized E164 number.
|
251
|
+
# @param [Hash] options See the README for a list of options.
|
252
|
+
#
|
253
|
+
# @return [Array<String>] The pieces of the phone number.
|
254
|
+
#
|
255
|
+
# @example Format a Swiss number.
|
256
|
+
# Phony.format!("41441234567") # => "+41 44 123 45 67"
|
257
|
+
#
|
258
|
+
# @example Format a NANP number.
|
259
|
+
# Phony.format!("13015550100") # => "+1 301 555 0100"
|
260
|
+
#
|
261
|
+
# @example Format a NANP number in local format.
|
262
|
+
# Phony.format!("13015550100", :format => :local) # => "555 0100"
|
263
|
+
#
|
79
264
|
def format! phone_number, options = {}
|
80
|
-
@codes.
|
265
|
+
@codes.format phone_number, options
|
266
|
+
rescue
|
267
|
+
raise FormattingError.new
|
81
268
|
end
|
82
269
|
alias formatted format
|
83
270
|
alias formatted! format!
|
@@ -89,19 +276,9 @@ module Phony
|
|
89
276
|
# leaning towards being plausible.
|
90
277
|
#
|
91
278
|
def plausible? number, hints = {}
|
92
|
-
@
|
279
|
+
@codes.plausible? number, hints
|
93
280
|
end
|
94
281
|
|
95
|
-
# def service? number
|
96
|
-
# @codes.service? number.dup
|
97
|
-
# end
|
98
|
-
# def mobile? number
|
99
|
-
# @codes.mobile? number.dup
|
100
|
-
# end
|
101
|
-
# def landline? number
|
102
|
-
# @codes.landline? number.dup
|
103
|
-
# end
|
104
|
-
|
105
282
|
# Returns true if there is a character in the number
|
106
283
|
# after the first four numbers.
|
107
284
|
#
|
@@ -112,6 +289,13 @@ module Phony
|
|
112
289
|
# Converts any character in the vanity_number to its numeric representation.
|
113
290
|
# Does not check if the passed number is a valid vanity_number, simply does replacement.
|
114
291
|
#
|
292
|
+
# @param [String] vanity_number A vanity number.
|
293
|
+
#
|
294
|
+
# @return [String] The de-vanitized phone number.
|
295
|
+
#
|
296
|
+
# @example De-vanitize a number.
|
297
|
+
# Phony.vanity_to_number("1-800-HELLOTHERE") # => "1-800-4355684373"
|
298
|
+
#
|
115
299
|
def vanity_to_number vanity_number
|
116
300
|
@codes.vanity_to_number vanity_number.dup
|
117
301
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
describe 'Phony::Config' do
|
4
|
+
describe 'load' do
|
5
|
+
before do
|
6
|
+
# NOTE We redefine Phony as if it was not loaded for this set of tests.
|
7
|
+
Object.__send__(:remove_const, :Phony) if Object.constants.include?(:Phony)
|
8
|
+
|
9
|
+
load 'phony/config.rb'
|
10
|
+
end
|
11
|
+
after(:all) do
|
12
|
+
# After running this suite, we load all of Phony for the following tests.
|
13
|
+
load 'phony/config.rb'
|
14
|
+
|
15
|
+
Phony::Config.load
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'does not fail when loading all' do
|
19
|
+
Phony::Config.load
|
20
|
+
|
21
|
+
Phony.split('15551115511').should == ['1', '555', '111', '5511']
|
22
|
+
end
|
23
|
+
it 'raises when a CC is used that has not been loaded.' do
|
24
|
+
Phony::Config.load('41')
|
25
|
+
|
26
|
+
expect { Phony.split('15551115511') }.to raise_error
|
27
|
+
end
|
28
|
+
it 'raises when a CC is used that has not been loaded.' do
|
29
|
+
Phony::Config.load(only: ['41'])
|
30
|
+
|
31
|
+
expect { Phony.split('15551115511') }.to raise_error
|
32
|
+
end
|
33
|
+
it 'raises when a CC is used that has not been loaded.' do
|
34
|
+
Phony::Config.load(except: ['1'])
|
35
|
+
|
36
|
+
expect { Phony.split('15551115511') }.to raise_error
|
37
|
+
end
|
38
|
+
it 'does not raise when a CC is used that has been loaded.' do
|
39
|
+
Phony::Config.load(except: ['41'])
|
40
|
+
|
41
|
+
Phony.split('15551115511').should == ['1', '555', '111', '5511']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|