phonejack 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.hound.yml +4 -0
- data/.rubocop.default.yml +1632 -0
- data/.rubocop.yml +9 -0
- data/.travis.yml +12 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +191 -0
- data/Rakefile +26 -0
- data/bin/console +12 -0
- data/bin/setup +8 -0
- data/data/telephone_number_data_file.dat +0 -0
- data/data/telephone_number_data_file.xml +26498 -0
- data/lib/active_model/phonejack_validator.rb +9 -0
- data/lib/phonejack.rb +17 -0
- data/lib/phonejack/class_methods.rb +40 -0
- data/lib/phonejack/country.rb +67 -0
- data/lib/phonejack/data_importer.rb +96 -0
- data/lib/phonejack/formatter.rb +76 -0
- data/lib/phonejack/number.rb +17 -0
- data/lib/phonejack/number_format.rb +14 -0
- data/lib/phonejack/number_validation.rb +10 -0
- data/lib/phonejack/parser.rb +68 -0
- data/lib/phonejack/test_data_generator.rb +116 -0
- data/lib/phonejack/version.rb +3 -0
- data/lib/utilities/hash.rb +47 -0
- data/phonejack.gemspec +31 -0
- metadata +199 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
class PhonejackValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
country = options[:country].call(record) if options.key?(:country)
|
4
|
+
valid_types = options.fetch(:types, [])
|
5
|
+
args = [value, country, valid_types]
|
6
|
+
|
7
|
+
record.errors.add(attribute, :invalid) if Phonejack.invalid?(*args)
|
8
|
+
end
|
9
|
+
end
|
data/lib/phonejack.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'phonejack/version'
|
2
|
+
require 'utilities/hash'
|
3
|
+
require 'active_model/phonejack_validator' if defined?(ActiveModel)
|
4
|
+
|
5
|
+
module Phonejack
|
6
|
+
autoload :DataImporter, 'phonejack/data_importer'
|
7
|
+
autoload :TestDataGenerator, 'phonejack/test_data_generator'
|
8
|
+
autoload :Parser, 'phonejack/parser'
|
9
|
+
autoload :Number, 'phonejack/number'
|
10
|
+
autoload :Formatter, 'phonejack/formatter'
|
11
|
+
autoload :Country, 'phonejack/country'
|
12
|
+
autoload :NumberFormat, 'phonejack/number_format'
|
13
|
+
autoload :NumberValidation, 'phonejack/number_validation'
|
14
|
+
autoload :ClassMethods, 'phonejack/class_methods'
|
15
|
+
|
16
|
+
extend ClassMethods
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Phonejack
|
2
|
+
module ClassMethods
|
3
|
+
attr_accessor :override_file, :default_format_string
|
4
|
+
attr_reader :default_format_pattern
|
5
|
+
|
6
|
+
def default_format_pattern=(format_string)
|
7
|
+
@default_format_pattern = Regexp.new(format_string)
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(number, country = detect_country(number))
|
11
|
+
Phonejack::Number.new(sanitize(number), country)
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?(number, country = detect_country(number), keys = [])
|
15
|
+
parse(number, country).valid?(keys)
|
16
|
+
end
|
17
|
+
|
18
|
+
def invalid?(*args)
|
19
|
+
!valid?(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def sanitize(input_number)
|
23
|
+
input_number.to_s.gsub(/\D/, '')
|
24
|
+
end
|
25
|
+
|
26
|
+
def detect_country(number)
|
27
|
+
sanitized_number = sanitize(number)
|
28
|
+
detected_country = Country.all_countries.detect do |country|
|
29
|
+
sanitized_number.start_with?(country.country_code) && valid?(sanitized_number, country.country_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
detected_country.country_id.to_sym if detected_country
|
33
|
+
end
|
34
|
+
|
35
|
+
# generates binary file from xml that user gives us
|
36
|
+
def generate_override_file(file)
|
37
|
+
DataImporter.new(file, override: true).import!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Phonejack
|
2
|
+
class Country
|
3
|
+
attr_reader :country_code, :national_prefix, :national_prefix_formatting_rule,
|
4
|
+
:national_prefix_for_parsing, :national_prefix_transform_rule, :international_prefix,
|
5
|
+
:formats, :validations, :mobile_token, :country_id, :general_validation, :main_country_for_code
|
6
|
+
|
7
|
+
MOBILE_TOKEN_COUNTRIES = { AR: '9' }
|
8
|
+
|
9
|
+
def initialize(data_hash)
|
10
|
+
@country_code = data_hash[:country_code]
|
11
|
+
@country_id = data_hash[:id]
|
12
|
+
@formats = data_hash.fetch(:formats, []).map { |format| NumberFormat.new(format) }
|
13
|
+
@general_validation = NumberValidation.new(:general_desc, data_hash[:validations][:general_desc]) if data_hash.fetch(:validations, {})[:general_desc]
|
14
|
+
@international_prefix = Regexp.new(data_hash[:international_prefix]) if data_hash[:international_prefix]
|
15
|
+
@main_country_for_code = data_hash[:main_country_for_code] == 'true'
|
16
|
+
@mobile_token = MOBILE_TOKEN_COUNTRIES[@country_id.to_sym]
|
17
|
+
@national_prefix = data_hash[:national_prefix]
|
18
|
+
@national_prefix_formatting_rule = data_hash[:national_prefix_formatting_rule]
|
19
|
+
@national_prefix_for_parsing = Regexp.new(data_hash[:national_prefix_for_parsing]) if data_hash[:national_prefix_for_parsing]
|
20
|
+
@national_prefix_transform_rule = data_hash[:national_prefix_transform_rule]
|
21
|
+
@validations = data_hash.fetch(:validations, {})
|
22
|
+
.except(:general_desc, :area_code_optional)
|
23
|
+
.map { |name, data| NumberValidation.new(name, data) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def detect_format(number)
|
27
|
+
native_format = formats.detect do |format|
|
28
|
+
number =~ Regexp.new("^(#{format.leading_digits})") && number =~ Regexp.new("^(#{format.pattern})$")
|
29
|
+
end
|
30
|
+
|
31
|
+
return native_format if native_format || main_country_for_code
|
32
|
+
parent_country.detect_format(number) if parent_country
|
33
|
+
end
|
34
|
+
|
35
|
+
def parent_country
|
36
|
+
return if main_country_for_code
|
37
|
+
Country.all_countries.detect do |country|
|
38
|
+
country.country_code == self.country_code && country.main_country_for_code
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def full_general_pattern
|
43
|
+
%r{^(#{country_code})?(#{national_prefix})?(?<national_num>#{general_validation.pattern})$}
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.phone_data
|
47
|
+
@phone_data ||= load_data
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.find(country_id)
|
51
|
+
data = phone_data[country_id.to_s.upcase.to_sym]
|
52
|
+
new(data) if data
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.load_data
|
56
|
+
data_file = "#{File.dirname(__FILE__)}/../../data/telephone_number_data_file.dat"
|
57
|
+
main_data = Marshal.load(File.binread(data_file))
|
58
|
+
override_data = {}
|
59
|
+
override_data = Marshal.load(File.binread(Phonejack.override_file)) if Phonejack.override_file
|
60
|
+
return main_data.deep_deep_merge!(override_data)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.all_countries
|
64
|
+
@all_countries ||= phone_data.values.map {|data| new(data)}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Phonejack
|
2
|
+
require 'nokogiri'
|
3
|
+
class DataImporter
|
4
|
+
attr_reader :data, :file, :override
|
5
|
+
|
6
|
+
def initialize(file_name, override: false)
|
7
|
+
@data = {}
|
8
|
+
@file = File.open(file_name) { |f| Nokogiri::XML(f) }
|
9
|
+
@override = override
|
10
|
+
end
|
11
|
+
|
12
|
+
def import!
|
13
|
+
parse_main_data
|
14
|
+
save_data_file
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_main_data
|
18
|
+
file.css('territories territory').each do |territory|
|
19
|
+
country_code = territory.attributes['id'].value.to_sym
|
20
|
+
@data[country_code] ||= {}
|
21
|
+
|
22
|
+
load_base_attributes(@data[country_code], territory)
|
23
|
+
load_references(@data[country_code], territory)
|
24
|
+
load_validations(@data[country_code], territory)
|
25
|
+
load_formats(@data[country_code], territory)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def load_formats(country_data, territory)
|
32
|
+
formats_arr = territory.css('availableFormats numberFormat').map do |format|
|
33
|
+
{}.tap do |fhash|
|
34
|
+
format.attributes.values.each do |attr|
|
35
|
+
key = underscore(attr.name).to_sym
|
36
|
+
fhash[key] = if key == :national_prefix_formatting_rule
|
37
|
+
attr.value
|
38
|
+
else
|
39
|
+
attr.value.delete("\n ")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
format.elements.each do |child|
|
43
|
+
key = underscore(child.name).to_sym
|
44
|
+
fhash[key] = [:format, :intl_format].include?(key) ? child.text : child.text.delete("\n ")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return if override && formats_arr.empty?
|
50
|
+
country_data[:formats] = formats_arr
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_validations(country_data, territory)
|
54
|
+
country_data[:validations] = {}
|
55
|
+
territory.elements.each do |element|
|
56
|
+
next if element.name == 'references' || element.name == 'availableFormats'
|
57
|
+
country_data[:validations][underscore(element.name).to_sym] = {}.tap do |validation_hash|
|
58
|
+
element.elements.each { |child| validation_hash[underscore(child.name).to_sym] = child.text.delete("\n ") }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
country_data.delete(:validations) if country_data[:validations].empty? && override
|
62
|
+
end
|
63
|
+
|
64
|
+
def load_base_attributes(country_data, territory)
|
65
|
+
territory.attributes.each do |key, value_object|
|
66
|
+
underscored_key = underscore(key).to_sym
|
67
|
+
country_data[underscored_key] = if underscored_key == :national_prefix_for_parsing
|
68
|
+
value_object.value.delete("\n ")
|
69
|
+
else
|
70
|
+
value_object.value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def load_references(country_data, territory)
|
76
|
+
ref_arr = territory.css('references sourceUrl').map(&:text)
|
77
|
+
return if override && ref_arr.empty?
|
78
|
+
country_data[:references] = ref_arr
|
79
|
+
end
|
80
|
+
|
81
|
+
def underscore(camel_cased_word)
|
82
|
+
return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
|
83
|
+
word = camel_cased_word.to_s.gsub(/::/, '/')
|
84
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
85
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
86
|
+
word.tr!('-', '_')
|
87
|
+
word.downcase!
|
88
|
+
word
|
89
|
+
end
|
90
|
+
|
91
|
+
def save_data_file
|
92
|
+
data_file = override ? 'telephone_number_data_override_file.dat' : "#{File.dirname(__FILE__)}/../../data/telephone_number_data_file.dat"
|
93
|
+
File.open(data_file, 'wb+') { |f| Marshal.dump(@data, f) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Phonejack
|
2
|
+
class Formatter
|
3
|
+
|
4
|
+
attr_reader :normalized_number, :country, :valid, :original_number
|
5
|
+
|
6
|
+
def initialize(number_obj)
|
7
|
+
@normalized_number = number_obj.normalized_number
|
8
|
+
@country = number_obj.country
|
9
|
+
@valid = number_obj.valid?
|
10
|
+
@original_number = number_obj.original_number
|
11
|
+
end
|
12
|
+
|
13
|
+
def national_number(formatted: true)
|
14
|
+
return original_or_default if !valid? || !number_format
|
15
|
+
build_national_number(formatted: formatted)
|
16
|
+
end
|
17
|
+
|
18
|
+
def e164_number(formatted: true)
|
19
|
+
return original_or_default if !valid?
|
20
|
+
build_e164_number(formatted: formatted)
|
21
|
+
end
|
22
|
+
|
23
|
+
def international_number(formatted: true)
|
24
|
+
return original_or_default if !valid? || !number_format
|
25
|
+
build_international_number(formatted: formatted)
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :valid?, :valid
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def number_format
|
33
|
+
@number_format ||= country.detect_format(normalized_number)
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_national_number(formatted: true)
|
37
|
+
captures = normalized_number.match(number_format.pattern).captures
|
38
|
+
national_prefix_formatting_rule = number_format.national_prefix_formatting_rule || country.national_prefix_formatting_rule
|
39
|
+
|
40
|
+
formatted_string = format(ruby_format_string(number_format.format), *captures)
|
41
|
+
captures.delete(country.mobile_token)
|
42
|
+
|
43
|
+
if national_prefix_formatting_rule
|
44
|
+
national_prefix_string = national_prefix_formatting_rule.dup
|
45
|
+
national_prefix_string.gsub!(/\$NP/, country.national_prefix)
|
46
|
+
national_prefix_string.gsub!(/\$FG/, captures[0])
|
47
|
+
formatted_string.sub!(captures[0], national_prefix_string)
|
48
|
+
end
|
49
|
+
|
50
|
+
formatted ? formatted_string : Phonejack.sanitize(formatted_string)
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_e164_number(formatted: true)
|
54
|
+
formatted_string = "+#{country.country_code}#{normalized_number}"
|
55
|
+
formatted ? formatted_string : Phonejack.sanitize(formatted_string)
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_international_number(formatted: true)
|
59
|
+
return original_or_default if !valid? || number_format.nil?
|
60
|
+
captures = normalized_number.match(number_format.pattern).captures
|
61
|
+
key = number_format.intl_format || number_format.format
|
62
|
+
formatted_string = "+#{country.country_code} #{format(ruby_format_string(key), *captures)}"
|
63
|
+
formatted ? formatted_string : Phonejack.sanitize(formatted_string)
|
64
|
+
end
|
65
|
+
|
66
|
+
def ruby_format_string(format_string)
|
67
|
+
format_string.gsub(/(\$\d)/) { |cap| "%#{cap.reverse}s" }
|
68
|
+
end
|
69
|
+
|
70
|
+
def original_or_default
|
71
|
+
return original_number unless Phonejack.default_format_string && Phonejack.default_format_pattern
|
72
|
+
captures = original_number.match(Phonejack.default_format_pattern).captures
|
73
|
+
format(ruby_format_string(Phonejack.default_format_string), *captures)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Phonejack
|
2
|
+
class Number
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :country, :parser, :formatter, :original_number
|
6
|
+
|
7
|
+
delegate [:valid?, :valid_types, :normalized_number] => :parser
|
8
|
+
delegate [:national_number, :e164_number, :international_number] => :formatter
|
9
|
+
|
10
|
+
def initialize(number, country)
|
11
|
+
@original_number = number
|
12
|
+
@country = Country.find(country)
|
13
|
+
@parser = Parser.new(self)
|
14
|
+
@formatter = Formatter.new(self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Phonejack
|
2
|
+
class NumberFormat
|
3
|
+
|
4
|
+
attr_reader :pattern, :leading_digits, :format, :national_prefix_formatting_rule, :intl_format
|
5
|
+
|
6
|
+
def initialize(data_hash)
|
7
|
+
@pattern = Regexp.new(data_hash[:pattern]) if data_hash[:pattern]
|
8
|
+
@leading_digits = Regexp.new(data_hash[:leading_digits]) if data_hash[:leading_digits]
|
9
|
+
@format = data_hash[:format]
|
10
|
+
@intl_format = data_hash[:intl_format]
|
11
|
+
@national_prefix_formatting_rule = data_hash[:national_prefix_formatting_rule]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Phonejack
|
2
|
+
class Parser
|
3
|
+
attr_reader :original_number, :normalized_number, :country
|
4
|
+
|
5
|
+
def initialize(number_obj)
|
6
|
+
@original_number = number_obj.original_number
|
7
|
+
@country = number_obj.country
|
8
|
+
@normalized_number = build_normalized_number if @country
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid_types
|
12
|
+
@valid_types ||= validate
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?(keys = [])
|
16
|
+
keys.empty? ? !valid_types.empty? : !(valid_types & keys.map(&:to_sym)).empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# normalized_number is basically a "best effort" at national number without
|
22
|
+
# any formatting. This is what we will use to derive formats, validations and
|
23
|
+
# basically anything else that uses google data
|
24
|
+
def build_normalized_number
|
25
|
+
match_result = parse_prefix.match(country.full_general_pattern)
|
26
|
+
match_result ? match_result[:national_num] : original_number
|
27
|
+
end
|
28
|
+
|
29
|
+
# returns an array of valid types for the normalized number
|
30
|
+
# if array is empty, we can assume that the number is invalid
|
31
|
+
def validate
|
32
|
+
return [] unless country
|
33
|
+
country.validations.select do |validation|
|
34
|
+
normalized_number =~ Regexp.new("^(#{validation.pattern})$")
|
35
|
+
end.map(&:name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_prefix
|
39
|
+
return original_number unless country.national_prefix_for_parsing
|
40
|
+
duped = original_number.dup
|
41
|
+
match_object = duped.match(country.national_prefix_for_parsing)
|
42
|
+
|
43
|
+
# we need to do the "start_with?" here because we need to make sure it's not finding
|
44
|
+
# something in the middle of the number. However, we can't modify the regex to do this
|
45
|
+
# for us because it will offset the match groups that are referenced in the transform rules
|
46
|
+
return original_number unless match_object && duped.start_with?(match_object[0])
|
47
|
+
if country.national_prefix_transform_rule
|
48
|
+
transform_national_prefix(duped, match_object)
|
49
|
+
else
|
50
|
+
duped.sub!(match_object[0], '')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def transform_national_prefix(duped, match_object)
|
55
|
+
if country.mobile_token && match_object.captures.any?
|
56
|
+
format(build_format_string, duped.sub!(match_object[0], match_object[1]))
|
57
|
+
elsif match_object.captures.none?
|
58
|
+
duped.sub!(match_object[0], '')
|
59
|
+
else
|
60
|
+
format(build_format_string, *match_object.captures)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def build_format_string
|
65
|
+
country.national_prefix_transform_rule.gsub(/(\$\d)/) { |cap| "%#{cap.reverse}s" }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Phonejack
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'httparty'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
class TestDataGenerator
|
7
|
+
URL = "https://libphonenumber.appspot.com/phonenumberparser?number=%s&country=%s".freeze
|
8
|
+
COUNTRIES = {
|
9
|
+
AE: %w(971529933171 971553006144 971551000291),
|
10
|
+
AR: %w(111512345678 380151234567 299154104587 01112345678),
|
11
|
+
AU: %w(0467703037),
|
12
|
+
BE: %w(32498485960 32477702206 32474095692),
|
13
|
+
BO: %w(59178500348 59178006138 59178006139),
|
14
|
+
BR: %w(011992339376 1123456789 11961234567),
|
15
|
+
BY: %w(80152450911 294911911 152450911),
|
16
|
+
CA: %w(16135550119 16135550171 16135550112 16135550194
|
17
|
+
16135550122 16135550131 15146708700 14169158200),
|
18
|
+
CH: %w(41794173875 41795061129 41795820985),
|
19
|
+
CL: %w(961234567 221234567),
|
20
|
+
CN: %w(15694876068 13910503766 15845989469 05523245954 04717158875 03748086894),
|
21
|
+
CO: %w(3211234567 12345678),
|
22
|
+
CR: %w(22123456 83123456),
|
23
|
+
DE: %w(15222503070),
|
24
|
+
DK: %w(4524453744 4551622172 4542428484),
|
25
|
+
EC: %w(593992441504 593984657155 593984015053),
|
26
|
+
EE: %w(37253629280 37253682997 37254004517),
|
27
|
+
ES: %w(34606217800 34667751353 34646570628),
|
28
|
+
FR: %w(0607114556),
|
29
|
+
GB: %w(448444156790 442079308181 442076139800 442076361000
|
30
|
+
07780991912 442076299400 442072227888),
|
31
|
+
HK: %w(64636251),
|
32
|
+
HU: %w(36709311285 36709311279 36709311206),
|
33
|
+
IE: %w(0863634875),
|
34
|
+
IN: %w(915622231515 912942433300 912912510101 911126779191
|
35
|
+
912224818000 917462223999 912266653366 912266325757
|
36
|
+
914066298585 911242451234 911166566162 911123890606
|
37
|
+
911123583754 5622231515 2942433300 2912510101
|
38
|
+
1126779191 2224818000 7462223999 2266653366
|
39
|
+
2266325757 4066298585 1242451234 1166566162
|
40
|
+
1123890606 1123583754 09176642499),
|
41
|
+
IT: %w(393478258998 393440161350),
|
42
|
+
JP: %w(312345678 9012345678),
|
43
|
+
KR: %w(821036424812 821053812833 821085894820),
|
44
|
+
MX: %w(4423593227 14423593227),
|
45
|
+
NL: %w(31610958780 31610000852 31611427604),
|
46
|
+
NO: %w(4792272668 4797065876 4792466013),
|
47
|
+
NZ: %w(64212715077 6421577017 64212862111),
|
48
|
+
PE: %w(51994156035 51987527881 51972737259),
|
49
|
+
PH: %w(639285588185 639285588262 639285548190),
|
50
|
+
PL: %w(48665666003 48885882321 48885889958),
|
51
|
+
QA: %w(97470482288 97474798678),
|
52
|
+
RO: %w(40724242563 40727798526 40727735377),
|
53
|
+
SA: %w(966503891468 966501543349 966500939012),
|
54
|
+
SE: %w(46708922920 46723985268 46761001966),
|
55
|
+
SG: %w(96924755),
|
56
|
+
TR: %w(905497728782 905497728780 905497728781),
|
57
|
+
TT: %w(18687804765 18687804843 18687804752),
|
58
|
+
TW: %w(886905627933 886905627901 886905627925),
|
59
|
+
US: %w(16502530000 14044879000 15123435283 13032450086 16175751300
|
60
|
+
3175083345 13128404100 12485934000 19497941600 14257395600 13103106000
|
61
|
+
16086699600 12125650000 14123456700 14157360000 12068761800
|
62
|
+
12023461100 3175082333),
|
63
|
+
VE: %w(584149993108 584248407260 584248271518),
|
64
|
+
ZA: %w(27826187617 27823014578 27828840632)
|
65
|
+
}.freeze
|
66
|
+
def self.import!
|
67
|
+
output_hash = {}
|
68
|
+
fetch_data(output_hash)
|
69
|
+
write_file(output_hash)
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.fetch_data(output_hash)
|
74
|
+
COUNTRIES.each do |key, value|
|
75
|
+
output_hash[key] = {}
|
76
|
+
|
77
|
+
value.each_with_index do |num, counter|
|
78
|
+
page = HTTParty.get(format(URL, num, key.to_s))
|
79
|
+
parsed_page = Nokogiri::HTML.parse(page)
|
80
|
+
body = parsed_page.elements.first.elements.css('body').first
|
81
|
+
parsed_data = parse_remote_data(counter, body.elements.css('table')[2])
|
82
|
+
output_hash[key].merge!(parsed_data)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return output_hash
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.parse_remote_data(counter, table)
|
89
|
+
output = { counter.to_s => {} }
|
90
|
+
table.elements.each do |row|
|
91
|
+
next if row.elements.one?
|
92
|
+
key = case row.elements.css('th').text
|
93
|
+
when 'E164 format'
|
94
|
+
:e164_formatted
|
95
|
+
when 'National format'
|
96
|
+
:national_formatted
|
97
|
+
when 'International format'
|
98
|
+
:international_formatted
|
99
|
+
end
|
100
|
+
|
101
|
+
output[counter.to_s][key] = row.elements.css('td').text if key
|
102
|
+
end
|
103
|
+
return output
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.write_file(data)
|
107
|
+
File.open('test/valid_numbers.yml', 'w') do |file|
|
108
|
+
file.write "# This file is generated automatically by TestDataGenerator. \n" \
|
109
|
+
"# Any changes made to this file will be overridden next time test data is generated. \n" \
|
110
|
+
"# Please edit TestDataGenerator if you need to add test cases. \n"
|
111
|
+
|
112
|
+
file.write data.to_yaml
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|