phoney 0.1.3 → 0.2.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.
- checksums.yaml +7 -0
- data/Gemfile +1 -5
- data/Gemfile.lock +3 -11
- data/{LICENCE → MIT-LICENCE} +1 -1
- data/README.rdoc +16 -20
- data/Rakefile +9 -25
- data/lib/data/regions.bin +0 -0
- data/lib/phoney.rb +103 -6
- data/lib/phoney/formatter.rb +144 -0
- data/lib/phoney/parser.rb +62 -218
- data/lib/phoney/region.rb +19 -35
- data/lib/phoney/rules.rb +56 -0
- data/lib/phoney/vanity.rb +31 -0
- data/lib/phoney/version.rb +2 -10
- data/phoney.gemspec +15 -43
- data/resources/Default.phoneformat +0 -0
- data/resources/liveinput_tester.rb +32 -0
- data/resources/phoneformat_converter.rb +122 -0
- data/resources/tasks/region_data.rake +23 -0
- data/test/formatter_test.rb +91 -0
- data/test/lib/phoney/test_helper.rb +6 -0
- data/test/phone_number_test.rb +4 -0
- data/test/regions/br_test.rb +33 -0
- data/test/regions/de_test.rb +45 -0
- data/test/regions/fr_test.rb +17 -0
- data/test/regions/ni_test.rb +14 -0
- data/test/regions/th_test.rb +38 -0
- data/test/regions/tw_test.rb +28 -0
- data/test/regions/ua_test.rb +18 -0
- data/test/regions/us_test.rb +44 -0
- metadata +45 -45
- data/lib/data/regions.yml +0 -19601
- data/lib/phoney/base.rb +0 -102
- data/lib/phoney/utils.rb +0 -52
- data/spec/parser/br_spec.rb +0 -64
- data/spec/parser/de_spec.rb +0 -73
- data/spec/parser/us_spec.rb +0 -49
- data/spec/phone_number_spec.rb +0 -40
- data/spec/spec.opts +0 -4
- data/spec/spec_helper.rb +0 -4
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 91ea215316dbde0a1e0ab7c0a8ee480c17021db9264ad5949f20ad87a9fb01c2
|
4
|
+
data.tar.gz: 8010af03683d901660a83ecd03a2b8fa6ff9e5e4ae19ce50a7c74b04d33f20ab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9803f88389d58b814d942589496dbca4e74b3891066c8501f375e2a6ebc6c5a97dc946f91287f5867d5a8f48a2fce8e31149ad2ca08db3abe3790a4280047d29
|
7
|
+
data.tar.gz: b8a687dcb6fbf2d00d4f4491aa36eb5d74a7ba45c2ec466ed48fba9000fbd3f3183a16ae3218e06dbcb8fbb9356b8d38f0908d18f1bbeeec9b157a378c5133a4
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,26 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
phoney (0.
|
4
|
+
phoney (0.9.9)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
8
8
|
specs:
|
9
|
-
|
9
|
+
minitest (4.6.2)
|
10
10
|
rake (0.9.2.2)
|
11
|
-
rspec (2.11.0)
|
12
|
-
rspec-core (~> 2.11.0)
|
13
|
-
rspec-expectations (~> 2.11.0)
|
14
|
-
rspec-mocks (~> 2.11.0)
|
15
|
-
rspec-core (2.11.1)
|
16
|
-
rspec-expectations (2.11.2)
|
17
|
-
diff-lcs (~> 1.1.3)
|
18
|
-
rspec-mocks (2.11.2)
|
19
11
|
|
20
12
|
PLATFORMS
|
21
13
|
ruby
|
22
14
|
|
23
15
|
DEPENDENCIES
|
16
|
+
minitest
|
24
17
|
phoney!
|
25
18
|
rake
|
26
|
-
rspec (= 2.11.0)
|
data/{LICENCE → MIT-LICENCE}
RENAMED
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= phoney
|
2
2
|
|
3
3
|
This is a library for representing phone numbers.
|
4
|
-
It provides a
|
4
|
+
It provides a Phoney class that can format phone numbers depending on the region you set.
|
5
5
|
|
6
6
|
== Installation
|
7
7
|
|
@@ -14,7 +14,7 @@ Source:: <tt>git clone git://github.com/habermann24/phoney.git</tt>
|
|
14
14
|
|
15
15
|
require 'phoney'
|
16
16
|
# region defaults to US
|
17
|
-
pn =
|
17
|
+
pn = Phoney.new("+17041234567")
|
18
18
|
pn.to_s # "+1 (704) 123-4567"
|
19
19
|
pn.area_code # "704"
|
20
20
|
pn.country_code # "1"
|
@@ -24,30 +24,30 @@ Source:: <tt>git clone git://github.com/habermann24/phoney.git</tt>
|
|
24
24
|
|
25
25
|
require 'phoney'
|
26
26
|
|
27
|
-
|
27
|
+
Phoney.region = :de
|
28
28
|
|
29
|
-
pn =
|
29
|
+
pn = Phoney.new("04105456789")
|
30
30
|
pn.to_s # "+49 4105 456789"
|
31
31
|
pn.area_code # "4105"
|
32
32
|
pn.country_code # "49"
|
33
33
|
pn.number # "456789"
|
34
34
|
|
35
|
-
== Creating
|
35
|
+
== Creating Phoney instances
|
36
36
|
|
37
|
-
Phoney gives you a
|
38
|
-
The default region phoney uses for formatting is the US-region format. So if you want to parse phone numbers from a different country, you have to set
|
37
|
+
Phoney gives you a Phoney class that wraps all the logic of phone number parsing and representation.
|
38
|
+
The default region phoney uses for formatting is the US-region format. So if you want to parse phone numbers from a different country, you have to set Phoney.region or pass the region code every time!
|
39
39
|
|
40
|
-
|
40
|
+
Phoney.region = :us
|
41
41
|
|
42
|
-
The most common way to create a
|
43
|
-
|
44
|
-
|
42
|
+
The most common way to create a Phoney object is by parsing from a string:
|
43
|
+
Phoney.new("7041231234") # uses region :us
|
44
|
+
Phoney.new("01805708090", :de) # uses region :de
|
45
45
|
|
46
46
|
Or instead of parsing a string, you can provide a hash for the first parameter:
|
47
|
-
|
47
|
+
Phoney.new(:number => "1231234", :area_code => "704", :country_code => "1")
|
48
48
|
|
49
49
|
# falls back to US region, so also uses "1" for <tt>country_code</tt>
|
50
|
-
|
50
|
+
Phoney.new(:number => "1231234", :area_code => "704")
|
51
51
|
|
52
52
|
== Formatting
|
53
53
|
|
@@ -59,13 +59,13 @@ When given a string, it interpolates the string with the following fields:
|
|
59
59
|
* %a - area_code (91)
|
60
60
|
* %n - number (5125486)
|
61
61
|
|
62
|
-
pn =
|
62
|
+
pn = Phoney.new('+446546546546')
|
63
63
|
|
64
64
|
pn.to_s # => "+44 65 4654 6546"
|
65
65
|
pn.format("%a/%n") # => "64/46546546"
|
66
66
|
pn.format("+ %c (%a) %n") # => "+ 44 (65) 46546546"
|
67
67
|
|
68
|
-
Usually you will just want
|
68
|
+
Usually you will just want Phoney to figure out how to format the number correctly.
|
69
69
|
When given a symbol you can let the parser guess the best format and pass in one of the following auto-formatting symbols:
|
70
70
|
pn.format(:default) # => "+44 65 4654 6546"
|
71
71
|
|
@@ -77,8 +77,4 @@ When given a symbol you can let the parser guess the best format and pass in one
|
|
77
77
|
|
78
78
|
== TODOs
|
79
79
|
|
80
|
-
- More
|
81
|
-
|
82
|
-
== Licence
|
83
|
-
|
84
|
-
see LICENSE
|
80
|
+
- More tests for different countries
|
data/Rakefile
CHANGED
@@ -1,29 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
-
end
|
7
|
-
|
8
|
-
require 'rdoc/task'
|
9
|
-
|
10
|
-
Rake::RDocTask.new do |rdoc|
|
11
|
-
rdoc.rdoc_dir = 'doc/rdoc'
|
12
|
-
rdoc.template = ENV['template'] if ENV['template']
|
13
|
-
rdoc.title = "Phoney Documentation"
|
14
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
15
|
-
rdoc.options << '--charset' << 'utf-8'
|
16
|
-
rdoc.rdoc_files.include('README.rdoc')
|
17
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
18
|
-
end
|
19
|
-
|
20
|
-
require 'rspec/core/rake_task'
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
21
5
|
|
22
|
-
|
23
|
-
task :default => :spec
|
6
|
+
task :default => :test
|
24
7
|
|
25
|
-
|
26
|
-
t.
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << 'test/lib'
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
27
11
|
end
|
28
12
|
|
29
|
-
|
13
|
+
Dir.glob('resources/tasks/*.rake').each { |r| import r }
|
Binary file
|
data/lib/phoney.rb
CHANGED
@@ -1,8 +1,105 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require 'phoney/version'
|
2
|
+
require 'phoney/rules'
|
3
|
+
require 'phoney/region'
|
4
|
+
require 'phoney/formatter'
|
5
|
+
require 'phoney/parser'
|
6
|
+
|
7
|
+
module Phoney
|
8
|
+
PLACEHOLDER_CHAR = '#'
|
9
|
+
DIGITS = '0123456789'
|
10
|
+
NUMPAD_CHARS = '+#*'+DIGITS
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def region
|
14
|
+
@region ||= Region[:us]
|
15
|
+
end
|
16
|
+
|
17
|
+
def region=(region)
|
18
|
+
@region = Region[region.to_s.to_sym]
|
19
|
+
end
|
20
|
+
|
21
|
+
def country_code
|
22
|
+
@country_code ||= region.country_code.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def area_code
|
26
|
+
@area_code ||= nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def area_code=(area_code)
|
30
|
+
@area_code = area_code
|
31
|
+
end
|
32
|
+
|
33
|
+
def version
|
34
|
+
VERSION::STRING
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(params, region_code=nil)
|
39
|
+
region = Region.find(region_code)
|
40
|
+
country_code = region.country_code.to_s if region
|
41
|
+
|
42
|
+
if params.is_a?(String)
|
43
|
+
params = Parser.parse_to_parts(params, region_code)
|
44
|
+
end
|
45
|
+
|
46
|
+
self.number = params[:number].to_s
|
47
|
+
# The rare case when some digits are in front of the area code
|
48
|
+
self.prefix_code = params[:prefix_code].to_s
|
49
|
+
# Can be empty, because some special numbers just don't have an area code (e.g. 911)
|
50
|
+
self.area_code = params[:area_code].to_s || self.class.area_code
|
51
|
+
self.country_code = params[:country_code].to_s || country_code || self.class.country_code
|
52
|
+
|
53
|
+
raise "Must enter number" if(self.number.nil? || self.number.empty?)
|
54
|
+
raise "Must enter country code or set default country code" if(self.country_code.nil? || self.country_code.empty?)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Does this number belong to the default country code?
|
58
|
+
def has_default_country_code?
|
59
|
+
country_code.to_s == self.class.country_code.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
# Does this number belong to the default area code?
|
63
|
+
def has_default_area_code?
|
64
|
+
(!area_code.to_s.empty? && area_code.to_s == self.class.area_code.to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Formats the phone number.
|
68
|
+
# If the method argument is a String, it is used as a format string, with the following fields being interpolated:
|
69
|
+
#
|
70
|
+
# * %c - country_code (385)
|
71
|
+
# * %a - area_code (91)
|
72
|
+
# * %n - number (5125486)
|
73
|
+
#
|
74
|
+
# If the method argument is a Symbol, we use one of the default formattings and let the parser do the rest.
|
75
|
+
def format(fmt)
|
76
|
+
if fmt.is_a?(Symbol)
|
77
|
+
case fmt
|
78
|
+
when :default
|
79
|
+
Parser::parse("+#{country_code} #{prefix_code}#{area_code} #{number}", country_code)
|
80
|
+
when :national
|
81
|
+
Parser::parse("#{area_code} #{number}", country_code)
|
82
|
+
when :local
|
83
|
+
STDERR.puts "Warning: Using local format without setting a default area code!?" if Phoney.area_code.nil?
|
84
|
+
Parser::parse(number, country_code)
|
85
|
+
else
|
86
|
+
raise "The format #{fmt} doesn't exist'"
|
87
|
+
end
|
88
|
+
else
|
89
|
+
format_number(fmt)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# The default format is the canonical format: "+{country_code} {area_code} {number}"
|
94
|
+
def to_s
|
95
|
+
format(:default)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def format_number(fmt)
|
100
|
+
fmt.gsub("%c", country_code || "").gsub("%a", area_code || "").gsub("%n", number || "")
|
101
|
+
end
|
102
|
+
end
|
6
103
|
|
7
104
|
# Load our region file when we require the library
|
8
|
-
|
105
|
+
Phoney::Region.load
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Phoney
|
2
|
+
module Formatter
|
3
|
+
# Returns the string formatted according to a pattern.
|
4
|
+
#
|
5
|
+
# Examples:
|
6
|
+
# format('123456789', 'XXX-XX-XXXX')
|
7
|
+
# => "123-45-6789"
|
8
|
+
# format('12345', 'XXX-XX-XXXX')
|
9
|
+
# => "123-45"
|
10
|
+
#
|
11
|
+
# Parameters:
|
12
|
+
# string -- The string to be formatted.
|
13
|
+
# pattern -- The format string, see above examples.
|
14
|
+
# fill -- A string for padding. If the empty string, then the pattern is
|
15
|
+
# filled as much as possible, and the rest of the pattern is
|
16
|
+
# truncated. If nil, and the string is too long for the pattern,
|
17
|
+
# the string is returned unchanged. Otherwise, the string is
|
18
|
+
# padded to fill the pattern, which is not truncated.
|
19
|
+
def format(input, pattern, options={})
|
20
|
+
fill = options[:fill]
|
21
|
+
intl_prefix = options[:intl_prefix]||''
|
22
|
+
trunk_prefix = options[:trunk_prefix]||''
|
23
|
+
slots = pattern.count(PLACEHOLDER_CHAR)
|
24
|
+
|
25
|
+
# Return original input if it is too long
|
26
|
+
return input if (fill.nil? && input.length > slots)
|
27
|
+
|
28
|
+
# Pad and clone the string if necessary.
|
29
|
+
source = (fill.nil? || fill.empty?) ? input : input.ljust(slots, fill)
|
30
|
+
|
31
|
+
result = ''
|
32
|
+
slot = 0
|
33
|
+
has_open = had_c = had_n = false
|
34
|
+
|
35
|
+
pattern.split('').each_with_index do |chr, index|
|
36
|
+
case chr
|
37
|
+
when 'c'
|
38
|
+
had_c = true
|
39
|
+
result << intl_prefix
|
40
|
+
when 'n'
|
41
|
+
had_n = true
|
42
|
+
result << trunk_prefix
|
43
|
+
when '#'
|
44
|
+
if slot < source.length
|
45
|
+
result << source[slot]
|
46
|
+
slot += 1
|
47
|
+
else
|
48
|
+
result << ' ' if has_open
|
49
|
+
end
|
50
|
+
when '('
|
51
|
+
if slot < source.length
|
52
|
+
has_open = true
|
53
|
+
result << chr
|
54
|
+
end
|
55
|
+
when ')'
|
56
|
+
if (slot < source.length || has_open)
|
57
|
+
has_open = false
|
58
|
+
result << chr
|
59
|
+
end
|
60
|
+
else
|
61
|
+
# Don't show space after n if no trunk prefix or after c if no intl prefix
|
62
|
+
next if (chr == ' ' && pattern[index-1] == 'n' && trunk_prefix.empty?)
|
63
|
+
next if (chr == ' ' && pattern[index-1] == 'c' && intl_prefix.empty?)
|
64
|
+
|
65
|
+
result << chr if (slot < source.length)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Not all format strings have a 'c' or 'n' in them.
|
70
|
+
# If we have an international prefix or a trunk prefix but the format string
|
71
|
+
# doesn't explictly say where to put it then simply add it to the beginning.
|
72
|
+
result.prepend trunk_prefix if (!had_n && !trunk_prefix.empty?)
|
73
|
+
result.prepend "#{intl_prefix} " if (!had_c && !intl_prefix.empty?)
|
74
|
+
|
75
|
+
result.strip
|
76
|
+
end
|
77
|
+
|
78
|
+
# Strips all non-numberpad characters from a string
|
79
|
+
# => For example: "+45 (123) 023 1.1.1" -> "+45123023111"
|
80
|
+
def normalize(str)
|
81
|
+
str.gsub(/[^0-9+*#]/,'') unless str.nil?
|
82
|
+
end
|
83
|
+
|
84
|
+
def international_call_prefix_for(input, options={})
|
85
|
+
options[:region] ||= Phoney.region
|
86
|
+
|
87
|
+
return nil if input.length == 0
|
88
|
+
|
89
|
+
options[:region].dialout_prefixes.each do |prefix|
|
90
|
+
stripped_prefix = Regexp.escape prefix.delete(' ').split('->').first[0, input.length]
|
91
|
+
regexp = Regexp.new "^#{stripped_prefix.gsub('\\#', '[0-9]')}"
|
92
|
+
|
93
|
+
return format(input, prefix.gsub(/[\\+0-9]/, '#'), fill: '') if input =~ regexp
|
94
|
+
|
95
|
+
if (input.start_with?('+') && (stripped_prefix.start_with?(input[1..-1]) || input[1..-1] =~ regexp))
|
96
|
+
return format(input, '#'+prefix.gsub(/[\\+0-9]/, '#'), fill: '')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
input.start_with?('+') ? '+' : nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO: handle case where international call prefix implicitly specifies country (e.g. tz: "005->254")
|
104
|
+
def extract_country_code(input, options={})
|
105
|
+
options[:region] ||= Phoney.region
|
106
|
+
intl_prefix = international_call_prefix_for(input, region: options[:region])
|
107
|
+
|
108
|
+
# only try to extract a country code if we're dialing internationally
|
109
|
+
if intl_prefix
|
110
|
+
rest = input[intl_prefix.count(NUMPAD_CHARS)..-1]
|
111
|
+
region = Phoney::Region.all.find { |r| rest.start_with? r.country_code.to_s }
|
112
|
+
|
113
|
+
region.country_code.to_s if region
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def extract_trunk_prefix(input, options={})
|
118
|
+
options[:region] ||= Phoney.region
|
119
|
+
|
120
|
+
intl_prefix = international_call_prefix_for input
|
121
|
+
country_code = extract_country_code(input, region: options[:region])
|
122
|
+
region_scope = country_code.nil? ? options[:region] : Phoney::Region[country_code]
|
123
|
+
|
124
|
+
if intl_prefix
|
125
|
+
# Strip international prefix from number
|
126
|
+
input = input[intl_prefix.count(NUMPAD_CHARS)..-1]
|
127
|
+
end
|
128
|
+
|
129
|
+
if country_code
|
130
|
+
# Strip country code from number
|
131
|
+
input = input[country_code.count(DIGITS)..-1]
|
132
|
+
end
|
133
|
+
|
134
|
+
region_scope.trunk_prefixes.each do |prefix|
|
135
|
+
stripped_prefix = Regexp.escape prefix.delete(' ')
|
136
|
+
regexp = Regexp.new "^#{stripped_prefix.gsub('\\#', '[0-9]')}"
|
137
|
+
|
138
|
+
return format(input, prefix.gsub(/[0-9]/, '#'), fill: '') if input =~ regexp
|
139
|
+
end
|
140
|
+
|
141
|
+
return nil
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/phoney/parser.rb
CHANGED
@@ -1,245 +1,89 @@
|
|
1
1
|
require 'strscan'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module Phoney
|
5
4
|
module Parser
|
6
5
|
class << self
|
7
|
-
include
|
8
|
-
|
9
|
-
def parse(phone_number, region_code=nil)
|
10
|
-
parse_to_parts(phone_number, region_code)[:formatted_number]
|
11
|
-
end
|
6
|
+
include Formatter
|
12
7
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
dialout_prefix = get_dialout_prefix(phone_number, region)
|
27
|
-
national_prefix = get_national_prefix(phone_number, region)
|
28
|
-
dialout_region = get_dialout_region(phone_number, region)
|
29
|
-
dialout_country = ''
|
30
|
-
|
31
|
-
# No dialout prefix without a dialout region, and no dialout region without a prefix
|
32
|
-
if((dialout_region && dialout_prefix.empty?) || (!dialout_region && !dialout_prefix.empty?))
|
33
|
-
rule_sets = []
|
34
|
-
else
|
35
|
-
rule_sets = get_rule_sets_for_region(phone_number, dialout_region || region)
|
8
|
+
def parse(input, options={})
|
9
|
+
number = normalize(input)
|
10
|
+
intl_prefix = international_call_prefix_for number
|
11
|
+
country_code = extract_country_code number
|
12
|
+
trunk_prefix = extract_trunk_prefix number
|
13
|
+
region = options[:region] || Phoney.region
|
14
|
+
flags = []
|
15
|
+
|
16
|
+
if intl_prefix
|
17
|
+
# Strip international prefix from number
|
18
|
+
number = number[intl_prefix.count(NUMPAD_CHARS)..-1]
|
19
|
+
region = Phoney::Region[country_code]
|
20
|
+
flags << :c
|
36
21
|
end
|
37
22
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
country_code = dialout_region.country_code.to_s
|
42
|
-
dialout_country = country_code
|
43
|
-
else
|
44
|
-
prefix = national_prefix
|
45
|
-
prefix += dialout_prefix.delete(' ') unless(dialout_prefix.empty?)
|
46
|
-
end
|
47
|
-
|
48
|
-
# strip the total prefix from the beginning of the number
|
49
|
-
phone_number = phone_number[prefix.length..-1]
|
50
|
-
number = phone_number
|
51
|
-
|
52
|
-
prefered_type = 0 # for sorting the priority
|
53
|
-
|
54
|
-
# if we're dialing out or using the national prefix
|
55
|
-
if(dialout_region || !national_prefix.empty?)
|
56
|
-
# we need to sort the rules slightly different
|
57
|
-
prefered_type = dialout_region.nil? ? 1 : 2
|
58
|
-
end
|
59
|
-
|
60
|
-
# sorting for rule priorities
|
61
|
-
rule_sets.each do |rule_set|
|
62
|
-
rule_set[:rules] = rule_set[:rules].sort_by do |rule|
|
63
|
-
# [ prefered rule type ASC, total_digits ASC ]
|
64
|
-
[ (rule[:type]==prefered_type) ? -1 : rule[:type], rule[:total_digits], rule[:index] ]
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# finally...find our matching rule
|
69
|
-
matching_rule = find_matching_rule(phone_number, rule_sets)
|
70
|
-
|
71
|
-
# now that know how to format the number, do the formatting work...
|
72
|
-
if(matching_rule)
|
73
|
-
if(matching_rule[:areacode_offset] > 0)
|
74
|
-
prefix_code = phone_number[0, matching_rule[:areacode_offset]]
|
75
|
-
end
|
76
|
-
area_code = phone_number[matching_rule[:areacode_offset], matching_rule[:areacode_length]]
|
77
|
-
number = phone_number[matching_rule[:areacode_offset]+matching_rule[:areacode_length]..-1]
|
78
|
-
phone_number = format(phone_number, matching_rule[:format].to_s)
|
79
|
-
|
80
|
-
# replace 'n' with our national_prefix if it exists
|
81
|
-
if(phone_number[/n/])
|
82
|
-
phone_number.gsub!(/n{1}/, national_prefix)
|
83
|
-
|
84
|
-
# reset the national_prefix so we don't add it twice
|
85
|
-
national_prefix = ''
|
86
|
-
end
|
87
|
-
end
|
23
|
+
if country_code
|
24
|
+
# Strip country code from number
|
25
|
+
number = number[country_code.count(DIGITS)..-1]
|
88
26
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if(matching_rule && phone_number[/c/])
|
93
|
-
# format the country code
|
94
|
-
if(dialout_prefix == '+')
|
95
|
-
phone_number.gsub!(/c{1}/, "+#{dialout_country}")
|
27
|
+
if intl_prefix == '+'
|
28
|
+
intl_prefix += country_code
|
96
29
|
else
|
97
|
-
|
98
|
-
phone_number = "#{dialout_prefix} #{phone_number}" unless dialout_prefix.empty?
|
99
|
-
end
|
100
|
-
else
|
101
|
-
# default formatting
|
102
|
-
if(dialout_prefix == '+')
|
103
|
-
if(dialout_country.empty?)
|
104
|
-
phone_number = "+#{phone_number}"
|
105
|
-
else
|
106
|
-
phone_number = "+#{dialout_country} #{phone_number}"
|
107
|
-
end
|
108
|
-
else
|
109
|
-
phone_number = "#{dialout_country} #{phone_number}" unless dialout_country.empty?
|
110
|
-
phone_number = "#{dialout_prefix} #{phone_number}" unless dialout_prefix.empty?
|
111
|
-
phone_number = national_prefix + phone_number unless national_prefix.empty?
|
30
|
+
intl_prefix = [intl_prefix, country_code].join(' ')
|
112
31
|
end
|
113
32
|
end
|
114
33
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
# remove possible non-numeric characters from the (invalid) number
|
119
|
-
number.gsub!(/[^0-9]/,'') if number
|
120
|
-
|
121
|
-
# Finally...we can output our parts as a hash
|
122
|
-
{ :formatted_number => phone_number,
|
123
|
-
:prefix_code => prefix_code,
|
124
|
-
:area_code => area_code,
|
125
|
-
:country_code => country_code,
|
126
|
-
:number => number }
|
127
|
-
end
|
128
|
-
|
129
|
-
private
|
130
|
-
# Returns the rule sets that we need to check for a given number and region.
|
131
|
-
# The rule_sets are filtered by the length of the number!
|
132
|
-
def get_rule_sets_for_region(string, region)
|
133
|
-
rule_sets = []
|
34
|
+
rule = find_matching_rule_for number, region: region, flags: flags
|
35
|
+
rule ||= find_matching_rule_for number, region: region
|
36
|
+
return format number, rule.pattern, intl_prefix: intl_prefix if rule
|
134
37
|
|
135
|
-
if
|
136
|
-
|
137
|
-
|
38
|
+
if trunk_prefix
|
39
|
+
# Strip trunk prefix from number
|
40
|
+
number = number[trunk_prefix.count(DIGITS)..-1]
|
41
|
+
flags << :n
|
42
|
+
|
43
|
+
if intl_prefix
|
44
|
+
intl_prefix += " (#{trunk_prefix})"
|
45
|
+
trunk_prefix = nil
|
138
46
|
end
|
139
47
|
end
|
140
48
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
# Given any number, find the rule in rule_sets that applies.
|
145
|
-
# Returns nil if no matching rule was found!
|
146
|
-
def find_matching_rule(number, rule_sets)
|
147
|
-
return nil if !number.match(/\A[0-9]+\Z/)
|
148
|
-
match = nil
|
149
|
-
|
150
|
-
# go through all our given rules
|
151
|
-
for rule_set in rule_sets do
|
152
|
-
digits = rule_set[:digits]
|
153
|
-
prefix = number[0,digits].to_i
|
154
|
-
rules = rule_set[:rules].select { |rule| rule[:total_digits] >= number.length }
|
155
|
-
|
156
|
-
rules.each do |rule|
|
157
|
-
if(prefix >= rule[:min] && prefix <= rule[:max])
|
158
|
-
match = rule
|
159
|
-
break
|
160
|
-
end
|
161
|
-
end
|
49
|
+
if country_code || intl_prefix != '+'
|
50
|
+
rule = find_matching_rule_for number, region: region, flags: flags
|
51
|
+
rule ||= find_matching_rule_for number, region: region
|
162
52
|
|
163
|
-
|
53
|
+
pattern = '#'*number.length
|
54
|
+
pattern = rule.pattern if rule
|
55
|
+
|
56
|
+
format number, pattern, intl_prefix: intl_prefix, trunk_prefix: trunk_prefix
|
57
|
+
else
|
58
|
+
input
|
164
59
|
end
|
165
|
-
|
166
|
-
match
|
167
|
-
end
|
168
|
-
|
169
|
-
# According to the region, is this number input trying to dial out?
|
170
|
-
def dialing_out?(string, region=nil)
|
171
|
-
region ||= PhoneNumber.default_region
|
172
|
-
!get_dialout_prefix(string, region).empty?
|
173
60
|
end
|
174
61
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
region ||= PhoneNumber.default_region
|
179
|
-
prefixes = region.dialout_prefixes
|
180
|
-
dialout_prefix = ''
|
181
|
-
|
182
|
-
# check if we're dialing outside our region
|
183
|
-
if string[0].chr == '+'
|
184
|
-
dialout_prefix = '+'
|
185
|
-
end
|
186
|
-
|
187
|
-
for prefix in prefixes do
|
188
|
-
regexp = Regexp.escape(prefix)
|
189
|
-
match_str = string
|
62
|
+
private
|
63
|
+
def find_matching_rule_for(number, options={})
|
64
|
+
options[:region] ||= Region.new
|
190
65
|
|
191
|
-
#
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
66
|
+
# Consider all rule sets that aren't too specific for the number
|
67
|
+
options[:region].rule_sets.select { |r| number.length >= r.significant_digits }.each do |rule_set|
|
68
|
+
rules = rule_set.rules
|
69
|
+
|
70
|
+
# Only consider rules with :c flag if this flag is set
|
71
|
+
if options[:flags] && options[:flags].include?(:c)
|
72
|
+
rules = rules.reject { |r| !r.flags.include? :c }
|
73
|
+
else
|
74
|
+
# Only consider rules with :n flag if this flag is set
|
75
|
+
if options[:flags] && options[:flags].include?(:n)
|
76
|
+
rules = rules.reject { |r| !r.flags.include? :n }
|
77
|
+
end
|
78
|
+
end
|
197
79
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
dialout_prefix
|
205
|
-
end
|
206
|
-
|
207
|
-
# Get the national prefix from the given string.
|
208
|
-
# Returns an empty string if no national prefix was found.
|
209
|
-
def get_national_prefix(string, region=nil)
|
210
|
-
region ||= PhoneNumber.default_region
|
211
|
-
prefix = region.national_prefix
|
212
|
-
national_prefix = ''
|
213
|
-
|
214
|
-
# in case we're not dialing out and the number starts with the national_prefix
|
215
|
-
if(!dialing_out?(string, region) && string =~ Regexp.new("^#{Regexp.escape(prefix)}"))
|
216
|
-
national_prefix = prefix
|
217
|
-
end
|
218
|
-
|
219
|
-
national_prefix
|
220
|
-
end
|
221
|
-
|
222
|
-
# Get the dialout region by looking at the string.
|
223
|
-
# Returns a Region object if we're dialing outside a region that is supported.
|
224
|
-
# Otherwise returns nil.
|
225
|
-
def get_dialout_region(string, region)
|
226
|
-
region ||= PhoneNumber.default_region
|
227
|
-
dialout_prefix = get_dialout_prefix(string, region)
|
228
|
-
dialout_region = nil
|
229
|
-
|
230
|
-
unless dialout_prefix.empty?
|
231
|
-
# region codes are 1 to 3 digits
|
232
|
-
range_end = [string.length-dialout_prefix.delete(' ').length, 3].min
|
233
|
-
|
234
|
-
(1..range_end).each do |i|
|
235
|
-
dialout_region = Region.find(string[dialout_prefix.delete(' ').length, i])
|
236
|
-
break if dialout_region
|
80
|
+
# Find and return the first rule that matches
|
81
|
+
matching_rule = rules.find { |rule| rule.matches? number }
|
82
|
+
return matching_rule unless matching_rule.nil?
|
237
83
|
end
|
84
|
+
|
85
|
+
return nil
|
238
86
|
end
|
239
|
-
|
240
|
-
dialout_region
|
241
|
-
end
|
242
87
|
end
|
243
88
|
end
|
244
|
-
|
245
89
|
end
|