phony 1.0.1 → 1.1.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.
- 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
|