global_phone_dbgen 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6fb0537a5d1364a57071fb8d11787f7e593b2a96
4
+ data.tar.gz: a3e71b3fb80e5de63c27cd1ced8ad4c7ad986c90
5
+ SHA512:
6
+ metadata.gz: ab2f6eeeb4d51582aaba0baea5a83bbf139ddc90eeb7d1260c51cd17cda6b41c7aa34ba62d60b4a248c316d5ebfb6463ec54886febfe78b95cd601a0b6badad7
7
+ data.tar.gz: 340ff6c3986a361a06f8d2ea4bccaa005bbb34dca99351c269af8f5b0424472f0697b4454e45a8514489f2df1736ecda7ccb8a110d41ca68a0c3ae314e48f107
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.
@@ -0,0 +1,135 @@
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 format (`+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
+ require 'global_phone'
26
+ GlobalPhone.db_path = Rails.root.join('db/global_phone.json')
27
+
28
+ ## Examples
29
+
30
+ Parse an international number string into a `GlobalPhone::Number` object:
31
+
32
+ >> number = GlobalPhone.parse('+1-312-555-1212')
33
+ => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=1 name=US> national_string="3125551212">
34
+
35
+ Query the country code and likely territory name of the number:
36
+
37
+ >> number.country_code
38
+ => "1"
39
+
40
+ >> number.territory.name
41
+ => "US"
42
+
43
+ Present the number in national and international formats:
44
+
45
+ >> number.national_format
46
+ => "(312) 555-1212"
47
+
48
+ >> number.international_format
49
+ => "+1 312-555-1212"
50
+
51
+ 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.)
52
+
53
+ >> number.valid?
54
+ true
55
+
56
+ Get the number's normalized E.164 international string:
57
+
58
+ >> number.international_string
59
+ => "+13125551212"
60
+
61
+ Parse a number in national format for a given territory:
62
+
63
+ >> number = GlobalPhone.parse("(0) 20-7031-3000", :gb)
64
+ => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=44 name=GB> national_string="2070313000">
65
+
66
+ Parse an international number using a territory's international dialing prefix:
67
+
68
+ >> number = GlobalPhone.parse("00 1 3125551212", :gb)
69
+ => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=1 name=US> national_string="3125551212">
70
+
71
+ 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):
72
+
73
+ >> GlobalPhone.default_territory_name = :gb
74
+ => :gb
75
+
76
+ >> GlobalPhone.parse("(0) 20-7031-3000")
77
+ => #<GlobalPhone::Number territory=#<GlobalPhone::Territory country_code=44 name=GB> national_string="2070313000">
78
+
79
+ Shortcuts for validating a phone number:
80
+
81
+ >> GlobalPhone.validate("+1 312-555-1212")
82
+ => true
83
+
84
+ >> GlobalPhone.validate("+442070313000")
85
+ => true
86
+
87
+ >> GlobalPhone.validate("(0) 20-7031-3000")
88
+ => false
89
+
90
+ >> GlobalPhone.validate("(0) 20-7031-3000", :gb)
91
+ => true
92
+
93
+ Shortcuts for normalizing a phone number in E.164 format:
94
+
95
+ >> GlobalPhone.normalize("(312) 555-1212")
96
+ => "+13125551212"
97
+
98
+ >> GlobalPhone.normalize("+442070313000")
99
+ => "+442070313000"
100
+
101
+ >> GlobalPhone.normalize("(0) 20-7031-3000")
102
+ => nil
103
+
104
+ >> GlobalPhone.normalize("(0) 20-7031-3000", :gb)
105
+ => "+442070313000"
106
+
107
+ ## Caveats
108
+
109
+ GlobalPhone currently does not parse emergency numbers or SMS short code numbers.
110
+
111
+ Validation is not definitive and may return false positives, but *should not* return false negatives unless the database is out of date.
112
+
113
+ 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.)
114
+
115
+ ## Development
116
+
117
+ 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:
118
+
119
+ $ git clone https://github.com/sstephenson/global_phone.git
120
+
121
+ 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.
122
+
123
+ GlobalPhone is heavily inspired by Andreas Gal's [PhoneNumber.js](https://github.com/andreasgal/PhoneNumber.js) library.
124
+
125
+ ### Version History
126
+
127
+ **1.0.0** (May 28, 2013)
128
+
129
+ * Initial public release.
130
+
131
+ ### License
132
+
133
+ Copyright &copy; 2013 Sam Stephenson
134
+
135
+ Released under the MIT license. See [`LICENSE`](LICENSE) for details.
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ require 'global_phone/database_generator'
3
+ require 'open-uri'
4
+
5
+ REMOTE_URL = "http://libphonenumber.googlecode.com/svn/trunk/resources/PhoneNumberMetaData.xml"
6
+
7
+ def usage
8
+ warn "Usage: #$0 [--compact] [--test] [<filename> | <url>]"
9
+ end
10
+
11
+ def help
12
+ warn <<-EOS
13
+
14
+ Generates a database for the Ruby GlobalNumber library in JSON format
15
+ and writes it to standard output.
16
+
17
+ Specify either a local path or URL pointing to a copy of Google's
18
+ libphonenumber PhoneNumberMetaData.xml file.
19
+
20
+ Omit the filename argument to download and use the latest version of
21
+ Google's database from:
22
+ #{REMOTE_URL}
23
+
24
+ Options:
25
+ --compact Strip all whitespace from the JSON output
26
+ --test Generate example phone number fixtures for smoke tests
27
+
28
+ EOS
29
+ end
30
+
31
+ path = REMOTE_URL
32
+ method = :record_data
33
+ compact = false
34
+
35
+ while arg = ARGV.shift
36
+ case arg
37
+ when "-c", "--compact"
38
+ compact = true
39
+ when "-t", "--test"
40
+ method = :test_cases
41
+ when "-h", "--help"
42
+ usage
43
+ help
44
+ exit
45
+ when /^-/
46
+ warn "#$0: unknown option `#{arg}'"
47
+ usage
48
+ exit 1
49
+ else
50
+ path = arg
51
+ break
52
+ end
53
+ end
54
+
55
+ generator = GlobalPhone::DatabaseGenerator.load(open(path).read)
56
+ result = generator.send(method)
57
+
58
+ if compact
59
+ puts JSON.generate(result)
60
+ else
61
+ puts JSON.pretty_generate(result)
62
+ end
@@ -0,0 +1,149 @@
1
+ require 'json'
2
+ require 'nokogiri'
3
+
4
+ module GlobalPhone
5
+ class DatabaseGenerator
6
+ VERSION = '1.0.0'
7
+
8
+ def self.load_file(filename)
9
+ load(File.read(filename))
10
+ end
11
+
12
+ def self.load(xml)
13
+ new(Nokogiri.XML(xml))
14
+ end
15
+
16
+ attr_reader :doc
17
+
18
+ def initialize(doc)
19
+ @doc = doc
20
+ end
21
+
22
+ def test_cases
23
+ @test_cases ||= territory_nodes.map do |node|
24
+ example_numbers_for_territory_node(node)
25
+ end.flatten(1)
26
+ end
27
+
28
+ def record_data
29
+ @record_data ||= territory_nodes_by_region.map do |country_code, territory_nodes|
30
+ truncate(compile_region(territory_nodes, country_code))
31
+ end
32
+ end
33
+
34
+ def inspect
35
+ "#<#{self.class.name} (#{doc.search("*").size} elements)>"
36
+ end
37
+
38
+ protected
39
+ def territory_nodes
40
+ doc.search("territory")
41
+ end
42
+
43
+ def territory_nodes_by_region
44
+ territory_nodes.group_by { |node| node["countryCode"] }
45
+ end
46
+
47
+ def territory_name(node)
48
+ node["id"]
49
+ end
50
+
51
+ def example_numbers_for_territory_node(node)
52
+ name = territory_name(node)
53
+ return [] if name == "001"
54
+ node.search(example_numbers_selector).map { |node| [node.text, name] }
55
+ end
56
+
57
+ def example_numbers_selector
58
+ "./*[not(" + example_number_types_to_exclude.map do |type|
59
+ "self::#{type}"
60
+ end.join(" or ") + ")]/exampleNumber"
61
+ end
62
+
63
+ def example_number_types_to_exclude
64
+ %w( emergency shortCode )
65
+ end
66
+
67
+ def compile_region(territory_nodes, country_code)
68
+ territories, main_territory_node = compile_territories(territory_nodes)
69
+ formats = compile_formats(territory_nodes)
70
+
71
+ [
72
+ country_code,
73
+ formats,
74
+ territories,
75
+ main_territory_node["internationalPrefix"],
76
+ main_territory_node["nationalPrefix"],
77
+ squish(main_territory_node["nationalPrefixForParsing"]),
78
+ squish(main_territory_node["nationalPrefixTransformRule"])
79
+ ]
80
+ end
81
+
82
+ def compile_territories(territory_nodes)
83
+ territories = []
84
+ main_territory_node = territory_nodes.first
85
+
86
+ territory_nodes.each do |node|
87
+ territory = truncate(compile_territory(node))
88
+ if node["mainCountryForCode"]
89
+ main_territory_node = node
90
+ territories.unshift(territory)
91
+ else
92
+ territories.push(territory)
93
+ end
94
+ end
95
+
96
+ [territories, main_territory_node]
97
+ end
98
+
99
+ def compile_territory(node)
100
+ [
101
+ territory_name(node),
102
+ pattern(node, "generalDesc possibleNumberPattern"),
103
+ pattern(node, "generalDesc nationalNumberPattern"),
104
+ squish(node["nationalPrefixFormattingRule"])
105
+ ]
106
+ end
107
+
108
+ def compile_formats(territory_nodes)
109
+ format_nodes_for(territory_nodes).map do |node|
110
+ truncate(compile_format(node))
111
+ end
112
+ end
113
+
114
+ def compile_format(node)
115
+ [
116
+ node["pattern"],
117
+ text_or_nil(node, "format"),
118
+ pattern(node, "leadingDigits"),
119
+ node["nationalPrefixFormattingRule"],
120
+ text_or_nil(node, "intlFormat")
121
+ ]
122
+ end
123
+
124
+ def format_nodes_for(territory_nodes)
125
+ territory_nodes.map do |node|
126
+ node.search("availableFormats numberFormat").to_a
127
+ end.flatten
128
+ end
129
+
130
+ def truncate(array)
131
+ array.dup.tap do |result|
132
+ result.pop while result.any? && result.last.nil?
133
+ end
134
+ end
135
+
136
+ def squish(string)
137
+ string.gsub(/\s+/, "") if string
138
+ end
139
+
140
+ def pattern(node, selector)
141
+ squish(text_or_nil(node, selector))
142
+ end
143
+
144
+ def text_or_nil(node, selector)
145
+ nodes = node.search(selector)
146
+ nodes.empty? ? nil : nodes.text
147
+ end
148
+ end
149
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: global_phone_dbgen
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sam Stephenson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ description: Provides a global_phone_dbgen command to generate databases for the GlobalPhone
28
+ library.
29
+ email:
30
+ - sstephenson@gmail.com
31
+ executables:
32
+ - global_phone_dbgen
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - README.md
37
+ - LICENSE
38
+ - lib/global_phone/database_generator.rb
39
+ - bin/global_phone_dbgen
40
+ homepage: https://github.com/sstephenson/global_phone
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.0.0
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Generate databases for use with the GlobalPhone library
64
+ test_files: []