global_phone_dbgen 1.0.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.
@@ -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: []