phonelib 0.7.7 → 0.10.9
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 +4 -4
- data/README.md +1 -1
- data/data/extended_data.dat +0 -0
- data/data/phone_data.dat +0 -0
- data/lib/phonelib/core.rb +38 -5
- data/lib/phonelib/data_importer.rb +11 -4
- data/lib/phonelib/data_importer_helper.rb +9 -1
- data/lib/phonelib/phone.rb +5 -1
- data/lib/phonelib/phone_analyzer.rb +39 -15
- data/lib/phonelib/phone_analyzer_helper.rb +32 -26
- data/lib/phonelib/phone_extended_data.rb +14 -7
- data/lib/phonelib/phone_formatter.rb +5 -9
- data/lib/phonelib/version.rb +3 -1
- data/lib/phonelib.rb +10 -4
- data/lib/tasks/phonelib_tasks.rake +2 -0
- data/lib/validators/phone_validator.rb +2 -0
- metadata +6 -118
- data/lib/validators/phone_validator3.rb +0 -130
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d02888169692ebcf0b98bd02f467f3d2241eb1d6687345c5176cd3d7054b4b70
|
4
|
+
data.tar.gz: d73a746b3e5d4f9fed365dca8491d8f259016c4304428c39035cf27792f93502
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f0491a84599e35d6d665df74971250c5521b82e91411e00938a98c720efae6b5fb651107e79574fa0050543540a0c9648fced165f73f2b976e0024d89c9aadb
|
7
|
+
data.tar.gz: adb6f0e9095bb1bf35836860f7049b3e5013d0903b710deedb61a71fbd206b5d0f29f2bdfb2d0da4ea75b0a5eec6ee6c0e174a76a3fc7c43b07e236ae2ec6aca
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ But it still doesn't include all Google's library functionality.
|
|
13
13
|
|
14
14
|
## Incorrect parsing or validation
|
15
15
|
|
16
|
-
In case your phone number is incorrectly parsed, you can check original libphonenumber for result [here](https://
|
16
|
+
In case your phone number is incorrectly parsed, you can check original libphonenumber for result [here](https://htmlpreview.github.io/?https://github.com/google/libphonenumber/blob/master/javascript/i18n/phonenumbers/demo-compiled.html) and in case of same parse result [open an issue for them](http://issuetracker.google.com/issues/new?component=192347). This gem's data is based on it.
|
17
17
|
If you can't wait for libphonenumber to resolve the issue, try to use ```Phonelib.add_additional_regex``` and ```Phonelib.additional_regexes``` methods.
|
18
18
|
|
19
19
|
## Information
|
data/data/extended_data.dat
CHANGED
Binary file
|
data/data/phone_data.dat
CHANGED
Binary file
|
data/lib/phonelib/core.rb
CHANGED
@@ -1,15 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phonelib
|
2
4
|
# main module that includes all basic data and methods
|
3
5
|
module Core
|
4
6
|
# @private variable will include hash with data for validation
|
5
7
|
@@phone_data = nil
|
6
8
|
|
9
|
+
# eagerly initialize the gem, loads data into memory. not required, initialization is done lazily otherwise, but
|
10
|
+
# may be desirable in production enviroments to avoid initialization time on first use.
|
11
|
+
def eager_load!
|
12
|
+
return if @@skip_eager_loading
|
13
|
+
phone_data
|
14
|
+
phone_ext_data
|
15
|
+
end
|
16
|
+
|
17
|
+
@@skip_eager_loading = false
|
18
|
+
def skip_eager_loading!
|
19
|
+
@@skip_eager_loading = true
|
20
|
+
end
|
21
|
+
|
7
22
|
# getter for phone data for other modules of gem, can be used outside
|
8
23
|
# @return [Hash] all data for phone parsing
|
9
24
|
def phone_data
|
10
25
|
@@phone_data ||= load_data.freeze
|
11
26
|
end
|
12
27
|
|
28
|
+
# @private getter for phone data indexed by country code (internal use only)
|
29
|
+
def data_by_country_codes
|
30
|
+
@@data_by_country_codes ||= phone_data.each_value.group_by { |d| d[COUNTRY_CODE] }.freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
# @private getter for all international prefixes in phone_data
|
34
|
+
def phone_data_int_prefixes
|
35
|
+
@@all_int_prefixes ||= phone_data.map {|k,v| v[:international_prefix] }.select { |v| v != '' }.compact.uniq.join('|').freeze
|
36
|
+
end
|
37
|
+
|
13
38
|
# @private used to cache frequently-used regular expressions
|
14
39
|
@@phone_regexp_cache = {}
|
15
40
|
|
@@ -30,14 +55,14 @@ module Phonelib
|
|
30
55
|
@@default_country = nil
|
31
56
|
|
32
57
|
# getter method for default_country variable
|
33
|
-
# @return [String
|
58
|
+
# @return [String,Symbol,Array<String,Symbol>,nil] Default country ISO2 code or codes used for parsing
|
34
59
|
def default_country
|
35
60
|
@@default_country
|
36
61
|
end
|
37
62
|
|
38
63
|
# setter method for default_country variable
|
39
|
-
# @param country [String
|
40
|
-
# @return [String
|
64
|
+
# @param country [String,Symbol,Array<String,Symbol>] Default country ISO2 code or codes used for parsing
|
65
|
+
# @return [String,Symbol,Array<String,Symbol>] Default country ISO2 code or codes used for parsing
|
41
66
|
def default_country=(country)
|
42
67
|
@@default_country = country
|
43
68
|
end
|
@@ -177,9 +202,9 @@ module Phonelib
|
|
177
202
|
end
|
178
203
|
|
179
204
|
def add_additional_regex(country, type, national_regex)
|
180
|
-
return unless Phonelib::Core::TYPES_DESC.
|
205
|
+
return unless Phonelib::Core::TYPES_DESC.key?(type.to_sym)
|
181
206
|
return unless national_regex.is_a?(String)
|
182
|
-
@@phone_data = nil
|
207
|
+
@@phone_data = @@data_by_country_codes = nil
|
183
208
|
@@additional_regexes[country.to_s.upcase] ||= {}
|
184
209
|
@@additional_regexes[country.to_s.upcase][type] ||= []
|
185
210
|
@@additional_regexes[country.to_s.upcase][type] << national_regex
|
@@ -356,6 +381,7 @@ module Phonelib
|
|
356
381
|
carrier_selection_codes: 'Carrier Selection codes',
|
357
382
|
area_code_optional: 'Are code optional'
|
358
383
|
}.freeze
|
384
|
+
TYPES_DESC_KEYS = TYPES_DESC.keys.freeze
|
359
385
|
|
360
386
|
# @private short codes types keys
|
361
387
|
SHORT_CODES = [
|
@@ -382,6 +408,13 @@ module Phonelib
|
|
382
408
|
# @private Extended data key for carrier in prefixes hash
|
383
409
|
EXT_CARRIER_KEY = :c
|
384
410
|
|
411
|
+
# @private Static arrays used to avoid allocations
|
412
|
+
FIXED_OR_MOBILE_ARRAY = [Core::FIXED_OR_MOBILE].freeze
|
413
|
+
FIXED_LINE_OR_MOBILE_ARRAY = [Core::FIXED_LINE, Core::MOBILE].freeze
|
414
|
+
POSSIBLE_VALID_ARRAY = [:possible, :valid].freeze
|
415
|
+
VALID_POSSIBLE_ARRAY = [:valid, :possible].freeze
|
416
|
+
NIL_RESULT_ARRAY = [nil, nil].freeze
|
417
|
+
|
385
418
|
# method for parsing phone number.
|
386
419
|
# On first run fills @@phone_data with data present in yaml file
|
387
420
|
# @param phone [String] the phone number to be parsed
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'phonelib/data_importer_helper'
|
2
4
|
|
3
5
|
module Phonelib
|
@@ -6,7 +8,7 @@ module Phonelib
|
|
6
8
|
require 'nokogiri'
|
7
9
|
|
8
10
|
# official libphonenumber repo for cloning
|
9
|
-
REPO = 'https://github.com/
|
11
|
+
REPO = 'https://github.com/google/libphonenumber.git'
|
10
12
|
|
11
13
|
# importing function
|
12
14
|
def self.import
|
@@ -45,6 +47,7 @@ module Phonelib
|
|
45
47
|
'TA' => 'SH',
|
46
48
|
'TC' => 'US',
|
47
49
|
'TT' => 'US',
|
50
|
+
'UM' => 'US',
|
48
51
|
'VA' => 'IT',
|
49
52
|
'VC' => 'US',
|
50
53
|
'VG' => 'US',
|
@@ -125,6 +128,9 @@ module Phonelib
|
|
125
128
|
if country[Core::NATIONAL_PREFIX_TRANSFORM_RULE]
|
126
129
|
country[Core::NATIONAL_PREFIX_TRANSFORM_RULE].gsub!('$', '\\')
|
127
130
|
end
|
131
|
+
if country[:id] == '001'
|
132
|
+
country[:id] = 'International ' + country[:country_code]
|
133
|
+
end
|
128
134
|
@data[country[:id]] = country
|
129
135
|
end
|
130
136
|
end
|
@@ -160,6 +166,7 @@ module Phonelib
|
|
160
166
|
# some countries missing formats, and are linking them to another countries
|
161
167
|
def process_format_links
|
162
168
|
FORMAT_SHARING.each do |destination, source|
|
169
|
+
next unless @data[destination]
|
163
170
|
@data[destination][:formats] ||= []
|
164
171
|
@data[destination][:formats] = @data[destination][:formats] + @data[source][:formats]
|
165
172
|
end
|
@@ -195,8 +202,8 @@ module Phonelib
|
|
195
202
|
|
196
203
|
require 'open-uri'
|
197
204
|
require 'csv'
|
198
|
-
io = open('http://
|
199
|
-
csv = CSV.new(io, {col_sep: "\t"})
|
205
|
+
io = URI.open('http://download.geonames.org/export/dump/countryInfo.txt')
|
206
|
+
csv = CSV.new(io, **{col_sep: "\t"})
|
200
207
|
csv.each do |row|
|
201
208
|
next if row[0].nil? || row[0].start_with?('#') || row[0].empty? || row[0].size != 2
|
202
209
|
|
@@ -207,7 +214,7 @@ module Phonelib
|
|
207
214
|
# adds double country code flag in case country allows
|
208
215
|
def add_double_country_flag(country)
|
209
216
|
if DOUBLE_COUNTRY_CODES_COUNTRIES.include?(country[:id])
|
210
|
-
country[
|
217
|
+
country[Core::DOUBLE_COUNTRY_PREFIX_FLAG] = true
|
211
218
|
end
|
212
219
|
country
|
213
220
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phonelib
|
2
4
|
# @private helper module for parsing raw libphonenumber data
|
3
5
|
module DataImporterHelper
|
@@ -101,7 +103,13 @@ module Phonelib
|
|
101
103
|
line = str_clean line, false
|
102
104
|
next if line.empty? || line[0] == '#'
|
103
105
|
prefix, line_data = line.split('|')
|
104
|
-
|
106
|
+
if line_data
|
107
|
+
data[prefix] = if line_data.strip =~ /[^ ]{3,}&[^ ]{3,}/
|
108
|
+
line_data.strip.split('&')
|
109
|
+
else
|
110
|
+
line_data.strip
|
111
|
+
end
|
112
|
+
end
|
105
113
|
end
|
106
114
|
data
|
107
115
|
end
|
data/lib/phonelib/phone.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phonelib
|
2
4
|
# class for parsed phone number, includes validation and formatting methods
|
3
5
|
class Phone
|
@@ -24,7 +26,7 @@ module Phonelib
|
|
24
26
|
# @return [Phonelib::Phone] parsed phone instance
|
25
27
|
def initialize(phone, country = nil)
|
26
28
|
@original, @extension = separate_extension(phone.to_s)
|
27
|
-
@extension.gsub
|
29
|
+
@extension = @extension.gsub(/[^0-9]/, '') if @extension
|
28
30
|
|
29
31
|
if sanitized.empty?
|
30
32
|
@data = {}
|
@@ -178,6 +180,8 @@ module Phonelib
|
|
178
180
|
|
179
181
|
# @private extracts extension from passed phone number if provided
|
180
182
|
def separate_extension(original)
|
183
|
+
return [original, ''] unless Phonelib.extension_separate_symbols
|
184
|
+
|
181
185
|
regex = if Phonelib.extension_separate_symbols.is_a?(Array)
|
182
186
|
cr("#{Phonelib.extension_separate_symbols.join('|')}")
|
183
187
|
else
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phonelib
|
2
4
|
# @private phone analyzing methods module
|
3
5
|
module PhoneAnalyzer
|
@@ -17,7 +19,6 @@ module Phonelib
|
|
17
19
|
# country (2 letters) like 'US', 'us' or :us for United States
|
18
20
|
def analyze(phone, passed_country)
|
19
21
|
countries = country_or_default_country passed_country
|
20
|
-
|
21
22
|
return analyze_single_country(phone, countries.first, passed_country) if countries.size == 1
|
22
23
|
|
23
24
|
results = {}
|
@@ -32,7 +33,7 @@ module Phonelib
|
|
32
33
|
|
33
34
|
# pick best result when several countries specified
|
34
35
|
def pick_results(results)
|
35
|
-
|
36
|
+
Core::VALID_POSSIBLE_ARRAY.each do |key|
|
36
37
|
final = results.select { |_k, v| v[key].any? }
|
37
38
|
return decorate_analyze_result(final) if final.size > 0
|
38
39
|
end
|
@@ -68,12 +69,19 @@ module Phonelib
|
|
68
69
|
end
|
69
70
|
|
70
71
|
# replacing national prefix to simplified format
|
71
|
-
def with_replaced_national_prefix(
|
72
|
-
return
|
72
|
+
def with_replaced_national_prefix(passed_phone, data)
|
73
|
+
return passed_phone unless data[Core::NATIONAL_PREFIX_TRANSFORM_RULE]
|
74
|
+
phone = if passed_phone.start_with?(data[Core::COUNTRY_CODE]) && !data[Core::DOUBLE_COUNTRY_PREFIX_FLAG]
|
75
|
+
passed_phone.delete_prefix(data[Core::COUNTRY_CODE])
|
76
|
+
else
|
77
|
+
passed_phone
|
78
|
+
end
|
79
|
+
return passed_phone unless phone.match? cr("^#{type_regex(data[Core::TYPES][Core::GENERAL], Core::POSSIBLE_PATTERN)}$")
|
80
|
+
|
73
81
|
pattern = cr("^(?:#{data[Core::NATIONAL_PREFIX_FOR_PARSING]})")
|
74
82
|
match = phone.match pattern
|
75
83
|
if match && match.captures.compact.size > 0
|
76
|
-
phone.gsub(pattern, data[Core::NATIONAL_PREFIX_TRANSFORM_RULE])
|
84
|
+
data[Core::COUNTRY_CODE] + phone.gsub(pattern, data[Core::NATIONAL_PREFIX_TRANSFORM_RULE])
|
77
85
|
else
|
78
86
|
phone
|
79
87
|
end
|
@@ -124,15 +132,27 @@ module Phonelib
|
|
124
132
|
#
|
125
133
|
# * +phone+ - phone number for parsing
|
126
134
|
def detect_and_parse(phone, country)
|
127
|
-
|
128
|
-
|
135
|
+
countries_data = country_code_candidates_for(phone).flat_map { |code|
|
136
|
+
Phonelib.data_by_country_codes[code] || []
|
137
|
+
}
|
138
|
+
countries_data.each_with_object({}) do |data, result|
|
139
|
+
key = data[:id]
|
129
140
|
parsed = parse_single_country(phone, data)
|
141
|
+
unless parsed && parsed[key] && parsed[key][:valid].size > 0
|
142
|
+
replaced_parsed = parse_single_country(with_replaced_national_prefix(phone, data), data)
|
143
|
+
parsed = replaced_parsed unless replaced_parsed.nil?
|
144
|
+
end
|
130
145
|
if (!Phonelib.strict_double_prefix_check || key == country) && double_prefix_allowed?(data, phone, parsed && parsed[key])
|
131
|
-
|
146
|
+
parsed2 = parse_single_country(changed_dp_phone(key, phone), data)
|
147
|
+
parsed = parsed2 if parsed2 && parsed2[key] && parsed2[key][:valid].size > 0
|
132
148
|
end
|
133
149
|
result.merge!(parsed) unless parsed.nil?
|
134
|
-
end
|
135
|
-
|
150
|
+
end.compact
|
151
|
+
end
|
152
|
+
|
153
|
+
def country_code_candidates_for(phone)
|
154
|
+
stripped_phone = phone.gsub(cr("Phonelib.phone_data_int_prefixes") { /^(#{Phonelib.phone_data_int_prefixes})/ }, '')
|
155
|
+
((1..3).map { |length| phone[0, length] } + (1..3).map { |length| stripped_phone[0, length] }).uniq
|
136
156
|
end
|
137
157
|
|
138
158
|
# Create phone representation in e164 format
|
@@ -142,13 +162,13 @@ module Phonelib
|
|
142
162
|
# * +phone+ - phone number for parsing
|
143
163
|
# * +data+ - country data to be based on for creating e164 representation
|
144
164
|
def convert_to_e164(phone, data)
|
145
|
-
match = phone.match full_regex_for_data(data, Core::VALID_PATTERN, !
|
165
|
+
match = phone.match full_regex_for_data(data, Core::VALID_PATTERN, !original_starts_with_plus_or_double_zero?)
|
146
166
|
case
|
147
167
|
when match
|
148
168
|
"#{data[Core::COUNTRY_CODE]}#{match.to_a.last}"
|
149
169
|
when phone.match(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"))
|
150
170
|
phone.sub(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"), Core::PLUS_SIGN)
|
151
|
-
when
|
171
|
+
when original_starts_with_plus_or_double_zero? && phone.start_with?(data[Core::COUNTRY_CODE])
|
152
172
|
phone
|
153
173
|
else
|
154
174
|
"#{data[Core::COUNTRY_CODE]}#{phone}"
|
@@ -164,7 +184,11 @@ module Phonelib
|
|
164
184
|
# * +not_valid+ - specifies that number is not valid by general desc pattern
|
165
185
|
def national_and_data(data, country_match, not_valid = false)
|
166
186
|
result = data.select { |k, _v| k != :types && k != :formats }
|
167
|
-
|
187
|
+
index = 0
|
188
|
+
if Phonelib.additional_regexes.is_a?(Hash) && Phonelib.additional_regexes[data[:id]]
|
189
|
+
index = Phonelib.additional_regexes[data[:id]].values.flatten.join('|').scan(/\(/).size
|
190
|
+
end
|
191
|
+
phone = country_match[-1 - index]
|
168
192
|
result[:national] = phone
|
169
193
|
result[:format] = number_format(phone, data[Core::FORMATS])
|
170
194
|
result.merge! all_number_types(phone, data[Core::TYPES], not_valid)
|
@@ -205,7 +229,7 @@ module Phonelib
|
|
205
229
|
def number_format(national, format_data)
|
206
230
|
format_data && format_data.find do |format|
|
207
231
|
(format[Core::LEADING_DIGITS].nil? || \
|
208
|
-
national.match(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
|
232
|
+
national.match?(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
|
209
233
|
national.match(cr("^(#{format[Core::PATTERN]})$"))
|
210
234
|
end || Core::DEFAULT_NUMBER_FORMAT
|
211
235
|
end
|
@@ -226,7 +250,7 @@ module Phonelib
|
|
226
250
|
type_regex(patterns, Core::VALID_PATTERN)
|
227
251
|
]
|
228
252
|
else
|
229
|
-
|
253
|
+
Core::NIL_RESULT_ARRAY
|
230
254
|
end
|
231
255
|
end
|
232
256
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phonelib
|
2
4
|
# @private helper methods for analyser
|
3
5
|
module PhoneAnalyzerHelper
|
@@ -7,12 +9,15 @@ module Phonelib
|
|
7
9
|
if result.size == 1
|
8
10
|
result
|
9
11
|
else
|
12
|
+
matched_countries = country_or_default_country(nil) & result.keys
|
13
|
+
matched_countries = result.keys.take(1) if matched_countries.size == 0
|
14
|
+
result = result.keep_if {|k, _v| matched_countries.include?(k) } if matched_countries
|
10
15
|
Hash[result.take(1)]
|
11
16
|
end
|
12
17
|
end
|
13
18
|
|
14
|
-
def
|
15
|
-
original_s[0] == Core::PLUS_SIGN
|
19
|
+
def original_starts_with_plus_or_double_zero?
|
20
|
+
original_s[0] == Core::PLUS_SIGN || original_s[0..1] == '00'
|
16
21
|
end
|
17
22
|
|
18
23
|
# converts symbols in phone to numbers
|
@@ -49,14 +54,15 @@ module Phonelib
|
|
49
54
|
|
50
55
|
# caches regular expression, reusing it for later lookups
|
51
56
|
def cr(regexp)
|
52
|
-
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(regexp).freeze
|
57
|
+
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(block_given? ? yield(regexp) : regexp).freeze
|
53
58
|
end
|
54
59
|
|
55
60
|
# defines whether country can have double country prefix in number
|
56
61
|
def country_can_dp?(country)
|
57
62
|
Phonelib.phone_data[country] &&
|
58
63
|
Phonelib.phone_data[country][Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
59
|
-
!
|
64
|
+
!original_starts_with_plus_or_double_zero? &&
|
65
|
+
original_s.start_with?(Phonelib.phone_data[country][Core::COUNTRY_CODE])
|
60
66
|
end
|
61
67
|
|
62
68
|
# changes phone to with/without double country prefix
|
@@ -66,6 +72,7 @@ module Phonelib
|
|
66
72
|
|
67
73
|
country_code = Phonelib.phone_data[country][Core::COUNTRY_CODE]
|
68
74
|
if phone.start_with? country_code * 2
|
75
|
+
# remove double prefix in case it is there
|
69
76
|
phone.gsub(cr("^#{country_code}"), '')
|
70
77
|
else
|
71
78
|
"#{country_code}#{phone}"
|
@@ -83,7 +90,7 @@ module Phonelib
|
|
83
90
|
data[Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
84
91
|
phone =~ cr("^#{data[Core::COUNTRY_CODE]}") &&
|
85
92
|
parsed && (parsed[:valid].nil? || parsed[:valid].empty?) &&
|
86
|
-
!
|
93
|
+
!original_starts_with_plus_or_double_zero?
|
87
94
|
end
|
88
95
|
|
89
96
|
# Returns original number passed if it's a string or empty string otherwise
|
@@ -97,7 +104,8 @@ module Phonelib
|
|
97
104
|
#
|
98
105
|
# * +country+ - country passed for parsing
|
99
106
|
def country_or_default_country(country)
|
100
|
-
country ||= (
|
107
|
+
country ||= (original_starts_with_plus_or_double_zero? ? nil : Phonelib.default_country)
|
108
|
+
|
101
109
|
if country.is_a?(Array)
|
102
110
|
country.compact.map { |e| e.to_s.upcase }
|
103
111
|
else
|
@@ -113,14 +121,15 @@ module Phonelib
|
|
113
121
|
# * +data+ - country data hash
|
114
122
|
# * +country_optional+ - whether to put country code as optional group
|
115
123
|
def full_regex_for_data(data, type, country_optional = true)
|
116
|
-
regex = []
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
124
|
+
regex = +"^0{2}?(#{data[Core::INTERNATIONAL_PREFIX]})?(#{data[Core::COUNTRY_CODE]})#{country_optional ? '?' : ''}(#{data[Core::NATIONAL_PREFIX_FOR_PARSING] || data[Core::NATIONAL_PREFIX]})?"
|
125
|
+
if data[Core::TYPES]
|
126
|
+
regex << "("
|
127
|
+
regex << type_regex(data[Core::TYPES][Core::GENERAL], type)
|
128
|
+
regex << ")"
|
129
|
+
end
|
130
|
+
regex << "$"
|
122
131
|
|
123
|
-
cr(
|
132
|
+
cr(regex)
|
124
133
|
end
|
125
134
|
|
126
135
|
# Returns regex for type with special types if needed
|
@@ -130,11 +139,11 @@ module Phonelib
|
|
130
139
|
# * +data+ - country types data for single type
|
131
140
|
# * +type+ - possible or valid regex type needed
|
132
141
|
def type_regex(data, type)
|
133
|
-
regex = [data[type]]
|
134
142
|
if Phonelib.parse_special && data[Core::SHORT] && data[Core::SHORT][type]
|
135
|
-
|
143
|
+
"#{data[type]}|#{data[Core::SHORT][type]}"
|
144
|
+
else
|
145
|
+
data[type]
|
136
146
|
end
|
137
|
-
regex.join('|')
|
138
147
|
end
|
139
148
|
|
140
149
|
# Check if phone match country data
|
@@ -144,9 +153,7 @@ module Phonelib
|
|
144
153
|
# * +phone+ - phone number for parsing
|
145
154
|
# * +data+ - country data
|
146
155
|
def phone_match_data?(phone, data, possible = false)
|
147
|
-
|
148
|
-
inter_prefix = "(#{data[Core::INTERNATIONAL_PREFIX]})?"
|
149
|
-
return unless phone.match cr("^0{2}?#{inter_prefix}#{country_code}")
|
156
|
+
return unless phone.match?(cr("^0{2}?(#{data[Core::INTERNATIONAL_PREFIX]})?#{data[Core::COUNTRY_CODE]}"))
|
150
157
|
|
151
158
|
type = possible ? Core::POSSIBLE_PATTERN : Core::VALID_PATTERN
|
152
159
|
phone.match full_regex_for_data(data, type, false)
|
@@ -155,10 +162,9 @@ module Phonelib
|
|
155
162
|
# checks if types has both :mobile and :fixed_line and replaces it with
|
156
163
|
# :fixed_or_mobile in case both present
|
157
164
|
def sanitize_fixed_mobile(types)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
types[key] = types[key] - fixed_mobile + [Core::FIXED_OR_MOBILE]
|
165
|
+
Core::POSSIBLE_VALID_ARRAY.each do |key|
|
166
|
+
if (Core::FIXED_LINE_OR_MOBILE_ARRAY - types[key]).empty?
|
167
|
+
types[key] = types[key] - Core::FIXED_LINE_OR_MOBILE_ARRAY + Core::FIXED_OR_MOBILE_ARRAY
|
162
168
|
end
|
163
169
|
end
|
164
170
|
types
|
@@ -172,7 +178,7 @@ module Phonelib
|
|
172
178
|
def types_for_check(data)
|
173
179
|
exclude_list = PhoneAnalyzer::NOT_FOR_CHECK
|
174
180
|
exclude_list += Phonelib::Core::SHORT_CODES unless Phonelib.parse_special
|
175
|
-
Core::
|
181
|
+
Core::TYPES_DESC_KEYS - exclude_list + fixed_and_mobile_keys(data)
|
176
182
|
end
|
177
183
|
|
178
184
|
# Checks if fixed line pattern and mobile pattern are the same and returns
|
@@ -183,9 +189,9 @@ module Phonelib
|
|
183
189
|
# * +data+ - country data
|
184
190
|
def fixed_and_mobile_keys(data)
|
185
191
|
if data[Core::FIXED_LINE] == data[Core::MOBILE]
|
186
|
-
|
192
|
+
Core::FIXED_OR_MOBILE_ARRAY
|
187
193
|
else
|
188
|
-
|
194
|
+
Core::FIXED_LINE_OR_MOBILE_ARRAY
|
189
195
|
end
|
190
196
|
end
|
191
197
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phonelib
|
2
4
|
# module provides extended data methods for parsed phone
|
3
5
|
module PhoneExtendedData
|
@@ -16,12 +18,19 @@ module Phonelib
|
|
16
18
|
Phonelib::Core::EXT_GEO_NAME_KEY
|
17
19
|
end
|
18
20
|
|
19
|
-
# Returns timezone of parsed phone number or nil
|
20
|
-
# there is no timezone specified in db for this number
|
21
|
+
# Returns first timezone (in case several match) of parsed phone number or nil
|
22
|
+
# if number is invalid or there is no timezone specified in db for this number
|
21
23
|
# @return [String|nil] timezone for parsed phone
|
22
24
|
def timezone
|
23
|
-
|
24
|
-
|
25
|
+
timezones.first
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns timezones of parsed phone number or nil if number is invalid or
|
29
|
+
# there is no timezone specified in db for this number
|
30
|
+
# @return [Array] timezones for parsed phone
|
31
|
+
def timezones
|
32
|
+
res = get_ext_name Phonelib::Core::EXT_TIMEZONES, Phonelib::Core::EXT_TIMEZONE_KEY
|
33
|
+
res.is_a?(Array) ? res : [res]
|
25
34
|
end
|
26
35
|
|
27
36
|
# Returns carrier of parsed phone number or nil if number is invalid or
|
@@ -51,9 +60,7 @@ module Phonelib
|
|
51
60
|
def get_ext_name(names_key, id_key)
|
52
61
|
return nil unless ext_data[id_key]
|
53
62
|
|
54
|
-
|
55
|
-
return nil unless res
|
56
|
-
res.size == 1 ? res.first : res
|
63
|
+
Phonelib.phone_ext_data[names_key][ext_data[id_key]]
|
57
64
|
end
|
58
65
|
|
59
66
|
# @private returns extended data ids for current number
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Phonelib
|
2
4
|
# module includes all formatting methods
|
3
5
|
module PhoneFormatter
|
@@ -5,7 +7,7 @@ module Phonelib
|
|
5
7
|
# @param formatted [Boolean] whether to return numbers only or formatted
|
6
8
|
# @return [String] formatted national number
|
7
9
|
def national(formatted = true)
|
8
|
-
return @national_number unless
|
10
|
+
return @national_number unless possible?
|
9
11
|
format_match, format_string = formatting_data
|
10
12
|
|
11
13
|
if format_match
|
@@ -52,7 +54,7 @@ module Phonelib
|
|
52
54
|
def international(formatted = true, prefix = '+')
|
53
55
|
prefix = formatted if formatted.is_a?(String)
|
54
56
|
return nil if sanitized.empty?
|
55
|
-
return "#{prefix}#{
|
57
|
+
return "#{prefix}#{sanitized}" unless possible?
|
56
58
|
return "#{prefix}#{data_country_code}#{@national_number}" unless formatted
|
57
59
|
|
58
60
|
fmt = @data[country][:format]
|
@@ -135,12 +137,6 @@ module Phonelib
|
|
135
137
|
true
|
136
138
|
end
|
137
139
|
|
138
|
-
# @private defines whether to put country prefix or not
|
139
|
-
def country_prefix_or_not
|
140
|
-
return '' unless data_country_code
|
141
|
-
sanitized.start_with?(data_country_code) ? '' : data_country_code
|
142
|
-
end
|
143
|
-
|
144
140
|
# @private returns extension with separator defined
|
145
141
|
def formatted_extension
|
146
142
|
return '' if @extension.nil? || @extension.empty?
|
@@ -159,7 +155,7 @@ module Phonelib
|
|
159
155
|
data[Core::NATIONAL_PREFIX_RULE] || '$1'
|
160
156
|
|
161
157
|
# change rule's constants to values
|
162
|
-
rule.gsub
|
158
|
+
rule = rule.gsub(/(\$NP|\$FG)/, '$NP' => prefix, '$FG' => '$1')
|
163
159
|
|
164
160
|
# add space to format groups, change first group to rule,
|
165
161
|
format_string = format[:format].gsub(/(\d)\$/, '\\1 $')
|
data/lib/phonelib/version.rb
CHANGED
data/lib/phonelib.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# main Phonelib module definition
|
2
4
|
module Phonelib
|
3
5
|
# load phonelib classes/modules
|
@@ -12,9 +14,13 @@ module Phonelib
|
|
12
14
|
end
|
13
15
|
|
14
16
|
if defined?(ActiveModel) || defined?(Rails)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
autoload :PhoneValidator, 'validators/phone_validator'
|
18
|
+
|
19
|
+
if defined?(Rails)
|
20
|
+
class Phonelib::Railtie < Rails::Railtie
|
21
|
+
initializer 'phonelib' do |app|
|
22
|
+
app.config.eager_load_namespaces << Phonelib
|
23
|
+
end
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
metadata
CHANGED
@@ -1,127 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phonelib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vadim Senderovich
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rake
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "<"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '14.0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "<"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '14.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: nokogiri
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.13.0
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 1.13.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: pry
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rspec
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - '='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 2.14.1
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - '='
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 2.14.1
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: codeclimate-test-reporter
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 1.0.0
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 1.0.0
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: simplecov
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: rack-cache
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - '='
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '1.2'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - '='
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '1.2'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: json
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - '='
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: 2.3.1
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - '='
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: 2.3.1
|
11
|
+
date: 2025-05-14 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
125
13
|
description: |2
|
126
14
|
Google libphonenumber library was taken as a basis for
|
127
15
|
this gem. Gem uses its data file for validations and number formatting.
|
@@ -148,11 +36,11 @@ files:
|
|
148
36
|
- lib/phonelib/version.rb
|
149
37
|
- lib/tasks/phonelib_tasks.rake
|
150
38
|
- lib/validators/phone_validator.rb
|
151
|
-
- lib/validators/phone_validator3.rb
|
152
39
|
homepage: https://github.com/daddyz/phonelib
|
153
40
|
licenses:
|
154
41
|
- MIT
|
155
|
-
metadata:
|
42
|
+
metadata:
|
43
|
+
changelog_uri: https://github.com/daddyz/phonelib/releases/tag/v0.10.9
|
156
44
|
post_install_message:
|
157
45
|
rdoc_options:
|
158
46
|
- " --no-private - CHANGELOG.md --readme README.md"
|
@@ -169,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
57
|
- !ruby/object:Gem::Version
|
170
58
|
version: '0'
|
171
59
|
requirements: []
|
172
|
-
rubygems_version: 3.
|
60
|
+
rubygems_version: 3.1.6
|
173
61
|
signing_key:
|
174
62
|
specification_version: 4
|
175
63
|
summary: Gem validates phone numbers with Google libphonenumber database
|
@@ -1,130 +0,0 @@
|
|
1
|
-
# Validator class for phone validations
|
2
|
-
#
|
3
|
-
# ==== Examples
|
4
|
-
#
|
5
|
-
# Validates that attribute is a valid phone number.
|
6
|
-
# If empty value passed for attribute it fails.
|
7
|
-
#
|
8
|
-
# class Phone < ActiveRecord::Base
|
9
|
-
# attr_accessible :number
|
10
|
-
# validates :number, phone: true
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# Validates that attribute is a possible phone number.
|
14
|
-
# If empty value passed for attribute it fails.
|
15
|
-
#
|
16
|
-
# class Phone < ActiveRecord::Base
|
17
|
-
# attr_accessible :number
|
18
|
-
# validates :number, phone: { possible: true }
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# Validates that attribute is a valid phone number.
|
22
|
-
# Empty value is allowed to be passed.
|
23
|
-
#
|
24
|
-
# class Phone < ActiveRecord::Base
|
25
|
-
# attr_accessible :number
|
26
|
-
# validates :number, phone: { allow_blank: true }
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# Validates that attribute is a valid phone number of specified type(s).
|
30
|
-
# It is also possible to check that attribute is a possible number of specified
|
31
|
-
# type(s). Symbol or array accepted.
|
32
|
-
#
|
33
|
-
# class Phone < ActiveRecord::Base
|
34
|
-
# attr_accessible :number, :mobile
|
35
|
-
# validates :number, phone: { types: [:mobile, :fixed], allow_blank: true }
|
36
|
-
# validates :mobile, phone: { possible: true, types: :mobile }
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# validates that phone is valid and it is from specified country or countries
|
40
|
-
#
|
41
|
-
# class Phone < ActiveRecord::Base
|
42
|
-
# attr_accessible :number
|
43
|
-
# validates :number, phone: { countries: [:us, :ca] }
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
# Validates that attribute does not include an extension.
|
47
|
-
# The default setting is to allow extensions
|
48
|
-
#
|
49
|
-
# class Phone < ActiveRecord::Base
|
50
|
-
# attr_accessible :number
|
51
|
-
# validates :number, phone: { extensions: false }
|
52
|
-
# end
|
53
|
-
#
|
54
|
-
class PhoneValidator < ActiveModel::EachValidator
|
55
|
-
# Include all core methods
|
56
|
-
include Phonelib::Core
|
57
|
-
|
58
|
-
# Validation method
|
59
|
-
def validate_each(record, attribute, value)
|
60
|
-
return if options[:allow_blank] && value.blank?
|
61
|
-
|
62
|
-
@phone = parse(value, specified_country(record))
|
63
|
-
valid = phone_valid? && valid_types? && valid_country? && valid_extensions?
|
64
|
-
|
65
|
-
record.errors.add(attribute, message, **options) unless valid
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def message
|
71
|
-
options[:message] || :invalid
|
72
|
-
end
|
73
|
-
|
74
|
-
def phone_valid?
|
75
|
-
@phone.send(options[:possible] ? :possible? : :valid?)
|
76
|
-
end
|
77
|
-
|
78
|
-
def valid_types?
|
79
|
-
return true unless options[:types]
|
80
|
-
(phone_types & types).size > 0
|
81
|
-
end
|
82
|
-
|
83
|
-
def valid_country?
|
84
|
-
return true unless options[:countries]
|
85
|
-
(phone_countries & countries).size > 0
|
86
|
-
end
|
87
|
-
|
88
|
-
def valid_extensions?
|
89
|
-
return true if !options.has_key?(:extensions) || options[:extensions]
|
90
|
-
@phone.extension.empty?
|
91
|
-
end
|
92
|
-
|
93
|
-
def specified_country(record)
|
94
|
-
return unless options[:country_specifier]
|
95
|
-
|
96
|
-
if options[:country_specifier].is_a?(Symbol)
|
97
|
-
record.send(options[:country_specifier])
|
98
|
-
else
|
99
|
-
options[:country_specifier].call(record)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# @private
|
104
|
-
def phone_types
|
105
|
-
method = options[:possible] ? :possible_types : :types
|
106
|
-
phone_types = @phone.send(method)
|
107
|
-
if (phone_types & [Phonelib::Core::FIXED_OR_MOBILE]).size > 0
|
108
|
-
phone_types += [Phonelib::Core::FIXED_LINE, Phonelib::Core::MOBILE]
|
109
|
-
end
|
110
|
-
phone_types
|
111
|
-
end
|
112
|
-
|
113
|
-
# @private
|
114
|
-
def phone_countries
|
115
|
-
method = options[:possible] ? :countries : :valid_countries
|
116
|
-
@phone.send(method)
|
117
|
-
end
|
118
|
-
|
119
|
-
# @private
|
120
|
-
def types
|
121
|
-
types = options[:types].is_a?(Array) ? options[:types] : [options[:types]]
|
122
|
-
types.map(&:to_sym)
|
123
|
-
end
|
124
|
-
|
125
|
-
# @private
|
126
|
-
def countries
|
127
|
-
countries = options[:countries].is_a?(Array) ? options[:countries] : [options[:countries]]
|
128
|
-
countries.map { |c| c.to_s.upcase }
|
129
|
-
end
|
130
|
-
end
|