phonelib 0.9.1 → 0.10.14
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 +11 -1
- data/lib/phonelib/data_importer.rb +8 -3
- data/lib/phonelib/data_importer_helper.rb +9 -1
- data/lib/phonelib/phone.rb +3 -1
- data/lib/phonelib/phone_analyzer.rb +28 -12
- data/lib/phonelib/phone_analyzer_helper.rb +28 -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 +3 -5
- data/lib/tasks/phonelib_tasks.rake +2 -0
- data/lib/validators/phone_validator.rb +2 -0
- metadata +5 -145
- 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: 69fe9f8fd0ceac31311de09cba322da5100f51b00cec1c55ca84cea3f7f94a31
|
|
4
|
+
data.tar.gz: 0053507cf791eb8ad5281d8b28cf7438e526fa240745d109f508a265a2832f64
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 328abf4796f0713071a90b284a553c96e5c63abc3fff63d2d75ed01cb3d468d671409759db28e54c0280d4da20b53eef114573c0bf93e1393e5e5b43ae663459
|
|
7
|
+
data.tar.gz: bb648088c085a2fb4efaecfc82ec4088ede9c49b18b744b169fb653461c41ab12a356aec0c57e51abadc9b2af4708744fa571adc61df78f86db08866d4e81d08
|
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,3 +1,5 @@
|
|
|
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
|
|
@@ -200,7 +202,7 @@ module Phonelib
|
|
|
200
202
|
end
|
|
201
203
|
|
|
202
204
|
def add_additional_regex(country, type, national_regex)
|
|
203
|
-
return unless Phonelib::Core::TYPES_DESC.
|
|
205
|
+
return unless Phonelib::Core::TYPES_DESC.key?(type.to_sym)
|
|
204
206
|
return unless national_regex.is_a?(String)
|
|
205
207
|
@@phone_data = @@data_by_country_codes = nil
|
|
206
208
|
@@additional_regexes[country.to_s.upcase] ||= {}
|
|
@@ -379,6 +381,7 @@ module Phonelib
|
|
|
379
381
|
carrier_selection_codes: 'Carrier Selection codes',
|
|
380
382
|
area_code_optional: 'Are code optional'
|
|
381
383
|
}.freeze
|
|
384
|
+
TYPES_DESC_KEYS = TYPES_DESC.keys.freeze
|
|
382
385
|
|
|
383
386
|
# @private short codes types keys
|
|
384
387
|
SHORT_CODES = [
|
|
@@ -405,6 +408,13 @@ module Phonelib
|
|
|
405
408
|
# @private Extended data key for carrier in prefixes hash
|
|
406
409
|
EXT_CARRIER_KEY = :c
|
|
407
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
|
+
|
|
408
418
|
# method for parsing phone number.
|
|
409
419
|
# On first run fills @@phone_data with data present in yaml file
|
|
410
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
|
|
@@ -126,6 +128,9 @@ module Phonelib
|
|
|
126
128
|
if country[Core::NATIONAL_PREFIX_TRANSFORM_RULE]
|
|
127
129
|
country[Core::NATIONAL_PREFIX_TRANSFORM_RULE].gsub!('$', '\\')
|
|
128
130
|
end
|
|
131
|
+
if country[:id] == '001'
|
|
132
|
+
country[:id] = 'International ' + country[:country_code]
|
|
133
|
+
end
|
|
129
134
|
@data[country[:id]] = country
|
|
130
135
|
end
|
|
131
136
|
end
|
|
@@ -198,7 +203,7 @@ module Phonelib
|
|
|
198
203
|
require 'open-uri'
|
|
199
204
|
require 'csv'
|
|
200
205
|
io = URI.open('http://download.geonames.org/export/dump/countryInfo.txt')
|
|
201
|
-
csv = CSV.new(io, {col_sep: "\t"})
|
|
206
|
+
csv = CSV.new(io, **{col_sep: "\t"})
|
|
202
207
|
csv.each do |row|
|
|
203
208
|
next if row[0].nil? || row[0].start_with?('#') || row[0].empty? || row[0].size != 2
|
|
204
209
|
|
|
@@ -209,7 +214,7 @@ module Phonelib
|
|
|
209
214
|
# adds double country code flag in case country allows
|
|
210
215
|
def add_double_country_flag(country)
|
|
211
216
|
if DOUBLE_COUNTRY_CODES_COUNTRIES.include?(country[:id])
|
|
212
|
-
country[
|
|
217
|
+
country[Core::DOUBLE_COUNTRY_PREFIX_FLAG] = true
|
|
213
218
|
end
|
|
214
219
|
country
|
|
215
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 = {}
|
|
@@ -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,9 +69,15 @@ module Phonelib
|
|
|
68
69
|
end
|
|
69
70
|
|
|
70
71
|
# replacing national prefix to simplified format
|
|
71
|
-
def with_replaced_national_prefix(
|
|
72
|
-
return
|
|
73
|
-
phone =
|
|
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
|
+
|
|
74
81
|
pattern = cr("^(?:#{data[Core::NATIONAL_PREFIX_FOR_PARSING]})")
|
|
75
82
|
match = phone.match pattern
|
|
76
83
|
if match && match.captures.compact.size > 0
|
|
@@ -131,15 +138,20 @@ module Phonelib
|
|
|
131
138
|
countries_data.each_with_object({}) do |data, result|
|
|
132
139
|
key = data[:id]
|
|
133
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
|
|
134
145
|
if (!Phonelib.strict_double_prefix_check || key == country) && double_prefix_allowed?(data, phone, parsed && parsed[key])
|
|
135
|
-
|
|
146
|
+
parsed2 = parse_single_country(changed_dp_phone(key, phone), data)
|
|
147
|
+
parsed = parsed2 if parsed2 && parsed2[key] && parsed2[key][:valid].size > 0
|
|
136
148
|
end
|
|
137
149
|
result.merge!(parsed) unless parsed.nil?
|
|
138
150
|
end.compact
|
|
139
151
|
end
|
|
140
152
|
|
|
141
153
|
def country_code_candidates_for(phone)
|
|
142
|
-
stripped_phone = phone.gsub(/^(#{Phonelib.phone_data_int_prefixes})
|
|
154
|
+
stripped_phone = phone.gsub(cr("Phonelib.phone_data_int_prefixes") { /^(#{Phonelib.phone_data_int_prefixes})/ }, '')
|
|
143
155
|
((1..3).map { |length| phone[0, length] } + (1..3).map { |length| stripped_phone[0, length] }).uniq
|
|
144
156
|
end
|
|
145
157
|
|
|
@@ -150,13 +162,13 @@ module Phonelib
|
|
|
150
162
|
# * +phone+ - phone number for parsing
|
|
151
163
|
# * +data+ - country data to be based on for creating e164 representation
|
|
152
164
|
def convert_to_e164(phone, data)
|
|
153
|
-
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?)
|
|
154
166
|
case
|
|
155
167
|
when match
|
|
156
168
|
"#{data[Core::COUNTRY_CODE]}#{match.to_a.last}"
|
|
157
169
|
when phone.match(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"))
|
|
158
170
|
phone.sub(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"), Core::PLUS_SIGN)
|
|
159
|
-
when
|
|
171
|
+
when original_starts_with_plus_or_double_zero? && phone.start_with?(data[Core::COUNTRY_CODE])
|
|
160
172
|
phone
|
|
161
173
|
else
|
|
162
174
|
"#{data[Core::COUNTRY_CODE]}#{phone}"
|
|
@@ -172,7 +184,11 @@ module Phonelib
|
|
|
172
184
|
# * +not_valid+ - specifies that number is not valid by general desc pattern
|
|
173
185
|
def national_and_data(data, country_match, not_valid = false)
|
|
174
186
|
result = data.select { |k, _v| k != :types && k != :formats }
|
|
175
|
-
|
|
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]
|
|
176
192
|
result[:national] = phone
|
|
177
193
|
result[:format] = number_format(phone, data[Core::FORMATS])
|
|
178
194
|
result.merge! all_number_types(phone, data[Core::TYPES], not_valid)
|
|
@@ -213,7 +229,7 @@ module Phonelib
|
|
|
213
229
|
def number_format(national, format_data)
|
|
214
230
|
format_data && format_data.find do |format|
|
|
215
231
|
(format[Core::LEADING_DIGITS].nil? || \
|
|
216
|
-
national.match(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
|
|
232
|
+
national.match?(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
|
|
217
233
|
national.match(cr("^(#{format[Core::PATTERN]})$"))
|
|
218
234
|
end || Core::DEFAULT_NUMBER_FORMAT
|
|
219
235
|
end
|
|
@@ -234,7 +250,7 @@ module Phonelib
|
|
|
234
250
|
type_regex(patterns, Core::VALID_PATTERN)
|
|
235
251
|
]
|
|
236
252
|
else
|
|
237
|
-
|
|
253
|
+
Core::NIL_RESULT_ARRAY
|
|
238
254
|
end
|
|
239
255
|
end
|
|
240
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
|
|
@@ -14,8 +16,8 @@ module Phonelib
|
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
def
|
|
18
|
-
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'
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
# converts symbols in phone to numbers
|
|
@@ -52,14 +54,15 @@ module Phonelib
|
|
|
52
54
|
|
|
53
55
|
# caches regular expression, reusing it for later lookups
|
|
54
56
|
def cr(regexp)
|
|
55
|
-
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(regexp).freeze
|
|
57
|
+
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(block_given? ? yield(regexp) : regexp).freeze
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
# defines whether country can have double country prefix in number
|
|
59
61
|
def country_can_dp?(country)
|
|
60
62
|
Phonelib.phone_data[country] &&
|
|
61
63
|
Phonelib.phone_data[country][Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
|
62
|
-
!
|
|
64
|
+
!original_starts_with_plus_or_double_zero? &&
|
|
65
|
+
original_s.start_with?(Phonelib.phone_data[country][Core::COUNTRY_CODE])
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
# changes phone to with/without double country prefix
|
|
@@ -87,7 +90,7 @@ module Phonelib
|
|
|
87
90
|
data[Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
|
88
91
|
phone =~ cr("^#{data[Core::COUNTRY_CODE]}") &&
|
|
89
92
|
parsed && (parsed[:valid].nil? || parsed[:valid].empty?) &&
|
|
90
|
-
!
|
|
93
|
+
!original_starts_with_plus_or_double_zero?
|
|
91
94
|
end
|
|
92
95
|
|
|
93
96
|
# Returns original number passed if it's a string or empty string otherwise
|
|
@@ -101,7 +104,8 @@ module Phonelib
|
|
|
101
104
|
#
|
|
102
105
|
# * +country+ - country passed for parsing
|
|
103
106
|
def country_or_default_country(country)
|
|
104
|
-
country ||= (
|
|
107
|
+
country ||= (original_starts_with_plus_or_double_zero? ? nil : Phonelib.default_country)
|
|
108
|
+
|
|
105
109
|
if country.is_a?(Array)
|
|
106
110
|
country.compact.map { |e| e.to_s.upcase }
|
|
107
111
|
else
|
|
@@ -117,14 +121,15 @@ module Phonelib
|
|
|
117
121
|
# * +data+ - country data hash
|
|
118
122
|
# * +country_optional+ - whether to put country code as optional group
|
|
119
123
|
def full_regex_for_data(data, type, country_optional = true)
|
|
120
|
-
regex = []
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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 << "$"
|
|
126
131
|
|
|
127
|
-
cr(
|
|
132
|
+
cr(regex)
|
|
128
133
|
end
|
|
129
134
|
|
|
130
135
|
# Returns regex for type with special types if needed
|
|
@@ -134,11 +139,11 @@ module Phonelib
|
|
|
134
139
|
# * +data+ - country types data for single type
|
|
135
140
|
# * +type+ - possible or valid regex type needed
|
|
136
141
|
def type_regex(data, type)
|
|
137
|
-
regex = [data[type]]
|
|
138
142
|
if Phonelib.parse_special && data[Core::SHORT] && data[Core::SHORT][type]
|
|
139
|
-
|
|
143
|
+
"#{data[type]}|#{data[Core::SHORT][type]}"
|
|
144
|
+
else
|
|
145
|
+
data[type]
|
|
140
146
|
end
|
|
141
|
-
regex.join('|')
|
|
142
147
|
end
|
|
143
148
|
|
|
144
149
|
# Check if phone match country data
|
|
@@ -148,9 +153,7 @@ module Phonelib
|
|
|
148
153
|
# * +phone+ - phone number for parsing
|
|
149
154
|
# * +data+ - country data
|
|
150
155
|
def phone_match_data?(phone, data, possible = false)
|
|
151
|
-
|
|
152
|
-
inter_prefix = "(#{data[Core::INTERNATIONAL_PREFIX]})?"
|
|
153
|
-
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]}"))
|
|
154
157
|
|
|
155
158
|
type = possible ? Core::POSSIBLE_PATTERN : Core::VALID_PATTERN
|
|
156
159
|
phone.match full_regex_for_data(data, type, false)
|
|
@@ -159,10 +162,9 @@ module Phonelib
|
|
|
159
162
|
# checks if types has both :mobile and :fixed_line and replaces it with
|
|
160
163
|
# :fixed_or_mobile in case both present
|
|
161
164
|
def sanitize_fixed_mobile(types)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
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
|
|
166
168
|
end
|
|
167
169
|
end
|
|
168
170
|
types
|
|
@@ -176,7 +178,7 @@ module Phonelib
|
|
|
176
178
|
def types_for_check(data)
|
|
177
179
|
exclude_list = PhoneAnalyzer::NOT_FOR_CHECK
|
|
178
180
|
exclude_list += Phonelib::Core::SHORT_CODES unless Phonelib.parse_special
|
|
179
|
-
Core::
|
|
181
|
+
Core::TYPES_DESC_KEYS - exclude_list + fixed_and_mobile_keys(data)
|
|
180
182
|
end
|
|
181
183
|
|
|
182
184
|
# Checks if fixed line pattern and mobile pattern are the same and returns
|
|
@@ -187,9 +189,9 @@ module Phonelib
|
|
|
187
189
|
# * +data+ - country data
|
|
188
190
|
def fixed_and_mobile_keys(data)
|
|
189
191
|
if data[Core::FIXED_LINE] == data[Core::MOBILE]
|
|
190
|
-
|
|
192
|
+
Core::FIXED_OR_MOBILE_ARRAY
|
|
191
193
|
else
|
|
192
|
-
|
|
194
|
+
Core::FIXED_LINE_OR_MOBILE_ARRAY
|
|
193
195
|
end
|
|
194
196
|
end
|
|
195
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
|
|
@@ -35,7 +37,7 @@ module Phonelib
|
|
|
35
37
|
return @country_code if @country_code
|
|
36
38
|
|
|
37
39
|
code = Phonelib.phone_data[country] && Phonelib.phone_data[country][Core::COUNTRY_CODE]
|
|
38
|
-
return @country_code = code unless code == '1' && Phonelib.phone_data[country][Core::LEADING_DIGITS]
|
|
40
|
+
return @country_code = code unless code == '1' && Phonelib.phone_data[country][Core::LEADING_DIGITS] && Phonelib.phone_data[country][Core::LEADING_DIGITS] =~ /[\[\|]/
|
|
39
41
|
|
|
40
42
|
match = e164.match(/\A\+(1(#{Phonelib.phone_data[country][Core::LEADING_DIGITS]}))/)
|
|
41
43
|
if 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,11 +14,7 @@ module Phonelib
|
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
if defined?(ActiveModel) || defined?(Rails)
|
|
15
|
-
|
|
16
|
-
autoload :PhoneValidator, 'validators/phone_validator3'
|
|
17
|
-
else
|
|
18
|
-
autoload :PhoneValidator, 'validators/phone_validator'
|
|
19
|
-
end
|
|
17
|
+
autoload :PhoneValidator, 'validators/phone_validator'
|
|
20
18
|
|
|
21
19
|
if defined?(Rails)
|
|
22
20
|
class Phonelib::Railtie < Rails::Railtie
|
metadata
CHANGED
|
@@ -1,155 +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.14
|
|
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.15'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '1.15'
|
|
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.9
|
|
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.9
|
|
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: benchmark-ips
|
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
|
100
|
-
requirements:
|
|
101
|
-
- - ">="
|
|
102
|
-
- !ruby/object:Gem::Version
|
|
103
|
-
version: '0'
|
|
104
|
-
type: :development
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - ">="
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0'
|
|
111
|
-
- !ruby/object:Gem::Dependency
|
|
112
|
-
name: benchmark-memory
|
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - ">="
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0'
|
|
118
|
-
type: :development
|
|
119
|
-
prerelease: false
|
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
-
requirements:
|
|
122
|
-
- - ">="
|
|
123
|
-
- !ruby/object:Gem::Version
|
|
124
|
-
version: '0'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: rack-cache
|
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
|
128
|
-
requirements:
|
|
129
|
-
- - '='
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: '1.2'
|
|
132
|
-
type: :development
|
|
133
|
-
prerelease: false
|
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
-
requirements:
|
|
136
|
-
- - '='
|
|
137
|
-
- !ruby/object:Gem::Version
|
|
138
|
-
version: '1.2'
|
|
139
|
-
- !ruby/object:Gem::Dependency
|
|
140
|
-
name: json
|
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
|
142
|
-
requirements:
|
|
143
|
-
- - '='
|
|
144
|
-
- !ruby/object:Gem::Version
|
|
145
|
-
version: 2.3.1
|
|
146
|
-
type: :development
|
|
147
|
-
prerelease: false
|
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
-
requirements:
|
|
150
|
-
- - '='
|
|
151
|
-
- !ruby/object:Gem::Version
|
|
152
|
-
version: 2.3.1
|
|
11
|
+
date: 2025-11-24 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
153
13
|
description: |2
|
|
154
14
|
Google libphonenumber library was taken as a basis for
|
|
155
15
|
this gem. Gem uses its data file for validations and number formatting.
|
|
@@ -176,11 +36,11 @@ files:
|
|
|
176
36
|
- lib/phonelib/version.rb
|
|
177
37
|
- lib/tasks/phonelib_tasks.rake
|
|
178
38
|
- lib/validators/phone_validator.rb
|
|
179
|
-
- lib/validators/phone_validator3.rb
|
|
180
39
|
homepage: https://github.com/daddyz/phonelib
|
|
181
40
|
licenses:
|
|
182
41
|
- MIT
|
|
183
|
-
metadata:
|
|
42
|
+
metadata:
|
|
43
|
+
changelog_uri: https://github.com/daddyz/phonelib/releases/tag/v0.10.14
|
|
184
44
|
post_install_message:
|
|
185
45
|
rdoc_options:
|
|
186
46
|
- " --no-private - CHANGELOG.md --readme README.md"
|
|
@@ -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
|