phony 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +14 -2
- data/lib/phony.rb +58 -463
- data/lib/phony/countries/all_other.rb +417 -0
- data/lib/phony/countries/austria.rb +69 -0
- data/lib/phony/countries/egypt.rb +40 -0
- data/lib/phony/countries/germany.rb +156 -0
- data/lib/phony/countries/greece.rb +30 -0
- data/lib/phony/countries/hungary.rb +23 -0
- data/lib/phony/countries/italy.rb +105 -0
- data/lib/phony/country.rb +102 -0
- data/lib/phony/country_codes.rb +102 -0
- data/lib/phony/local_splitter.rb +46 -0
- data/lib/phony/national_code.rb +27 -0
- data/lib/phony/national_splitters/fixed.rb +36 -0
- data/lib/phony/national_splitters/variable.rb +64 -0
- data/lib/phony/vanity.rb +35 -0
- data/spec/lib/phony/countries/austria_spec.rb +24 -0
- data/spec/lib/phony/countries/egypt_spec.rb +18 -0
- data/spec/lib/phony/countries/germany_spec.rb +24 -0
- data/spec/lib/phony/countries/greece_spec.rb +18 -0
- data/spec/lib/phony/countries/hungary_spec.rb +18 -0
- data/spec/lib/phony/countries/switzerland_spec.rb +18 -0
- data/spec/lib/phony/country_codes_spec.rb +110 -0
- data/spec/lib/phony/country_spec.rb +91 -0
- data/spec/lib/phony/local_splitter_spec.rb +48 -0
- data/spec/lib/phony/national_code_spec.rb +36 -0
- data/spec/lib/phony/national_splitters/fixed_spec.rb +43 -0
- data/spec/lib/phony/national_splitters/variable_spec.rb +35 -0
- data/spec/lib/phony/vanity_spec.rb +34 -0
- data/spec/lib/phony_spec.rb +117 -238
- metadata +45 -21
- data/lib/ndc/austria.rb +0 -69
- data/lib/ndc/fixed_size.rb +0 -113
- data/lib/ndc/germany.rb +0 -157
- data/lib/ndc/prefix.rb +0 -67
- data/lib/ndc/splitter.rb +0 -81
- data/spec/lib/ndc/austria_spec.rb +0 -40
- data/spec/lib/ndc/fixed_size_spec.rb +0 -65
- data/spec/lib/ndc/germany_spec.rb +0 -40
- data/spec/lib/ndc/prefix_spec.rb +0 -25
- data/spec/lib/ndc/splitter_spec.rb +0 -59
@@ -0,0 +1,102 @@
|
|
1
|
+
module Phony
|
2
|
+
|
3
|
+
# Handles determining the correct national code handler.
|
4
|
+
#
|
5
|
+
class CountryCodes
|
6
|
+
|
7
|
+
def normalize number
|
8
|
+
# Remove non-digit chars.
|
9
|
+
#
|
10
|
+
number.gsub! /\D*/, ''
|
11
|
+
remove_relative_zeros! number
|
12
|
+
end
|
13
|
+
|
14
|
+
# Splits this number into cc, ndc and locally split number parts.
|
15
|
+
#
|
16
|
+
def split number
|
17
|
+
national_handler, cc, rest = split_cc number
|
18
|
+
[cc, *national_handler.split(rest)]
|
19
|
+
end
|
20
|
+
|
21
|
+
def formatted number, options = {}
|
22
|
+
format_cc_ndc_local options[:format], options[:spaces] || ' ', *split(number)
|
23
|
+
end
|
24
|
+
# Formats country code and national destination code.
|
25
|
+
#
|
26
|
+
def format_cc_ndc_local format, space, cc, ndc, *parts
|
27
|
+
"#{format_cc_ndc(format, space, cc, ndc)}#{format_local(space, parts)}"
|
28
|
+
end
|
29
|
+
def format_cc_ndc format, space, cc, ndc
|
30
|
+
format, split_phone_number = case format
|
31
|
+
when nil, :international_absolute, :international, :+
|
32
|
+
[ndc.blank? ? '+%s%s' : '+%s%s%s%s', [cc, space, ndc, space]]
|
33
|
+
when :international_relative
|
34
|
+
[ndc.blank? ? '00%s%s' : '00%s%s%s%s', [cc, space, ndc, space]]
|
35
|
+
when :national
|
36
|
+
[ndc.blank? ? '' : '0%s%s', [ndc, space]]
|
37
|
+
when :local
|
38
|
+
['', []]
|
39
|
+
end
|
40
|
+
format % split_phone_number
|
41
|
+
end
|
42
|
+
def format_local space, parts_ary
|
43
|
+
parts_ary.join space.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO Speed these methods up.
|
47
|
+
#
|
48
|
+
def service? number
|
49
|
+
national_handler, cc, rest = split_cc number
|
50
|
+
national_handler.service? rest
|
51
|
+
end
|
52
|
+
def mobile? number
|
53
|
+
national_handler, cc, rest = split_cc number
|
54
|
+
national_handler.mobile? rest
|
55
|
+
end
|
56
|
+
def landline? number
|
57
|
+
national_handler, cc, rest = split_cc number
|
58
|
+
national_handler.landline? rest
|
59
|
+
end
|
60
|
+
|
61
|
+
# Is the given number a vanity number?
|
62
|
+
#
|
63
|
+
def vanity? number
|
64
|
+
national_handler, _, rest = split_cc number
|
65
|
+
national_handler.vanity? rest
|
66
|
+
end
|
67
|
+
# Converts a vanity number into a normalized E164 number.
|
68
|
+
#
|
69
|
+
def vanity_to_number vanity_number
|
70
|
+
national_handler, cc, rest = split_cc vanity_number
|
71
|
+
"#{cc}#{national_handler.vanity_to_number(rest)}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def split_cc rest
|
75
|
+
presumed_cc = ''
|
76
|
+
1.upto(3) do |i|
|
77
|
+
presumed_cc << rest.slice!(0..0)
|
78
|
+
national_code_handler = mapping[i][presumed_cc]
|
79
|
+
return [national_code_handler, presumed_cc, rest] if national_code_handler
|
80
|
+
end
|
81
|
+
# TODO raise
|
82
|
+
end
|
83
|
+
|
84
|
+
# Removes 0s from partially normalized numbers
|
85
|
+
# such as 410443643533.
|
86
|
+
#
|
87
|
+
# Example:
|
88
|
+
# 410443643533 -> 41443643533
|
89
|
+
def remove_relative_zeros! phone_number
|
90
|
+
_, cc, rest = split_cc phone_number
|
91
|
+
'%s%s' % [cc, rest].collect! { |code| code.gsub(/^0+/, '') }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Cached mapping of all countries.
|
95
|
+
#
|
96
|
+
def mapping
|
97
|
+
@mapping ||= Phony::Countries.mapping
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Phony
|
2
|
+
|
3
|
+
# Local splitter class to split the last part of
|
4
|
+
# a number, i.e. minus cc or ndc.
|
5
|
+
#
|
6
|
+
# Countries can create new instances according to their needs.
|
7
|
+
#
|
8
|
+
# Note: Countries should use instance_for
|
9
|
+
# to avoid getting new local splitter instances.
|
10
|
+
#
|
11
|
+
class LocalSplitter
|
12
|
+
|
13
|
+
@mapping = {}
|
14
|
+
|
15
|
+
# Get a splitter for the given format.
|
16
|
+
#
|
17
|
+
# Caches the created splitter for the given format.
|
18
|
+
#
|
19
|
+
def self.instance_for format = nil
|
20
|
+
@mapping[format] ||= new(format)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Initialize with a local format, like [3, 2, 2] (also the default).
|
24
|
+
#
|
25
|
+
# The format [3, 2, 2] splits a number like '3332222' into ['333', '22', '22'].
|
26
|
+
#
|
27
|
+
def initialize format = nil
|
28
|
+
@format = format || [3, 2, 2]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Split a local number according to an assumed country specific format.
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
# * split '3643533' # => ['364', '35', '33'] # (Switzerland)
|
35
|
+
#
|
36
|
+
def split number
|
37
|
+
@format.inject([]) do |result, size|
|
38
|
+
result << number.slice!(0..size-1)
|
39
|
+
return result if number.empty?
|
40
|
+
result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Phony
|
2
|
+
|
3
|
+
# This is the superclass of all special national number handlers.
|
4
|
+
#
|
5
|
+
# NationalCodes have a special numbers splitter, a national code splitter and a local code splitter.
|
6
|
+
#
|
7
|
+
class NationalCode
|
8
|
+
|
9
|
+
#
|
10
|
+
#
|
11
|
+
def initialize national_splitter, local_splitter
|
12
|
+
@national_splitter = national_splitter
|
13
|
+
@local_splitter = local_splitter
|
14
|
+
end
|
15
|
+
|
16
|
+
# Split gets a number without country code and splits it into
|
17
|
+
# its parts.
|
18
|
+
#
|
19
|
+
def split national_number
|
20
|
+
ndc, rest = @national_splitter.split national_number.dup
|
21
|
+
return ndc unless rest
|
22
|
+
[ndc, *@local_splitter.split(rest)]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Phony
|
2
|
+
|
3
|
+
module NationalSplitters
|
4
|
+
|
5
|
+
# TODO
|
6
|
+
#
|
7
|
+
class Fixed
|
8
|
+
|
9
|
+
attr_writer :special_splitter
|
10
|
+
|
11
|
+
@mapping = {}
|
12
|
+
|
13
|
+
# Get a splitter for the given format.
|
14
|
+
#
|
15
|
+
# Caches the created splitter for the given format.
|
16
|
+
#
|
17
|
+
def self.instance_for size
|
18
|
+
@mapping[size] ||= new(size)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize size
|
22
|
+
@size = size
|
23
|
+
end
|
24
|
+
|
25
|
+
# Takes a national number and splits it into ndc and rest.
|
26
|
+
#
|
27
|
+
def split national_number
|
28
|
+
return [national_number] unless @size
|
29
|
+
[national_number.slice!(0...@size), national_number]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Phony
|
2
|
+
|
3
|
+
module NationalSplitters
|
4
|
+
|
5
|
+
class Variable < Fixed
|
6
|
+
|
7
|
+
#
|
8
|
+
#
|
9
|
+
def initialize fallback, ndc_map
|
10
|
+
super fallback
|
11
|
+
@ndcs = restructure ndc_map
|
12
|
+
end
|
13
|
+
|
14
|
+
# Takes a national number and splits it into ndc and rest.
|
15
|
+
#
|
16
|
+
def split national_number
|
17
|
+
fallback_number = national_number.dup
|
18
|
+
|
19
|
+
# Extract a starting point.
|
20
|
+
#
|
21
|
+
presumed_code = if @mapped_ndc_min_length > 0
|
22
|
+
national_number.slice!(0..@mapped_ndc_min_length-1)
|
23
|
+
else
|
24
|
+
''
|
25
|
+
end
|
26
|
+
|
27
|
+
# Try for all possible mapped.
|
28
|
+
#
|
29
|
+
@mapped_ndc_min_length.upto(@mapped_ndc_max_length) do |i|
|
30
|
+
sized_ndcs = @ndcs[i]
|
31
|
+
return [presumed_code, national_number] unless sized_ndcs && !sized_ndcs.include?(presumed_code)
|
32
|
+
presumed_code << national_number.slice!(0..0)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Not found.
|
36
|
+
#
|
37
|
+
return super(fallback_number)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def restructure ndc_map
|
43
|
+
optimize ndc_map.values.flatten
|
44
|
+
end
|
45
|
+
|
46
|
+
# Optimizes and restructures the given ndcs array.
|
47
|
+
#
|
48
|
+
def optimize(ndc_ary)
|
49
|
+
ndcs = {}
|
50
|
+
ndc_ary.each do |ndc|
|
51
|
+
ndcs[ndc.length] ||= []
|
52
|
+
ndcs[ndc.length] << ndc
|
53
|
+
end
|
54
|
+
keys = ndcs.keys
|
55
|
+
@mapped_ndc_min_length = keys.min # || 1
|
56
|
+
@mapped_ndc_max_length = keys.max
|
57
|
+
ndcs
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/phony/vanity.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Phony
|
2
|
+
module Vanity
|
3
|
+
|
4
|
+
# Returns a char to number mapping string for the String#tr method.
|
5
|
+
#
|
6
|
+
def self.mapping
|
7
|
+
@@mapping ||= [
|
8
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.freeze,
|
9
|
+
'2223334445556667777888999922233344455566677778889999'.freeze
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Replaces (and normalizes) vanity characters of passed number with correct digits.
|
14
|
+
#
|
15
|
+
def self.replace number
|
16
|
+
number.tr *mapping
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns true if there is a character in the number
|
20
|
+
# after the first four numbers.
|
21
|
+
#
|
22
|
+
@@vanity_regexp = /\A\d{3}[a-zA-Z]{6,12}\Z/
|
23
|
+
def self.vanity? number
|
24
|
+
!(normalized(number) =~ @@vanity_regexp).nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Vanity-Normalized.
|
28
|
+
#
|
29
|
+
@@vanity_normalizing_regexp = /^0*|[^\d\w]/
|
30
|
+
def self.normalized number
|
31
|
+
number.gsub @@vanity_normalizing_regexp, ''
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Phony::Countries::Austria do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@austria = Phony::Countries::Austria
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "split" do
|
10
|
+
it "should handle Vienna" do
|
11
|
+
@austria.split('198110').should == ['1', '98110']
|
12
|
+
end
|
13
|
+
it "should handle some mobile services" do
|
14
|
+
@austria.split('66914093902').should == ['669', '14093902']
|
15
|
+
end
|
16
|
+
it "should handle Graz" do
|
17
|
+
@austria.split('3161234567891').should == ['316', '1234567891']
|
18
|
+
end
|
19
|
+
it "should handle Rohrau" do
|
20
|
+
@austria.split('2164123456789').should == ['2164', '123456789']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Phony::Countries::Egypt do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@egypt = Phony::Countries::Egypt
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "split" do
|
10
|
+
it "works" do
|
11
|
+
@egypt.split('233453756').should == ['2', '33453756']
|
12
|
+
end
|
13
|
+
it "works" do
|
14
|
+
@egypt.split('921234567').should == ['92', '1234567']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Phony::Countries::Germany do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@germany = Phony::Countries::Germany
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "split" do
|
10
|
+
it "should handle Berlin" do
|
11
|
+
@germany.split('3038625454').should == ['30', '386', '25454']
|
12
|
+
end
|
13
|
+
it "should handle Cologne" do
|
14
|
+
@germany.split('22137683323').should == ['221', '376', '83323']
|
15
|
+
end
|
16
|
+
it "should handle Freiburg im Breisgau" do
|
17
|
+
@germany.split('7614767676').should == ['761', '476', '7676']
|
18
|
+
end
|
19
|
+
it "should handle Nettetal-Lobberich" do
|
20
|
+
@germany.split('21535100').should == ['2153', '510', '0']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Phony::Countries::Greece do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@greece = Phony::Countries::Greece
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "split" do
|
10
|
+
it 'works' do
|
11
|
+
@greece.split('21123456').should == ['21', '123456']
|
12
|
+
end
|
13
|
+
it 'works' do
|
14
|
+
@greece.split('25941234').should == ['2594', '1234']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Phony::Countries::Hungary do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@hungary = Phony::Countries::Hungary
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "split" do
|
10
|
+
it 'handles Budapest' do
|
11
|
+
@hungary.split('12345678').should == ['1', '234', '5678']
|
12
|
+
end
|
13
|
+
it 'works' do
|
14
|
+
@hungary.split('22123456').should == ['22', '123', '456']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Switzerland' do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@switzerland = Phony::Countries.with_cc '41'
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "split" do
|
10
|
+
it "works" do
|
11
|
+
@switzerland.split('443643532').should == ['44', '364', '35', '32']
|
12
|
+
end
|
13
|
+
it "works" do
|
14
|
+
@switzerland.split('800123456').should == ['800', '123', '456']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|