phonelib 0.8.4 → 0.10.11
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 +24 -1
- data/lib/phonelib/data_importer.rb +9 -4
- data/lib/phonelib/data_importer_helper.rb +9 -1
- data/lib/phonelib/phone.rb +3 -1
- data/lib/phonelib/phone_analyzer.rb +29 -12
- data/lib/phonelib/phone_analyzer_helper.rb +29 -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 -146
- 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: 700f7d81fef64c646c34a58849db498373d5dbbd02c1325fc34b57129617adab
|
4
|
+
data.tar.gz: 4d56e6bc50304ffea7aa9a2910b66db0ae154b6846b34186f86b04c24e12cdde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74c428da069f2eca4919c3db99fd13a18505dc55afa64c6b1f595135dfa2abb33768af6266630f203a03919d94514bf3f28cdb5f5279bd6e81f8e30713424822
|
7
|
+
data.tar.gz: 505ee8b7e47951ad862ca8d586127d93b1850e832130337de320d7e028d32598d96853204fb0e4c8875884c67d2fe7bee7868cdda72f4295461fb0c1db304a82
|
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,9 +1,24 @@
|
|
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
|
@@ -187,7 +202,7 @@ module Phonelib
|
|
187
202
|
end
|
188
203
|
|
189
204
|
def add_additional_regex(country, type, national_regex)
|
190
|
-
return unless Phonelib::Core::TYPES_DESC.
|
205
|
+
return unless Phonelib::Core::TYPES_DESC.key?(type.to_sym)
|
191
206
|
return unless national_regex.is_a?(String)
|
192
207
|
@@phone_data = @@data_by_country_codes = nil
|
193
208
|
@@additional_regexes[country.to_s.upcase] ||= {}
|
@@ -366,6 +381,7 @@ module Phonelib
|
|
366
381
|
carrier_selection_codes: 'Carrier Selection codes',
|
367
382
|
area_code_optional: 'Are code optional'
|
368
383
|
}.freeze
|
384
|
+
TYPES_DESC_KEYS = TYPES_DESC.keys.freeze
|
369
385
|
|
370
386
|
# @private short codes types keys
|
371
387
|
SHORT_CODES = [
|
@@ -392,6 +408,13 @@ module Phonelib
|
|
392
408
|
# @private Extended data key for carrier in prefixes hash
|
393
409
|
EXT_CARRIER_KEY = :c
|
394
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
|
+
|
395
418
|
# method for parsing phone number.
|
396
419
|
# On first run fills @@phone_data with data present in yaml file
|
397
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
|
@@ -197,8 +202,8 @@ module Phonelib
|
|
197
202
|
|
198
203
|
require 'open-uri'
|
199
204
|
require 'csv'
|
200
|
-
io = open('http://
|
201
|
-
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"})
|
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,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
|
@@ -130,15 +138,20 @@ module Phonelib
|
|
130
138
|
countries_data.each_with_object({}) do |data, result|
|
131
139
|
key = data[:id]
|
132
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
|
133
145
|
if (!Phonelib.strict_double_prefix_check || key == country) && double_prefix_allowed?(data, phone, parsed && parsed[key])
|
134
|
-
|
146
|
+
parsed2 = parse_single_country(changed_dp_phone(key, phone), data)
|
147
|
+
parsed = parsed2 if parsed2 && parsed2[key] && parsed2[key][:valid].size > 0
|
135
148
|
end
|
136
149
|
result.merge!(parsed) unless parsed.nil?
|
137
150
|
end.compact
|
138
151
|
end
|
139
152
|
|
140
153
|
def country_code_candidates_for(phone)
|
141
|
-
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})/ }, '')
|
142
155
|
((1..3).map { |length| phone[0, length] } + (1..3).map { |length| stripped_phone[0, length] }).uniq
|
143
156
|
end
|
144
157
|
|
@@ -149,13 +162,13 @@ module Phonelib
|
|
149
162
|
# * +phone+ - phone number for parsing
|
150
163
|
# * +data+ - country data to be based on for creating e164 representation
|
151
164
|
def convert_to_e164(phone, data)
|
152
|
-
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?)
|
153
166
|
case
|
154
167
|
when match
|
155
168
|
"#{data[Core::COUNTRY_CODE]}#{match.to_a.last}"
|
156
169
|
when phone.match(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"))
|
157
170
|
phone.sub(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"), Core::PLUS_SIGN)
|
158
|
-
when
|
171
|
+
when original_starts_with_plus_or_double_zero? && phone.start_with?(data[Core::COUNTRY_CODE])
|
159
172
|
phone
|
160
173
|
else
|
161
174
|
"#{data[Core::COUNTRY_CODE]}#{phone}"
|
@@ -171,7 +184,11 @@ module Phonelib
|
|
171
184
|
# * +not_valid+ - specifies that number is not valid by general desc pattern
|
172
185
|
def national_and_data(data, country_match, not_valid = false)
|
173
186
|
result = data.select { |k, _v| k != :types && k != :formats }
|
174
|
-
|
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]
|
175
192
|
result[:national] = phone
|
176
193
|
result[:format] = number_format(phone, data[Core::FORMATS])
|
177
194
|
result.merge! all_number_types(phone, data[Core::TYPES], not_valid)
|
@@ -212,7 +229,7 @@ module Phonelib
|
|
212
229
|
def number_format(national, format_data)
|
213
230
|
format_data && format_data.find do |format|
|
214
231
|
(format[Core::LEADING_DIGITS].nil? || \
|
215
|
-
national.match(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
|
232
|
+
national.match?(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
|
216
233
|
national.match(cr("^(#{format[Core::PATTERN]})$"))
|
217
234
|
end || Core::DEFAULT_NUMBER_FORMAT
|
218
235
|
end
|
@@ -233,7 +250,7 @@ module Phonelib
|
|
233
250
|
type_regex(patterns, Core::VALID_PATTERN)
|
234
251
|
]
|
235
252
|
else
|
236
|
-
|
253
|
+
Core::NIL_RESULT_ARRAY
|
237
254
|
end
|
238
255
|
end
|
239
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
|
@@ -8,13 +10,14 @@ module Phonelib
|
|
8
10
|
result
|
9
11
|
else
|
10
12
|
matched_countries = country_or_default_country(nil) & result.keys
|
13
|
+
matched_countries = result.keys.take(1) if matched_countries.size == 0
|
11
14
|
result = result.keep_if {|k, _v| matched_countries.include?(k) } if matched_countries
|
12
15
|
Hash[result.take(1)]
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
|
-
def
|
17
|
-
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'
|
18
21
|
end
|
19
22
|
|
20
23
|
# converts symbols in phone to numbers
|
@@ -51,14 +54,15 @@ module Phonelib
|
|
51
54
|
|
52
55
|
# caches regular expression, reusing it for later lookups
|
53
56
|
def cr(regexp)
|
54
|
-
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(regexp).freeze
|
57
|
+
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(block_given? ? yield(regexp) : regexp).freeze
|
55
58
|
end
|
56
59
|
|
57
60
|
# defines whether country can have double country prefix in number
|
58
61
|
def country_can_dp?(country)
|
59
62
|
Phonelib.phone_data[country] &&
|
60
63
|
Phonelib.phone_data[country][Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
61
|
-
!
|
64
|
+
!original_starts_with_plus_or_double_zero? &&
|
65
|
+
original_s.start_with?(Phonelib.phone_data[country][Core::COUNTRY_CODE])
|
62
66
|
end
|
63
67
|
|
64
68
|
# changes phone to with/without double country prefix
|
@@ -86,7 +90,7 @@ module Phonelib
|
|
86
90
|
data[Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
87
91
|
phone =~ cr("^#{data[Core::COUNTRY_CODE]}") &&
|
88
92
|
parsed && (parsed[:valid].nil? || parsed[:valid].empty?) &&
|
89
|
-
!
|
93
|
+
!original_starts_with_plus_or_double_zero?
|
90
94
|
end
|
91
95
|
|
92
96
|
# Returns original number passed if it's a string or empty string otherwise
|
@@ -100,7 +104,8 @@ module Phonelib
|
|
100
104
|
#
|
101
105
|
# * +country+ - country passed for parsing
|
102
106
|
def country_or_default_country(country)
|
103
|
-
country ||= (
|
107
|
+
country ||= (original_starts_with_plus_or_double_zero? ? nil : Phonelib.default_country)
|
108
|
+
|
104
109
|
if country.is_a?(Array)
|
105
110
|
country.compact.map { |e| e.to_s.upcase }
|
106
111
|
else
|
@@ -116,14 +121,15 @@ module Phonelib
|
|
116
121
|
# * +data+ - country data hash
|
117
122
|
# * +country_optional+ - whether to put country code as optional group
|
118
123
|
def full_regex_for_data(data, type, country_optional = true)
|
119
|
-
regex = []
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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 << "$"
|
125
131
|
|
126
|
-
cr(
|
132
|
+
cr(regex)
|
127
133
|
end
|
128
134
|
|
129
135
|
# Returns regex for type with special types if needed
|
@@ -133,11 +139,11 @@ module Phonelib
|
|
133
139
|
# * +data+ - country types data for single type
|
134
140
|
# * +type+ - possible or valid regex type needed
|
135
141
|
def type_regex(data, type)
|
136
|
-
regex = [data[type]]
|
137
142
|
if Phonelib.parse_special && data[Core::SHORT] && data[Core::SHORT][type]
|
138
|
-
|
143
|
+
"#{data[type]}|#{data[Core::SHORT][type]}"
|
144
|
+
else
|
145
|
+
data[type]
|
139
146
|
end
|
140
|
-
regex.join('|')
|
141
147
|
end
|
142
148
|
|
143
149
|
# Check if phone match country data
|
@@ -147,9 +153,7 @@ module Phonelib
|
|
147
153
|
# * +phone+ - phone number for parsing
|
148
154
|
# * +data+ - country data
|
149
155
|
def phone_match_data?(phone, data, possible = false)
|
150
|
-
|
151
|
-
inter_prefix = "(#{data[Core::INTERNATIONAL_PREFIX]})?"
|
152
|
-
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]}"))
|
153
157
|
|
154
158
|
type = possible ? Core::POSSIBLE_PATTERN : Core::VALID_PATTERN
|
155
159
|
phone.match full_regex_for_data(data, type, false)
|
@@ -158,10 +162,9 @@ module Phonelib
|
|
158
162
|
# checks if types has both :mobile and :fixed_line and replaces it with
|
159
163
|
# :fixed_or_mobile in case both present
|
160
164
|
def sanitize_fixed_mobile(types)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
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
|
165
168
|
end
|
166
169
|
end
|
167
170
|
types
|
@@ -175,7 +178,7 @@ module Phonelib
|
|
175
178
|
def types_for_check(data)
|
176
179
|
exclude_list = PhoneAnalyzer::NOT_FOR_CHECK
|
177
180
|
exclude_list += Phonelib::Core::SHORT_CODES unless Phonelib.parse_special
|
178
|
-
Core::
|
181
|
+
Core::TYPES_DESC_KEYS - exclude_list + fixed_and_mobile_keys(data)
|
179
182
|
end
|
180
183
|
|
181
184
|
# Checks if fixed line pattern and mobile pattern are the same and returns
|
@@ -186,9 +189,9 @@ module Phonelib
|
|
186
189
|
# * +data+ - country data
|
187
190
|
def fixed_and_mobile_keys(data)
|
188
191
|
if data[Core::FIXED_LINE] == data[Core::MOBILE]
|
189
|
-
|
192
|
+
Core::FIXED_OR_MOBILE_ARRAY
|
190
193
|
else
|
191
|
-
|
194
|
+
Core::FIXED_LINE_OR_MOBILE_ARRAY
|
192
195
|
end
|
193
196
|
end
|
194
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,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,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.11
|
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: 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-07-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.11
|
184
44
|
post_install_message:
|
185
45
|
rdoc_options:
|
186
46
|
- " --no-private - CHANGELOG.md --readme README.md"
|
@@ -197,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
197
57
|
- !ruby/object:Gem::Version
|
198
58
|
version: '0'
|
199
59
|
requirements: []
|
200
|
-
rubygems_version: 3.
|
60
|
+
rubygems_version: 3.1.6
|
201
61
|
signing_key:
|
202
62
|
specification_version: 4
|
203
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
|