globalphone 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +163 -0
- data/lib/global_phone.rb +10 -0
- data/lib/global_phone/context.rb +36 -0
- data/lib/global_phone/database.rb +48 -0
- data/lib/global_phone/format.rb +27 -0
- data/lib/global_phone/number.rb +91 -0
- data/lib/global_phone/parsing.rb +52 -0
- data/lib/global_phone/record.rb +26 -0
- data/lib/global_phone/region.rb +62 -0
- data/lib/global_phone/territory.rb +58 -0
- data/lib/global_phone/utils.rb +14 -0
- metadata +60 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Sam Stephenson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
# GlobalPhone
|
2
|
+
|
3
|
+
GlobalPhone parses, validates, and formats local and international phone numbers according to the [E.164 standard](http://en.wikipedia.org/wiki/E.164).
|
4
|
+
|
5
|
+
**Store and display phone numbers in your app.** Accept phone number input in national or international format. Convert phone numbers to international strings (`+13125551212`) for storage and retrieval. Present numbers in national format (`(312) 555-1212`) in your UI.
|
6
|
+
|
7
|
+
**Designed with the future in mind.** GlobalPhone uses format specifications from Google's open-source [libphonenumber](http://code.google.com/p/libphonenumber/) database. No need to upgrade the library when a new phone format is introduced—just generate a new copy of the database and check it into your app.
|
8
|
+
|
9
|
+
**Pure Ruby. No dependencies.** GlobalPhone is designed for Ruby 1.9.3 and up. (Works in 1.8.7, too—just bring your own `json` gem.)
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
1. Add the `global_phone` gem to your app. For example, using Bundler:
|
14
|
+
|
15
|
+
$ echo "gem 'global_phone'" >> Gemfile
|
16
|
+
$ bundle install
|
17
|
+
|
18
|
+
2. Use `global_phone_dbgen` to convert Google's libphonenumber `PhoneNumberMetaData.xml` file into a JSON database for GlobalPhone.
|
19
|
+
|
20
|
+
$ gem install global_phone_dbgen
|
21
|
+
$ global_phone_dbgen > db/global_phone.json
|
22
|
+
|
23
|
+
3. Tell GlobalPhone where to find the database. For example, in a Rails app, create an initializer in `config/initializers/global_phone.rb`:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'global_phone'
|
27
|
+
GlobalPhone.db_path = Rails.root.join('db/global_phone.json')
|
28
|
+
```
|
29
|
+
|
30
|
+
## Examples
|
31
|
+
|
32
|
+
Parse an international number string into a `GlobalPhone::Number` object:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
number = GlobalPhone.parse('+1-312-555-1212')
|
36
|
+
# => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=1 name=US> national_string="3125551212">
|
37
|
+
```
|
38
|
+
|
39
|
+
Query the country code and likely territory name of the number:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
number.country_code
|
43
|
+
# => "1"
|
44
|
+
|
45
|
+
number.territory.name
|
46
|
+
# => "US"
|
47
|
+
```
|
48
|
+
|
49
|
+
Present the number in national and international formats:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
number.national_format
|
53
|
+
# => "(312) 555-1212"
|
54
|
+
|
55
|
+
number.international_format
|
56
|
+
# => "+1 312-555-1212"
|
57
|
+
```
|
58
|
+
|
59
|
+
Is the number valid? (Note: this is not definitive. For example, the number here is "valid" by format, but there are no US numbers that start with 555. The `valid?` method may return false positives, but *should not* return false negatives unless the database is out of date.)
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
number.valid?
|
63
|
+
# => true
|
64
|
+
```
|
65
|
+
|
66
|
+
Get the number's normalized E.164 international string:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
number.international_string
|
70
|
+
# => "+13125551212"
|
71
|
+
```
|
72
|
+
|
73
|
+
Parse a number in national format for a given territory:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
number = GlobalPhone.parse("(0) 20-7031-3000", :gb)
|
77
|
+
# => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=44 name=GB> national_string="2070313000">
|
78
|
+
```
|
79
|
+
|
80
|
+
Parse an international number using a territory's international dialing prefix:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
number = GlobalPhone.parse("00 1 3125551212", :gb)
|
84
|
+
# => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=1 name=US> national_string="3125551212">
|
85
|
+
```
|
86
|
+
|
87
|
+
Set the default territory to Great Britain (territory names are [ISO 3166-1 Alpha-2](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) codes):
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
GlobalPhone.default_territory_name = :gb
|
91
|
+
# => :gb
|
92
|
+
|
93
|
+
GlobalPhone.parse("(0) 20-7031-3000")
|
94
|
+
# => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=44 name=GB> national_string="2070313000">
|
95
|
+
```
|
96
|
+
|
97
|
+
Shortcuts for validating a phone number:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
GlobalPhone.validate("+1 312-555-1212")
|
101
|
+
# => true
|
102
|
+
|
103
|
+
GlobalPhone.validate("+442070313000")
|
104
|
+
# => true
|
105
|
+
|
106
|
+
GlobalPhone.validate("(0) 20-7031-3000")
|
107
|
+
# => false
|
108
|
+
|
109
|
+
GlobalPhone.validate("(0) 20-7031-3000", :gb)
|
110
|
+
# => true
|
111
|
+
```
|
112
|
+
|
113
|
+
Shortcuts for normalizing a phone number in E.164 format:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
GlobalPhone.normalize("(312) 555-1212")
|
117
|
+
# => "+13125551212"
|
118
|
+
|
119
|
+
GlobalPhone.normalize("+442070313000")
|
120
|
+
# => "+442070313000"
|
121
|
+
|
122
|
+
GlobalPhone.normalize("(0) 20-7031-3000")
|
123
|
+
# => nil
|
124
|
+
|
125
|
+
GlobalPhone.normalize("(0) 20-7031-3000", :gb)
|
126
|
+
# => "+442070313000"
|
127
|
+
```
|
128
|
+
|
129
|
+
## Caveats
|
130
|
+
|
131
|
+
GlobalPhone currently does not parse emergency numbers or SMS short code numbers.
|
132
|
+
|
133
|
+
Validation is not definitive and may return false positives, but *should not* return false negatives unless the database is out of date.
|
134
|
+
|
135
|
+
Territory heuristics are imprecise. Parsing a number will usually result in the territory being set to the primary territory of the region. For example, Canadian numbers will be parsed with a territory of `US`. (In most cases this does not matter, but if your application needs to perform geolocation using phone numbers, GlobalPhone may not be a good fit.)
|
136
|
+
|
137
|
+
## Development
|
138
|
+
|
139
|
+
The GlobalPhone source code is [hosted on GitHub](https://github.com/sstephenson/global_phone). You can check out a copy of the latest code using Git:
|
140
|
+
|
141
|
+
$ git clone https://github.com/sstephenson/global_phone.git
|
142
|
+
|
143
|
+
If you've found a bug or have a question, please open an issue on the [issue tracker](https://github.com/sstephenson/global_phone/issues). Or, clone the GlobalPhone repository, write a failing test case, fix the bug, and submit a pull request.
|
144
|
+
|
145
|
+
GlobalPhone is heavily inspired by Andreas Gal's [PhoneNumber.js](https://github.com/andreasgal/PhoneNumber.js) library.
|
146
|
+
|
147
|
+
### Version History
|
148
|
+
|
149
|
+
**1.0.1** (May 29, 2013)
|
150
|
+
|
151
|
+
* GlobalPhone::Number#to_s returns the E.164 international string.
|
152
|
+
* Ensure GlobalPhone::Number always returns strings for #national_format, #international_format, and #international_string, regardless of validity.
|
153
|
+
* Relax format restrictions to more loosely match available national number patterns.
|
154
|
+
|
155
|
+
**1.0.0** (May 28, 2013)
|
156
|
+
|
157
|
+
* Initial public release.
|
158
|
+
|
159
|
+
### License
|
160
|
+
|
161
|
+
Copyright © 2013 Sam Stephenson
|
162
|
+
|
163
|
+
Released under the MIT license. See [`LICENSE`](LICENSE) for details.
|
data/lib/global_phone.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'global_phone/database'
|
2
|
+
|
3
|
+
module GlobalPhone
|
4
|
+
module Context
|
5
|
+
attr_accessor :db_path
|
6
|
+
|
7
|
+
def db
|
8
|
+
@db ||= begin
|
9
|
+
raise NoDatabaseError, "set `db_path=' first" unless db_path
|
10
|
+
Database.load_file(db_path)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_territory_name
|
15
|
+
@default_territory_name ||= :US
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_territory_name=(territory_name)
|
19
|
+
@default_territory_name = territory_name.to_s.intern
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(string, territory_name = default_territory_name)
|
23
|
+
db.parse(string, territory_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def normalize(string, territory_name = default_territory_name)
|
27
|
+
number = parse(string, territory_name)
|
28
|
+
number.international_string if number
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate(string, territory_name = default_territory_name)
|
32
|
+
number = parse(string, territory_name)
|
33
|
+
number && number.valid?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'global_phone/parsing'
|
2
|
+
require 'global_phone/region'
|
3
|
+
|
4
|
+
module GlobalPhone
|
5
|
+
class Database
|
6
|
+
include Parsing
|
7
|
+
|
8
|
+
def self.load_file(filename)
|
9
|
+
load(File.read(filename))
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.load(json)
|
13
|
+
require 'json'
|
14
|
+
new(JSON.parse(json))
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :regions
|
18
|
+
|
19
|
+
def initialize(record_data)
|
20
|
+
@regions = record_data.map { |data| Region.new(data) }
|
21
|
+
@territories_by_name = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def region(country_code)
|
25
|
+
regions_by_country_code[country_code.to_s]
|
26
|
+
end
|
27
|
+
|
28
|
+
def territory(name)
|
29
|
+
name = name.to_s.upcase
|
30
|
+
@territories_by_name[name] ||= if region = region_for_territory(name)
|
31
|
+
region.territory(name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
"#<#{self.class.name}>"
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
def regions_by_country_code
|
41
|
+
@regions_by_country_code ||= Hash[*regions.map { |r| [r.country_code, r] }.flatten]
|
42
|
+
end
|
43
|
+
|
44
|
+
def region_for_territory(name)
|
45
|
+
regions.find { |r| r.has_territory?(name) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'global_phone/record'
|
2
|
+
|
3
|
+
module GlobalPhone
|
4
|
+
class Format < Record
|
5
|
+
field 0, :pattern do |p| /^#{p}$/ end
|
6
|
+
field 1, :national_format_rule
|
7
|
+
field 2, :leading_digits do |d| /^#{d}/ end
|
8
|
+
field 3, :national_prefix_formatting_rule
|
9
|
+
field 4, :international_format_rule, :fallback => :national_format_rule
|
10
|
+
|
11
|
+
def match(national_string, match_leading_digits = true)
|
12
|
+
return false if match_leading_digits && leading_digits && national_string !~ leading_digits
|
13
|
+
national_string =~ pattern
|
14
|
+
end
|
15
|
+
|
16
|
+
def format_replacement_string(type)
|
17
|
+
format_rule = send(:"#{type}_format_rule")
|
18
|
+
format_rule.to_s.gsub("$", "\\") unless format_rule == "NA"
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply(national_string, type)
|
22
|
+
if replacement = format_replacement_string(type)
|
23
|
+
national_string.gsub(pattern, replacement)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module GlobalPhone
|
4
|
+
class Number
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
E161_MAPPING = Hash[*"a2b2c2d3e3f3g4h4i4j5k5l5m6n6o6p7q7r7s7t8u8v8w9x9y9z9".split("")]
|
8
|
+
VALID_ALPHA_CHARS = /[a-zA-Z]/
|
9
|
+
LEADING_PLUS_CHARS = /^\++/
|
10
|
+
NON_DIALABLE_CHARS = /[^,#+\*\d]/
|
11
|
+
SPLIT_FIRST_GROUP = /^(\d+)(.*)$/
|
12
|
+
|
13
|
+
def self.normalize(string)
|
14
|
+
string.to_s.
|
15
|
+
gsub(VALID_ALPHA_CHARS) { |c| E161_MAPPING[c.downcase] }.
|
16
|
+
gsub(LEADING_PLUS_CHARS, "+").
|
17
|
+
gsub(NON_DIALABLE_CHARS, "")
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :territory, :national_string
|
21
|
+
|
22
|
+
def_delegator :territory, :region
|
23
|
+
def_delegator :territory, :country_code
|
24
|
+
def_delegator :territory, :national_prefix
|
25
|
+
def_delegator :territory, :national_pattern
|
26
|
+
|
27
|
+
def initialize(territory, national_string)
|
28
|
+
@territory = territory
|
29
|
+
@national_string = national_string
|
30
|
+
end
|
31
|
+
|
32
|
+
def national_format
|
33
|
+
@national_format ||= begin
|
34
|
+
if format && result = format.apply(national_string, :national)
|
35
|
+
apply_national_prefix_format(result)
|
36
|
+
else
|
37
|
+
national_string
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def international_string
|
43
|
+
@international_string ||= international_format.gsub(NON_DIALABLE_CHARS, "")
|
44
|
+
end
|
45
|
+
|
46
|
+
def international_format
|
47
|
+
@international_format ||= begin
|
48
|
+
if format && formatted_number = format.apply(national_string, :international)
|
49
|
+
"+#{country_code} #{formatted_number}"
|
50
|
+
else
|
51
|
+
"+#{country_code} #{national_string}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid?
|
57
|
+
!!(format && national_string =~ national_pattern)
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
"#<#{self.class.name} territory=#{territory.inspect} national_string=#{national_string.inspect}>"
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
international_string
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
def format
|
70
|
+
@format ||= find_format_for(national_string)
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_format_for(string)
|
74
|
+
region.formats.detect { |format| format.match(string) } ||
|
75
|
+
region.formats.detect { |format| format.match(string, false) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def apply_national_prefix_format(result)
|
79
|
+
prefix = national_prefix_formatting_rule
|
80
|
+
return result unless prefix && match = result.match(SPLIT_FIRST_GROUP)
|
81
|
+
|
82
|
+
prefix = prefix.gsub("$NP", national_prefix)
|
83
|
+
prefix = prefix.gsub("$FG", match[1])
|
84
|
+
result = "#{prefix} #{match[2]}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def national_prefix_formatting_rule
|
88
|
+
format.national_prefix_formatting_rule || territory.national_prefix_formatting_rule
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'global_phone/number'
|
2
|
+
require 'global_phone/utils'
|
3
|
+
|
4
|
+
module GlobalPhone
|
5
|
+
module Parsing
|
6
|
+
def parse(string, territory_name)
|
7
|
+
string = Number.normalize(string)
|
8
|
+
territory = self.territory(territory_name)
|
9
|
+
raise ArgumentError, "unknown territory `#{territory_name}'" unless territory
|
10
|
+
|
11
|
+
if starts_with_plus?(string)
|
12
|
+
parse_international_string(string)
|
13
|
+
elsif string =~ territory.international_prefix
|
14
|
+
string = strip_international_prefix(territory, string)
|
15
|
+
parse_international_string(string)
|
16
|
+
else
|
17
|
+
territory.parse_national_string(string)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_international_string(string)
|
22
|
+
string = Number.normalize(string)
|
23
|
+
string = strip_leading_plus(string) if starts_with_plus?(string)
|
24
|
+
|
25
|
+
if region = region_for_string(string)
|
26
|
+
region.parse_national_string(string)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
def starts_with_plus?(string)
|
32
|
+
string[0, 1] == "+"
|
33
|
+
end
|
34
|
+
|
35
|
+
def strip_leading_plus(string)
|
36
|
+
string[1..-1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def strip_international_prefix(territory, string)
|
40
|
+
string.sub(territory.international_prefix, "")
|
41
|
+
end
|
42
|
+
|
43
|
+
def region_for_string(string)
|
44
|
+
candidates = country_code_candidates_for(string)
|
45
|
+
Utils.map_detect(candidates) { |country_code| region(country_code) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def country_code_candidates_for(string)
|
49
|
+
(1..3).map { |length| string[0, length] }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module GlobalPhone
|
4
|
+
class Record
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def self.field(index, name, options = {}, &block)
|
8
|
+
if block
|
9
|
+
transform_method_name = :"transform_field_#{name}"
|
10
|
+
define_method(transform_method_name, block)
|
11
|
+
end
|
12
|
+
|
13
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
14
|
+
def #{name}
|
15
|
+
value = @data[#{index.inspect}]
|
16
|
+
#{"value = #{transform_method_name}(value) if value" if block}
|
17
|
+
value #{"|| #{options[:fallback]}" if options[:fallback]}
|
18
|
+
end
|
19
|
+
RUBY
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(data)
|
23
|
+
@data = data
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'global_phone/format'
|
2
|
+
require 'global_phone/record'
|
3
|
+
require 'global_phone/territory'
|
4
|
+
require 'global_phone/utils'
|
5
|
+
|
6
|
+
module GlobalPhone
|
7
|
+
class Region < Record
|
8
|
+
field 0, :country_code
|
9
|
+
field 1, :format_record_data
|
10
|
+
field 2, :territory_record_data
|
11
|
+
field 3, :international_prefix do |p| /^(?:#{p})/ end
|
12
|
+
field 4, :national_prefix
|
13
|
+
field 5, :national_prefix_for_parsing do |p| /^(?:#{p})/ end
|
14
|
+
field 6, :national_prefix_transform_rule
|
15
|
+
|
16
|
+
def formats
|
17
|
+
@formats ||= format_record_data.map { |data| Format.new(data) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def territories
|
21
|
+
@territories ||= territory_record_data.map { |data| Territory.new(data, self) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def territory(name)
|
25
|
+
name = name.to_s.upcase
|
26
|
+
territories.detect { |region| region.name == name }
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_territory?(name)
|
30
|
+
territory_names.include?(name.to_s.upcase)
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_national_string(string)
|
34
|
+
string = Number.normalize(string)
|
35
|
+
if starts_with_country_code?(string)
|
36
|
+
string = strip_country_code(string)
|
37
|
+
find_first_parsed_national_string_from_territories(string)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
"#<#{self.class.name} country_code=#{country_code} territories=[#{territory_names.join(",")}]>"
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
def territory_names
|
47
|
+
territory_record_data.map(&:first)
|
48
|
+
end
|
49
|
+
|
50
|
+
def starts_with_country_code?(string)
|
51
|
+
string.index(country_code) == 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def strip_country_code(string)
|
55
|
+
string[country_code.length..-1]
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_first_parsed_national_string_from_territories(string)
|
59
|
+
Utils.map_detect(territories) { |territory| territory.parse_national_string(string) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'global_phone/number'
|
2
|
+
require 'global_phone/record'
|
3
|
+
|
4
|
+
module GlobalPhone
|
5
|
+
class Territory < Record
|
6
|
+
field 0, :name
|
7
|
+
field 1, :possible_pattern do |p| /^#{p}$/ end
|
8
|
+
field 2, :national_pattern do |p| /^#{p}$/ end
|
9
|
+
field 3, :national_prefix_formatting_rule
|
10
|
+
|
11
|
+
attr_reader :region
|
12
|
+
|
13
|
+
def_delegator :region, :country_code
|
14
|
+
def_delegator :region, :international_prefix
|
15
|
+
def_delegator :region, :national_prefix
|
16
|
+
def_delegator :region, :national_prefix_for_parsing
|
17
|
+
def_delegator :region, :national_prefix_transform_rule
|
18
|
+
|
19
|
+
def initialize(data, region)
|
20
|
+
super(data)
|
21
|
+
@region = region
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_national_string(string)
|
25
|
+
string = normalize(string)
|
26
|
+
Number.new(self, string) if possible?(string)
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"#<#{self.class.name} country_code=#{country_code} name=#{name}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
def strip_national_prefix(string)
|
35
|
+
if national_prefix_for_parsing
|
36
|
+
transform_rule = national_prefix_transform_rule || ""
|
37
|
+
transform_rule = transform_rule.gsub("$", "\\")
|
38
|
+
string_without_prefix = string.sub(national_prefix_for_parsing, transform_rule)
|
39
|
+
elsif starts_with_national_prefix?(string)
|
40
|
+
string_without_prefix = string[national_prefix.length..-1]
|
41
|
+
end
|
42
|
+
|
43
|
+
possible?(string_without_prefix) ? string_without_prefix : string
|
44
|
+
end
|
45
|
+
|
46
|
+
def normalize(string)
|
47
|
+
strip_national_prefix(Number.normalize(string))
|
48
|
+
end
|
49
|
+
|
50
|
+
def possible?(string)
|
51
|
+
string =~ possible_pattern
|
52
|
+
end
|
53
|
+
|
54
|
+
def starts_with_national_prefix?(string)
|
55
|
+
national_prefix && string.index(national_prefix) == 0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: globalphone
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sam Stephenson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-03-01 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: GlobalPhone parses, validates, and formats local and international phone
|
15
|
+
numbers according to the E.164 standard using the rules specified in Google's libphonenumber
|
16
|
+
database.
|
17
|
+
email:
|
18
|
+
- sstephenson@gmail.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- README.md
|
24
|
+
- LICENSE
|
25
|
+
- lib/global_phone/context.rb
|
26
|
+
- lib/global_phone/database.rb
|
27
|
+
- lib/global_phone/format.rb
|
28
|
+
- lib/global_phone/number.rb
|
29
|
+
- lib/global_phone/parsing.rb
|
30
|
+
- lib/global_phone/record.rb
|
31
|
+
- lib/global_phone/region.rb
|
32
|
+
- lib/global_phone/territory.rb
|
33
|
+
- lib/global_phone/utils.rb
|
34
|
+
- lib/global_phone.rb
|
35
|
+
homepage: https://github.com/Lundalogik/globalphone_fork
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.8.24
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: Parse, validate, and format phone numbers using Google's libphonenumber database
|
60
|
+
test_files: []
|