phony 1.9.0 → 2.0.0.beta1
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.
- data/README.textile +1 -1
- data/lib/phony.rb +28 -15
- data/lib/phony/countries.rb +609 -210
- data/lib/phony/countries/austria.rb +6 -1
- data/lib/phony/countries/bangladesh.rb +55 -0
- data/lib/phony/countries/belarus.rb +130 -0
- data/lib/phony/countries/georgia.rb +91 -0
- data/lib/phony/countries/germany.rb +20 -6
- data/lib/phony/countries/india.rb +50 -0
- data/lib/phony/countries/indonesia.rb +50 -0
- data/lib/phony/countries/italy.rb +38 -67
- data/lib/phony/countries/japan.rb +412 -0
- data/lib/phony/countries/kyrgyzstan.rb +118 -0
- data/lib/phony/countries/latvia.rb +40 -0
- data/lib/phony/countries/libya.rb +114 -0
- data/lib/phony/countries/malaysia.rb +7 -5
- data/lib/phony/countries/moldova.rb +50 -0
- data/lib/phony/countries/montenegro.rb +27 -0
- data/lib/phony/countries/namibia.rb +35 -0
- data/lib/phony/countries/nepal.rb +71 -0
- data/lib/phony/countries/netherlands.rb +3 -2
- data/lib/phony/countries/pakistan.rb +119 -0
- data/lib/phony/countries/paraguay.rb +145 -0
- data/lib/phony/countries/russia_kazakhstan_abhasia_south_osetia.rb +6 -6
- data/lib/phony/countries/serbia.rb +34 -0
- data/lib/phony/countries/somali.rb +22 -0
- data/lib/phony/countries/south_korea.rb +1 -1
- data/lib/phony/countries/sweden.rb +1 -1
- data/lib/phony/countries/taiwan.rb +51 -0
- data/lib/phony/countries/tajikistan.rb +76 -0
- data/lib/phony/countries/turkmenistan.rb +73 -0
- data/lib/phony/countries/ukraine.rb +614 -0
- data/lib/phony/countries/uruguay.rb +51 -0
- data/lib/phony/countries/zimbabwe.rb +37 -0
- data/lib/phony/country.rb +41 -0
- data/lib/phony/country_codes.rb +45 -18
- data/lib/phony/dsl.rb +33 -7
- data/lib/phony/local_splitters/fixed.rb +14 -1
- data/lib/phony/local_splitters/regex.rb +12 -1
- data/lib/phony/national_code.rb +7 -3
- data/lib/phony/national_splitters/default.rb +13 -1
- data/lib/phony/national_splitters/dsl.rb +9 -7
- data/lib/phony/national_splitters/fixed.rb +6 -0
- data/lib/phony/national_splitters/none.rb +6 -0
- data/lib/phony/national_splitters/regex.rb +6 -0
- data/lib/phony/national_splitters/variable.rb +7 -1
- data/spec/lib/phony/countries_spec.rb +684 -16
- data/spec/lib/phony/local_splitters/regex_spec.rb +41 -0
- data/spec/lib/phony/validations_spec.rb +542 -10
- data/spec/lib/phony_spec.rb +20 -6
- metadata +29 -9
- data/lib/phony/validator.rb +0 -26
- data/lib/phony/validators.rb +0 -88
@@ -0,0 +1,51 @@
|
|
1
|
+
# Uruguay (Eastern Republic of)
|
2
|
+
# https://www.numberingplans.com/?page=dialling&sub=areacodes
|
3
|
+
# https://www.numberingplans.com/?page=plans&sub=phonenr&alpha_2_input=UY
|
4
|
+
|
5
|
+
ndcs_with_7_subscriber_numbers = %w(2 4)
|
6
|
+
|
7
|
+
ndcs_with_6_subscriber_numbers = %w(
|
8
|
+
21
|
9
|
+
22
|
10
|
+
23
|
11
|
+
24
|
12
|
+
25
|
13
|
+
26
|
14
|
+
27
|
15
|
+
29
|
16
|
+
42
|
17
|
+
)
|
18
|
+
|
19
|
+
ndcs_with_5_subscriber_numbers = %w(
|
20
|
+
433
|
21
|
+
434
|
22
|
+
436
|
23
|
+
437
|
24
|
+
438
|
25
|
+
443
|
26
|
+
444
|
27
|
+
445
|
28
|
+
447
|
29
|
+
452
|
30
|
+
453
|
31
|
+
455
|
32
|
+
462
|
33
|
+
463
|
34
|
+
464
|
35
|
+
472
|
36
|
+
473
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
Phony.define do
|
41
|
+
country '598',
|
42
|
+
one_of('800') >> split(3,2) | # freephone
|
43
|
+
one_of('805') >> split(3,2) | # shared cost
|
44
|
+
one_of(%w(900 901 902 903 904 905 906 907 908)) >> split(3,2) | # premium rate
|
45
|
+
one_of('909') >> split(3,2) | # ISP
|
46
|
+
one_of(ndcs_with_5_subscriber_numbers) >> split(3,2) |
|
47
|
+
one_of(ndcs_with_6_subscriber_numbers) >> split(3,3) |
|
48
|
+
one_of(%w(93 94 95 96 97 98 99)) >> split(3,3) | # mobile
|
49
|
+
one_of(ndcs_with_7_subscriber_numbers) >> split(3,4) |
|
50
|
+
fixed(2) >> split(3,3)
|
51
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Zimbabwe, https://www.numberingplans.com/?page=plans&sub=phonenr&alpha_2_input=ZW
|
2
|
+
|
3
|
+
Phony.define do
|
4
|
+
country '263',
|
5
|
+
one_of(%w(2582 2583 5483)) >>
|
6
|
+
matched_split(/\A\d{6}\z/ => [3,3],
|
7
|
+
/\A\d+\z/ => [3] ) | # geographic [XXXX] XXX(XXX)
|
8
|
+
one_of(%w(147 204 205 222 227 228 238 248 2582 2583 271 272 274 276 281 283
|
9
|
+
284 286 287 288 289 298 308 317 371 375 376 379 383 387 389 517 518
|
10
|
+
557 558 628 637 667 668 687 688 698 848)) >>
|
11
|
+
matched_split(/\A\d{6}\z/ => [3,3],
|
12
|
+
/\A\d+\z/ => [3] ) | # geographic [XXX] XXX(XXX)
|
13
|
+
one_of(%w(270)) >>
|
14
|
+
matched_split(/\A\d{6}\z/ => [3,3],
|
15
|
+
/\A\d+\z/ => [3,2] ) | # geographic [XXX] XXXXX(X)
|
16
|
+
one_of(%w(273 275 277 278 279 282 285)) >>
|
17
|
+
matched_split(/\A\d{6}\z/ => [3,3],
|
18
|
+
/\A\d+\z/ => [2,2] ) | # geographic [XXX] XXXX(XX)
|
19
|
+
one_of(%w(912)) >>
|
20
|
+
matched_split(/\A\d{8}\z/ => [4,4], /\A\d+\z/ => [3]) | # mobile
|
21
|
+
one_of(%w(13 14 15 16 17 18 19 21 24 26 29 30 31 32 34 35 36 50 55 57 58 59 60
|
22
|
+
63 64 65 66 69)) >>
|
23
|
+
matched_split(/\A\d{6}\z/ => [3,3],
|
24
|
+
/\A\d+\z/ => [3] ) | # geographic [XX] XXX(XXX)
|
25
|
+
one_of(%w(20)) >>
|
26
|
+
matched_split(/\A\d{6}\z/ => [3,3],
|
27
|
+
/\A\d+\z/ => [3,2] ) | # geographic [XXX] XXXXX(X)
|
28
|
+
one_of(%w(25 33 39 51 52 53 54 56 61 62 67 68 )) >>
|
29
|
+
matched_split(/\A\d{6}\z/ => [3,3],
|
30
|
+
/\A\d+\z/ => [2,2] ) | # geographic [XXX] XXXX(XX)
|
31
|
+
one_of(%w(86)) >> split(4,4) | # VoIP telephony
|
32
|
+
one_of(%w(71 73 77)) >> split(4,3) | # mobile
|
33
|
+
one_of(%w(4 9)) >>
|
34
|
+
matched_split(/\A\d{6}\z/ => [4,4],
|
35
|
+
/\A\d+\z/ => [3,2] ) | # geographic [X] XXXXX(XXX)
|
36
|
+
fixed(2) >> split(3,3)
|
37
|
+
end
|
data/lib/phony/country.rb
CHANGED
@@ -19,6 +19,14 @@ module Phony
|
|
19
19
|
self
|
20
20
|
end
|
21
21
|
|
22
|
+
# Options.
|
23
|
+
#
|
24
|
+
# TODO Rewrite.
|
25
|
+
#
|
26
|
+
def with options = {}
|
27
|
+
@invalid_ndcs = options[:invalid_ndcs] || []
|
28
|
+
end
|
29
|
+
|
22
30
|
# A number is split with the code handlers as given in the initializer.
|
23
31
|
#
|
24
32
|
# Note: If the ndc is nil, it will not return it.
|
@@ -29,6 +37,12 @@ module Phony
|
|
29
37
|
return [zero, ndc, *rest] if rest && !rest.empty?
|
30
38
|
end
|
31
39
|
end
|
40
|
+
def split_ndc national_number
|
41
|
+
@codes.each do |code|
|
42
|
+
zero, ndc, *rest = code.split national_number
|
43
|
+
return [code.local_splitter, zero, ndc, *rest] if rest && !rest.empty?
|
44
|
+
end
|
45
|
+
end
|
32
46
|
|
33
47
|
# Removes 0s from partially normalized numbers
|
34
48
|
# such as 410443643533.
|
@@ -46,6 +60,33 @@ module Phony
|
|
46
60
|
return nil
|
47
61
|
end
|
48
62
|
|
63
|
+
# Tests for plausibility of this national number.
|
64
|
+
#
|
65
|
+
def plausible? rest, hints = {}
|
66
|
+
local, _, ndc, *rest = split_ndc rest
|
67
|
+
|
68
|
+
# Element based checking.
|
69
|
+
#
|
70
|
+
# Note: ndc == false means the country has none.
|
71
|
+
#
|
72
|
+
return false if ndc.nil?
|
73
|
+
return false if ndc && ndc.empty?
|
74
|
+
return false if @invalid_ndcs.include? ndc # TODO Refactor.
|
75
|
+
|
76
|
+
# # A valid range for the rest is 0 or 3+ total digits.
|
77
|
+
# #
|
78
|
+
# return false if (1..2) === rest_size
|
79
|
+
|
80
|
+
# National destination code plausible?
|
81
|
+
#
|
82
|
+
ndc_needed = hints[:ndc]
|
83
|
+
return false if ndc_needed && !(ndc_needed === ndc)
|
84
|
+
|
85
|
+
# Local code specific checks.
|
86
|
+
#
|
87
|
+
return local.plausible? rest, hints
|
88
|
+
end
|
89
|
+
|
49
90
|
# Is this national number a vanity number?
|
50
91
|
#
|
51
92
|
def vanity? national_number
|
data/lib/phony/country_codes.rb
CHANGED
@@ -22,7 +22,13 @@ module Phony
|
|
22
22
|
@instance ||= new
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
# 00 for the standard international call prefix.
|
26
|
+
# http://en.wikipedia.org/wiki/List_of_international_call_prefixes
|
27
|
+
#
|
28
|
+
# We can't know from what country that person was calling, so we
|
29
|
+
# can't remove the intl' call prefix.
|
30
|
+
#
|
31
|
+
@@basic_normalizing_pattern = /^00?|\D/
|
26
32
|
def clean number
|
27
33
|
clean! number && number.dup
|
28
34
|
end
|
@@ -33,15 +39,15 @@ module Phony
|
|
33
39
|
end
|
34
40
|
def normalize number
|
35
41
|
clean! number
|
36
|
-
|
37
|
-
@normalize_format % [cc,
|
42
|
+
country, cc, rest = split_cc number
|
43
|
+
@normalize_format % [cc, country.normalize(rest)]
|
38
44
|
end
|
39
45
|
|
40
46
|
# Splits this number into cc, ndc and locally split number parts.
|
41
47
|
#
|
42
48
|
def split number
|
43
|
-
|
44
|
-
[cc, *
|
49
|
+
country, cc, rest = split_cc number
|
50
|
+
[cc, *country.split(rest)]
|
45
51
|
end
|
46
52
|
|
47
53
|
def formatted number, options = {}
|
@@ -88,40 +94,61 @@ module Phony
|
|
88
94
|
#
|
89
95
|
#
|
90
96
|
def service? number
|
91
|
-
|
92
|
-
|
97
|
+
country, cc, rest = split_cc number
|
98
|
+
country.service? rest
|
93
99
|
end
|
94
100
|
def mobile? number
|
95
|
-
|
96
|
-
|
101
|
+
country, cc, rest = split_cc number
|
102
|
+
country.mobile? rest
|
97
103
|
end
|
98
104
|
def landline? number
|
99
|
-
|
100
|
-
|
105
|
+
country, cc, rest = split_cc number
|
106
|
+
country.landline? rest
|
101
107
|
end
|
102
108
|
|
103
109
|
# Is the given number a vanity number?
|
104
110
|
#
|
105
111
|
def vanity? number
|
106
|
-
|
107
|
-
|
112
|
+
country, _, rest = split_cc number
|
113
|
+
country.vanity? rest
|
108
114
|
end
|
109
115
|
# Converts a vanity number into a normalized E164 number.
|
110
116
|
#
|
111
117
|
def vanity_to_number vanity_number
|
112
|
-
|
113
|
-
"#{cc}#{
|
118
|
+
country, cc, rest = split_cc vanity_number
|
119
|
+
"#{cc}#{country.vanity_to_number(rest)}"
|
114
120
|
end
|
115
|
-
|
121
|
+
|
116
122
|
def split_cc rest
|
117
123
|
presumed_cc = ''
|
118
124
|
1.upto(3) do |i|
|
119
125
|
presumed_cc << rest.slice!(0..0)
|
120
|
-
|
121
|
-
return [
|
126
|
+
country = splitter_mapping[i][presumed_cc]
|
127
|
+
return [country, presumed_cc, rest] if country
|
122
128
|
end
|
123
129
|
# This line is never reached as CCs are in prefix code.
|
124
130
|
end
|
131
|
+
|
132
|
+
def plausible? number, hints = {}
|
133
|
+
normalized = clean number
|
134
|
+
|
135
|
+
# False if it fails the basic check.
|
136
|
+
#
|
137
|
+
return false unless (4..15) === normalized.size
|
138
|
+
|
139
|
+
country, cc, rest = split_cc normalized
|
140
|
+
|
141
|
+
# Country code plausible?
|
142
|
+
#
|
143
|
+
cc_needed = hints[:cc]
|
144
|
+
return false if cc_needed && !(cc_needed === cc)
|
145
|
+
|
146
|
+
# Country specific tests.
|
147
|
+
#
|
148
|
+
country.plausible? rest, hints
|
149
|
+
rescue StandardError => e
|
150
|
+
return false
|
151
|
+
end
|
125
152
|
|
126
153
|
# # TODO
|
127
154
|
# #
|
data/lib/phony/dsl.rb
CHANGED
@@ -49,9 +49,30 @@ module Phony
|
|
49
49
|
# Example:
|
50
50
|
# country '27', # CC, followed by rules, for example fixed(2) >> ...
|
51
51
|
#
|
52
|
-
def country country_code,
|
53
|
-
|
54
|
-
Phony::
|
52
|
+
def country country_code, definition, options = {}
|
53
|
+
definition.with options
|
54
|
+
Phony::CountryCodes.instance.add country_code, definition
|
55
|
+
end
|
56
|
+
|
57
|
+
class Trunk
|
58
|
+
|
59
|
+
def initialize code, options = {}
|
60
|
+
@code = code
|
61
|
+
@normalize = options[:normalize]
|
62
|
+
end
|
63
|
+
|
64
|
+
def national_splitter= national_splitter
|
65
|
+
@national_splitter = national_splitter
|
66
|
+
end
|
67
|
+
|
68
|
+
def >> local_splitter
|
69
|
+
@national_splitter.country_for local_splitter, @normalize, @code
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
def trunk code, options = {}
|
75
|
+
Trunk.new code, options
|
55
76
|
end
|
56
77
|
|
57
78
|
# National matcher & splitters.
|
@@ -68,6 +89,7 @@ module Phony
|
|
68
89
|
# Example:
|
69
90
|
# country '33', fixed(1) >> split(2,2,2,2) # France, uses a fixed NDC of size 1.
|
70
91
|
#
|
92
|
+
|
71
93
|
def fixed length, options = {}
|
72
94
|
options[:zero] = true if options[:zero].nil?
|
73
95
|
NationalSplitters::Fixed.instance_for length, options
|
@@ -122,7 +144,6 @@ module Phony
|
|
122
144
|
#
|
123
145
|
raise "Regexp /#{regex.source}/ needs a group in it that defines which digits belong to the NDC." unless regex.source =~ /\(/
|
124
146
|
|
125
|
-
on_fail_take = options.delete :on_fail_take
|
126
147
|
NationalSplitters::Regex.instance_for regex, options[:on_fail_take], options
|
127
148
|
end
|
128
149
|
|
@@ -158,6 +179,10 @@ module Phony
|
|
158
179
|
# :fallback => [2,2,2,2])
|
159
180
|
#
|
160
181
|
def matched_split options = {}
|
182
|
+
# TODO: Refactor: it's a workaround. It creates high coupling with Phony::LocalSplitters::Regex.
|
183
|
+
options.each do |_, format|
|
184
|
+
format << format.pop + 10
|
185
|
+
end
|
161
186
|
Phony::LocalSplitters::Regex.instance_for options
|
162
187
|
end
|
163
188
|
|
@@ -173,10 +198,11 @@ module Phony
|
|
173
198
|
# fixed(3, :zero => false) >> split(3,4),
|
174
199
|
# invalid_ndcs('911') # The regexp /911/ would also work.
|
175
200
|
#
|
176
|
-
def invalid_ndcs ndc
|
177
|
-
|
201
|
+
def invalid_ndcs *ndc
|
202
|
+
Validators::NDC.new invalid: ndc
|
178
203
|
end
|
179
204
|
|
205
|
+
|
180
206
|
end
|
181
207
|
|
182
|
-
end
|
208
|
+
end
|
@@ -27,7 +27,8 @@ module Phony
|
|
27
27
|
# The format [3, 2, 2] splits a number like '3332222' into ['333', '22', '22'].
|
28
28
|
#
|
29
29
|
def initialize format = nil
|
30
|
-
@format = format || [3, 2, 2]
|
30
|
+
@format = format && format.dup || [3, 2, 2]
|
31
|
+
# @format << @format.pop + 10 # Allow for call-through numbers with an arbitrary size.
|
31
32
|
end
|
32
33
|
|
33
34
|
# Split a local number according to an assumed country specific format.
|
@@ -43,6 +44,18 @@ module Phony
|
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
47
|
+
# TODO Fix length fudging.
|
48
|
+
#
|
49
|
+
def plausible? rest, hints = {}
|
50
|
+
(length > 10 ? length - 10 : length) === rest.inject(0) { |total, part| total + part.size }
|
51
|
+
end
|
52
|
+
|
53
|
+
# A valid length.
|
54
|
+
#
|
55
|
+
def length
|
56
|
+
@length ||= @format.inject { |total, part| total + part }
|
57
|
+
end
|
58
|
+
|
46
59
|
end
|
47
60
|
|
48
61
|
end
|
@@ -42,6 +42,17 @@ module Phony
|
|
42
42
|
split_with number, fallback
|
43
43
|
end
|
44
44
|
|
45
|
+
def plausible? rest, hints = {}
|
46
|
+
number = rest.inject('', :+)
|
47
|
+
mapping.each do |regex, format|
|
48
|
+
next unless number =~ regex
|
49
|
+
length = format.inject(0, :+)
|
50
|
+
length -= 10 if length > 10
|
51
|
+
return number.length == length
|
52
|
+
end
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
45
56
|
private
|
46
57
|
|
47
58
|
def split_with number, format
|
@@ -51,7 +62,7 @@ module Phony
|
|
51
62
|
result
|
52
63
|
end
|
53
64
|
end
|
54
|
-
|
65
|
+
|
55
66
|
end
|
56
67
|
|
57
68
|
end
|
data/lib/phony/national_code.rb
CHANGED
@@ -3,13 +3,17 @@ module Phony
|
|
3
3
|
# NationalCodes have a special numbers splitter, a national code splitter and a local code splitter.
|
4
4
|
#
|
5
5
|
class NationalCode
|
6
|
-
|
6
|
+
|
7
|
+
attr_reader :local_splitter
|
8
|
+
|
7
9
|
#
|
8
10
|
#
|
9
|
-
def initialize national_splitter, local_splitter, normalize = nil
|
11
|
+
def initialize national_splitter, local_splitter, normalize = nil, trunk_code = '0'
|
10
12
|
@national_splitter = national_splitter
|
11
13
|
@local_splitter = local_splitter
|
12
14
|
@normalize = normalize != false
|
15
|
+
@trunk_code = trunk_code
|
16
|
+
@trunk_code_replacement = /\A#{@trunk_code}+/
|
13
17
|
end
|
14
18
|
|
15
19
|
# Split gets a number without country code and splits it into
|
@@ -27,7 +31,7 @@ module Phony
|
|
27
31
|
#
|
28
32
|
def normalize national_number
|
29
33
|
return national_number unless @normalize
|
30
|
-
national_number.gsub(
|
34
|
+
national_number.gsub(@trunk_code_replacement, EMPTY_STRING)
|
31
35
|
end
|
32
36
|
|
33
37
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module Phony
|
2
2
|
|
3
3
|
module NationalSplitters
|
4
|
-
|
4
|
+
|
5
|
+
# TODO Default = Fixed.new(...)?
|
6
|
+
#
|
5
7
|
class Default < DSL
|
6
8
|
|
7
9
|
def self.instance_for
|
@@ -11,6 +13,16 @@ module Phony
|
|
11
13
|
def split national_number
|
12
14
|
[national_number]
|
13
15
|
end
|
16
|
+
|
17
|
+
def plausible? rest, size, hints = {}
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# A valid length (at least 3).
|
22
|
+
#
|
23
|
+
def length
|
24
|
+
3
|
25
|
+
end
|
14
26
|
|
15
27
|
end
|
16
28
|
end
|