phonelib 0.6.10 → 0.6.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/data/extended_data.dat +0 -0
- data/data/phone_data.dat +0 -0
- data/lib/phonelib/core.rb +11 -6
- data/lib/phonelib/data_importer.rb +22 -56
- data/lib/phonelib/data_importer_helper.rb +29 -3
- data/lib/phonelib/phone.rb +5 -5
- data/lib/phonelib/phone_analyzer.rb +28 -35
- data/lib/phonelib/phone_analyzer_helper.rb +38 -31
- data/lib/phonelib/phone_formatter.rb +15 -15
- data/lib/phonelib/version.rb +1 -1
- data/lib/validators/phone_validator.rb +16 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa38e795e6d299330a008f39f359cdd29ba221fc
|
4
|
+
data.tar.gz: 9bf8ce164c4838722a06aff72037147852100244
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61367e9387768b79bbb6d43c5b9718098c4a6772281b24111acd9bce2e6dadcfe08b9aa54aa64e4463b4457e593d4fb08e92da6e15d537dcf083df14d3c5e3f1
|
7
|
+
data.tar.gz: 58ba6833ce8aec90171faacef75b9fbfd865cd236242cb621c4fff26e457b4dc387c49d9e2293c7b9e0d77eac36d8442cbba830f1fe68011fff4539f54374758
|
data/data/extended_data.dat
CHANGED
Binary file
|
data/data/phone_data.dat
CHANGED
Binary file
|
data/lib/phonelib/core.rb
CHANGED
@@ -218,23 +218,28 @@ module Phonelib
|
|
218
218
|
# @private Pattern key
|
219
219
|
PATTERN = :pattern
|
220
220
|
|
221
|
+
PLUS_SIGN = '+'.freeze
|
222
|
+
|
223
|
+
# @private vanity numbers 4 keys letters
|
224
|
+
VANITY_4_LETTERS_KEYS_REGEX = /[SVYZ]/.freeze
|
225
|
+
|
221
226
|
# @private Area code possible types
|
222
|
-
AREA_CODE_TYPES = [FIXED_LINE, FIXED_OR_MOBILE, MOBILE]
|
227
|
+
AREA_CODE_TYPES = [FIXED_LINE, FIXED_OR_MOBILE, MOBILE].freeze
|
223
228
|
|
224
229
|
# @private Area code countries for mobile type
|
225
|
-
AREA_CODE_MOBILE_COUNTRIES = %w(AR MX BR)
|
230
|
+
AREA_CODE_MOBILE_COUNTRIES = %w(AR MX BR).freeze
|
226
231
|
|
227
232
|
# @private Area code mobile phone token
|
228
233
|
AREA_CODE_MOBILE_TOKENS = {
|
229
234
|
'MX' => '1',
|
230
235
|
'AR' => '9'
|
231
|
-
}
|
236
|
+
}.freeze
|
232
237
|
|
233
238
|
# @private Default number formatting data hash
|
234
239
|
DEFAULT_NUMBER_FORMAT = {
|
235
240
|
pattern: '(\\d+)(\\d{3})(\\d{4})',
|
236
241
|
format: '$1 $2 $3'
|
237
|
-
}
|
242
|
+
}.freeze
|
238
243
|
|
239
244
|
# @private hash of all phone types with human representation
|
240
245
|
TYPES_DESC = {
|
@@ -261,7 +266,7 @@ module Phonelib
|
|
261
266
|
standard_rate: 'Standard Rate Destination',
|
262
267
|
carrier_selection_codes: 'Carrier Selection codes',
|
263
268
|
area_code_optional: 'Are code optional'
|
264
|
-
}
|
269
|
+
}.freeze
|
265
270
|
|
266
271
|
# @private short codes types keys
|
267
272
|
SHORT_CODES = [
|
@@ -269,7 +274,7 @@ module Phonelib
|
|
269
274
|
:expanded_emergency, :no_international_dialling, :carrier_services,
|
270
275
|
:directory_services, :standard_rate, :carrier_selection_codes,
|
271
276
|
:area_code_optional
|
272
|
-
]
|
277
|
+
].freeze
|
273
278
|
|
274
279
|
# @private Extended data prefixes hash key
|
275
280
|
EXT_PREFIXES = :prefixes
|
@@ -58,29 +58,7 @@ module Phonelib
|
|
58
58
|
import_timezone_data
|
59
59
|
import_carrier_data
|
60
60
|
save_data_file
|
61
|
-
|
62
|
-
|
63
|
-
# method saves parsed data to data files
|
64
|
-
def save_data_file
|
65
|
-
data_file =
|
66
|
-
"#{File.dirname(__FILE__)}/../../#{Phonelib::Core::FILE_MAIN_DATA}"
|
67
|
-
|
68
|
-
File.open(data_file, 'wb+') do |f|
|
69
|
-
Marshal.dump(@data, f)
|
70
|
-
end
|
71
|
-
|
72
|
-
ext_file =
|
73
|
-
"#{File.dirname(__FILE__)}/../../#{Phonelib::Core::FILE_EXT_DATA}"
|
74
|
-
extended = {
|
75
|
-
Phonelib::Core::EXT_PREFIXES => @prefixes,
|
76
|
-
Phonelib::Core::EXT_GEO_NAMES => @geo_names,
|
77
|
-
Phonelib::Core::EXT_TIMEZONES => @timezones,
|
78
|
-
Phonelib::Core::EXT_CARRIERS => @carriers
|
79
|
-
}
|
80
|
-
File.open(ext_file, 'wb+') do |f|
|
81
|
-
Marshal.dump(extended, f)
|
82
|
-
end
|
83
|
-
puts 'DATA SAVED'
|
61
|
+
save_extended_data_file
|
84
62
|
end
|
85
63
|
|
86
64
|
# method clones libphonenumber repo to local dir
|
@@ -97,10 +75,8 @@ module Phonelib
|
|
97
75
|
puts 'IMPORTING MAIN DATA'
|
98
76
|
main_from_xml("#{@destination}#{MAIN_FILE}").elements.each do |el|
|
99
77
|
# each country
|
100
|
-
country =
|
101
|
-
country
|
102
|
-
|
103
|
-
country.merge! get_types_and_formats(el.children)
|
78
|
+
country = hash_from_xml(el, :attributes)
|
79
|
+
country.merge! types_and_formats(el.children)
|
104
80
|
country = add_double_country_flag country
|
105
81
|
@data[country[:id]] = country
|
106
82
|
end
|
@@ -111,17 +87,11 @@ module Phonelib
|
|
111
87
|
puts 'IMPORTING SHORT NUMBER DATA'
|
112
88
|
main_from_xml("#{@destination}#{SHORT_DATA_FILE}").elements.each do |el|
|
113
89
|
# each country
|
114
|
-
country =
|
115
|
-
country
|
116
|
-
|
117
|
-
country.merge! get_types_and_formats(el.children)
|
90
|
+
country = hash_from_xml(el, :attributes)
|
91
|
+
country.merge! types_and_formats(el.children)
|
118
92
|
|
119
93
|
country[:types].each do |type, data|
|
120
|
-
|
121
|
-
merge_short_with_main_type(country[:id], type, data)
|
122
|
-
else
|
123
|
-
@data[country[:id]][:types][type] = data
|
124
|
-
end
|
94
|
+
merge_short_with_main_type(country[:id], type, data)
|
125
95
|
end
|
126
96
|
end
|
127
97
|
end
|
@@ -134,10 +104,8 @@ module Phonelib
|
|
134
104
|
el.children.each do |phone_type|
|
135
105
|
next unless phone_type.name == 'availableFormats'
|
136
106
|
|
137
|
-
|
138
|
-
|
139
|
-
country_code = el.attribute('countryCode').value
|
140
|
-
@data[get_country_by_code(country_code)][:formats] += formats
|
107
|
+
country_code = country_by_code(el.attribute('countryCode').value)
|
108
|
+
@data[country_code][:formats] += parse_formats(phone_type.children)
|
141
109
|
end
|
142
110
|
end
|
143
111
|
end
|
@@ -175,26 +143,24 @@ module Phonelib
|
|
175
143
|
end
|
176
144
|
|
177
145
|
# method extracts formats and types from xml data
|
178
|
-
def
|
179
|
-
|
180
|
-
formats = []
|
146
|
+
def types_and_formats(children)
|
147
|
+
result = { types: {}, formats: [] }
|
181
148
|
|
182
149
|
without_comments(children).each do |phone_type|
|
183
150
|
if phone_type.name == 'availableFormats'
|
184
|
-
formats = parse_formats(phone_type.children)
|
151
|
+
result[:formats] = parse_formats(phone_type.children)
|
185
152
|
else
|
186
|
-
types[name2sym(phone_type.name)] =
|
187
|
-
|
153
|
+
result[:types][name2sym(phone_type.name)] =
|
154
|
+
hash_from_xml(phone_type, :element)
|
188
155
|
end
|
189
156
|
end
|
190
157
|
|
191
|
-
|
192
|
-
|
193
|
-
{ types: types, formats: formats }
|
158
|
+
fill_possible_to_types_if_nil(result)
|
194
159
|
end
|
195
160
|
|
196
161
|
# method adds short number patterns to main data parsed from main xml
|
197
162
|
def merge_short_with_main_type(country_id, type, data)
|
163
|
+
@data[country_id][:types][type] ||= {}
|
198
164
|
data.each do |k, v|
|
199
165
|
if @data[country_id][:types][type][k]
|
200
166
|
@data[country_id][:types][type][k] += "|#{v}"
|
@@ -205,20 +171,20 @@ module Phonelib
|
|
205
171
|
end
|
206
172
|
|
207
173
|
# adds possible pattern in case it doesn't exists
|
208
|
-
def
|
209
|
-
types.each do |type, data|
|
174
|
+
def fill_possible_to_types_if_nil(result)
|
175
|
+
result[:types].each do |type, data|
|
210
176
|
if data[Core::VALID_PATTERN] && !data[Core::POSSIBLE_PATTERN]
|
211
|
-
types[type][Core::POSSIBLE_PATTERN] =
|
177
|
+
result[:types][type][Core::POSSIBLE_PATTERN] =
|
212
178
|
data[Core::VALID_PATTERN]
|
213
179
|
end
|
214
180
|
end
|
215
|
-
|
181
|
+
result
|
216
182
|
end
|
217
183
|
|
218
184
|
# method parses xml for formats data
|
219
185
|
def parse_formats(formats_children)
|
220
186
|
without_comments(formats_children).map do |format|
|
221
|
-
current_format =
|
187
|
+
current_format = hash_from_xml(format, :children)
|
222
188
|
|
223
189
|
without_comments(format.children).each do |f|
|
224
190
|
current_format[name2sym(f.name)] =
|
@@ -226,7 +192,7 @@ module Phonelib
|
|
226
192
|
end
|
227
193
|
|
228
194
|
current_format
|
229
|
-
end
|
195
|
+
end
|
230
196
|
end
|
231
197
|
|
232
198
|
# method updates data from raw files
|
@@ -245,7 +211,7 @@ module Phonelib
|
|
245
211
|
end
|
246
212
|
|
247
213
|
# method finds country by country prefix
|
248
|
-
def
|
214
|
+
def country_by_code(country_code)
|
249
215
|
match = @data.select { |_k, v| v[:country_code] == country_code }
|
250
216
|
if match.size > 1
|
251
217
|
match = match.select { |_k, v| v[:main_country_for_code] == 'true' }
|
@@ -6,12 +6,38 @@ module Phonelib
|
|
6
6
|
# xml format attributes names
|
7
7
|
XML_FORMAT_NAMES = %w(intlFormat format)
|
8
8
|
|
9
|
+
def file_path(file)
|
10
|
+
"#{File.dirname(__FILE__)}/../../#{file}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# method saves parsed data to data files
|
14
|
+
def save_data_file
|
15
|
+
File.open(file_path(Phonelib::Core::FILE_MAIN_DATA), 'wb+') do |f|
|
16
|
+
Marshal.dump(@data, f)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# method saves extended data file
|
21
|
+
def save_extended_data_file
|
22
|
+
extended = {
|
23
|
+
Phonelib::Core::EXT_PREFIXES => @prefixes,
|
24
|
+
Phonelib::Core::EXT_GEO_NAMES => @geo_names,
|
25
|
+
Phonelib::Core::EXT_TIMEZONES => @timezones,
|
26
|
+
Phonelib::Core::EXT_CARRIERS => @carriers
|
27
|
+
}
|
28
|
+
File.open(file_path(Phonelib::Core::FILE_EXT_DATA), 'wb+') do |f|
|
29
|
+
Marshal.dump(extended, f)
|
30
|
+
end
|
31
|
+
puts 'DATA SAVED'
|
32
|
+
end
|
33
|
+
|
9
34
|
# method updates prefixes hash recursively
|
10
35
|
def fill_prefixes(key, value, prefix, prefixes)
|
11
36
|
prefixes = {} if prefixes.nil?
|
12
37
|
if prefix.size == 1
|
13
|
-
|
14
|
-
prefixes[
|
38
|
+
pr = prefix.to_i
|
39
|
+
prefixes[pr] ||= {}
|
40
|
+
prefixes[pr][key] = value
|
15
41
|
else
|
16
42
|
pr = prefix[0].to_i
|
17
43
|
prefixes[pr] = fill_prefixes(key, value, prefix[1..-1], prefixes[pr])
|
@@ -32,7 +58,7 @@ module Phonelib
|
|
32
58
|
end
|
33
59
|
|
34
60
|
# method creates hash from xml elements/element attributes
|
35
|
-
def
|
61
|
+
def hash_from_xml(data, type)
|
36
62
|
hash = {}
|
37
63
|
case type
|
38
64
|
when :attributes
|
data/lib/phonelib/phone.rb
CHANGED
@@ -42,20 +42,20 @@ module Phonelib
|
|
42
42
|
def sanitized
|
43
43
|
@sanitized ||=
|
44
44
|
vanity_converted(@original).gsub(
|
45
|
-
Phonelib.strict_check ?
|
45
|
+
Phonelib.strict_check ? cr('^\+') : cr('[^0-9]+'),
|
46
46
|
'')
|
47
47
|
end
|
48
48
|
|
49
49
|
# Returns all phone types that matched valid patterns
|
50
50
|
# @return [Array] all valid phone types
|
51
51
|
def types
|
52
|
-
@data.flat_map { |_iso2, data| data[:valid] }.uniq
|
52
|
+
@types ||= @data.flat_map { |_iso2, data| data[:valid] }.uniq
|
53
53
|
end
|
54
54
|
|
55
55
|
# Returns all possible types that matched possible patterns
|
56
56
|
# @return [Array] all possible phone types
|
57
57
|
def possible_types
|
58
|
-
@data.flat_map { |_iso2, data| data[:possible] }.uniq
|
58
|
+
@possible_types ||= @data.flat_map { |_iso2, data| data[:possible] }.uniq
|
59
59
|
end
|
60
60
|
|
61
61
|
# Returns first phone type that matched
|
@@ -105,7 +105,7 @@ module Phonelib
|
|
105
105
|
# Returns whether a current parsed phone number is valid
|
106
106
|
# @return [Boolean] parsed phone is valid
|
107
107
|
def valid?
|
108
|
-
@data.select { |_iso2, data| data[:valid].any? }.any?
|
108
|
+
@valid ||= @data.select { |_iso2, data| data[:valid].any? }.any?
|
109
109
|
end
|
110
110
|
|
111
111
|
# Returns whether a current parsed phone number is invalid
|
@@ -117,7 +117,7 @@ module Phonelib
|
|
117
117
|
# Returns whether a current parsed phone number is possible
|
118
118
|
# @return [Boolean] parsed phone is possible
|
119
119
|
def possible?
|
120
|
-
@data.select { |_iso2, data| data[:possible].any? }.any?
|
120
|
+
@possible ||= @data.select { |_iso2, data| data[:possible].any? }.any?
|
121
121
|
end
|
122
122
|
|
123
123
|
# Returns whether a current parsed phone number is impossible
|
@@ -18,16 +18,16 @@ module Phonelib
|
|
18
18
|
def analyze(phone, passed_country)
|
19
19
|
country = country_or_default_country passed_country
|
20
20
|
|
21
|
-
result =
|
21
|
+
result = parse_country(phone, country)
|
22
22
|
d_result = case
|
23
|
-
when result && result.values.find {|e| e[:valid].any? }
|
23
|
+
when result && result.values.find { |e| e[:valid].any? }
|
24
24
|
# all is good, return result
|
25
25
|
when passed_country.nil?
|
26
26
|
# trying for all countries if no country was passed
|
27
27
|
detect_and_parse phone
|
28
|
-
when
|
28
|
+
when country_can_dp?(country)
|
29
29
|
# if country allows double prefix trying modified phone
|
30
|
-
|
30
|
+
parse_country(changed_dp_phone(country, phone), country)
|
31
31
|
end
|
32
32
|
better_result(result, d_result)
|
33
33
|
end
|
@@ -36,19 +36,14 @@ module Phonelib
|
|
36
36
|
|
37
37
|
# method checks which result is better to return
|
38
38
|
def better_result(base_result, result = nil)
|
39
|
-
|
40
|
-
|
41
|
-
end
|
39
|
+
base_result ||= {}
|
40
|
+
return base_result unless result
|
42
41
|
|
43
|
-
|
44
|
-
return result
|
45
|
-
end
|
42
|
+
return result unless base_result.values.find { |e| e[:possible].any? }
|
46
43
|
|
47
|
-
|
48
|
-
return result
|
49
|
-
end
|
44
|
+
return result if result.values.find { |e| e[:valid].any? }
|
50
45
|
|
51
|
-
base_result
|
46
|
+
base_result
|
52
47
|
end
|
53
48
|
|
54
49
|
# trying to parse phone for single country including international prefix
|
@@ -58,9 +53,9 @@ module Phonelib
|
|
58
53
|
#
|
59
54
|
# * +phone+ - phone for parsing
|
60
55
|
# * +country+ - country to parse phone with
|
61
|
-
def
|
56
|
+
def parse_country(phone, country)
|
62
57
|
data = Phonelib.phone_data[country]
|
63
|
-
return nil unless
|
58
|
+
return nil unless data
|
64
59
|
|
65
60
|
# if country was provided and it's a valid country, trying to
|
66
61
|
# create e164 representation of phone number,
|
@@ -69,7 +64,7 @@ module Phonelib
|
|
69
64
|
# if phone starts with international prefix of provided
|
70
65
|
# country try to reanalyze without international prefix for
|
71
66
|
# all countries
|
72
|
-
return analyze(e164
|
67
|
+
return analyze(e164[1..-1], nil) if Core::PLUS_SIGN == e164[0]
|
73
68
|
# trying to parse number for provided country
|
74
69
|
parse_single_country e164, data
|
75
70
|
end
|
@@ -83,10 +78,10 @@ module Phonelib
|
|
83
78
|
def parse_single_country(e164, data)
|
84
79
|
valid_match = phone_match_data?(e164, data)
|
85
80
|
if valid_match
|
86
|
-
national_and_data(
|
81
|
+
national_and_data(data, valid_match)
|
87
82
|
else
|
88
83
|
possible_match = phone_match_data?(e164, data, true)
|
89
|
-
possible_match && national_and_data(
|
84
|
+
possible_match && national_and_data(data, possible_match, true)
|
90
85
|
end
|
91
86
|
end
|
92
87
|
|
@@ -100,7 +95,7 @@ module Phonelib
|
|
100
95
|
Phonelib.phone_data.each do |key, data|
|
101
96
|
parsed = parse_single_country(phone, data)
|
102
97
|
if double_prefix_allowed?(data, phone, parsed && parsed[key])
|
103
|
-
parsed = parse_single_country(
|
98
|
+
parsed = parse_single_country(changed_dp_phone(key, phone), data)
|
104
99
|
end
|
105
100
|
result.merge!(parsed) unless parsed.nil?
|
106
101
|
end
|
@@ -114,13 +109,14 @@ module Phonelib
|
|
114
109
|
# * +phone+ - phone number for parsing
|
115
110
|
# * +data+ - country data to be based on for creating e164 representation
|
116
111
|
def convert_to_e164(phone, data)
|
117
|
-
match = phone.match full_regex_for_data(data, Core::VALID_PATTERN)
|
112
|
+
match = phone.match full_regex_for_data(data, Core::VALID_PATTERN, !original_starts_with_plus?)
|
118
113
|
case
|
119
114
|
when match
|
120
|
-
|
121
|
-
"#{data[Core::COUNTRY_CODE]}#{phone[national_start..-1]}"
|
115
|
+
"#{data[Core::COUNTRY_CODE]}#{match.to_a.last}"
|
122
116
|
when phone.match(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"))
|
123
|
-
phone.sub(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"),
|
117
|
+
phone.sub(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"), Core::PLUS_SIGN)
|
118
|
+
when original_starts_with_plus? && phone.start_with?(data[Core::COUNTRY_CODE])
|
119
|
+
phone
|
124
120
|
else
|
125
121
|
"#{data[Core::COUNTRY_CODE]}#{phone}"
|
126
122
|
end
|
@@ -130,18 +126,15 @@ module Phonelib
|
|
130
126
|
#
|
131
127
|
# ==== Attributes
|
132
128
|
#
|
133
|
-
# * +phone+ - phone number for parsing
|
134
129
|
# * +data+ - country data
|
135
130
|
# * +country_match+ - result of match of phone within full regex
|
136
131
|
# * +not_valid+ - specifies that number is not valid by general desc pattern
|
137
|
-
def national_and_data(
|
138
|
-
prefix_length = data[Core::COUNTRY_CODE].length
|
139
|
-
prefix_length += [1, 2].map { |i| country_match[i].to_s.size }.inject(:+)
|
132
|
+
def national_and_data(data, country_match, not_valid = false)
|
140
133
|
result = data.select { |k, _v| k != :types && k != :formats }
|
141
|
-
|
142
|
-
result[:
|
143
|
-
result
|
144
|
-
|
134
|
+
phone = country_match.to_a.last
|
135
|
+
result[:national] = phone
|
136
|
+
result[:format] = number_format(phone, data[Core::FORMATS])
|
137
|
+
result.merge! all_number_types(phone, data[Core::TYPES], not_valid)
|
145
138
|
result[:valid] = [] if not_valid
|
146
139
|
|
147
140
|
{ result[:id] => result }
|
@@ -178,9 +171,9 @@ module Phonelib
|
|
178
171
|
# * +format_data+ - formatting data from country data
|
179
172
|
def number_format(national, format_data)
|
180
173
|
format_data && format_data.find do |format|
|
181
|
-
(format[Core::LEADING_DIGITS].nil? \
|
182
|
-
|
183
|
-
|
174
|
+
(format[Core::LEADING_DIGITS].nil? || \
|
175
|
+
national.match(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
|
176
|
+
national.match(cr("^(#{format[Core::PATTERN]})$"))
|
184
177
|
end || Core::DEFAULT_NUMBER_FORMAT
|
185
178
|
end
|
186
179
|
|
@@ -3,24 +3,28 @@ module Phonelib
|
|
3
3
|
module PhoneAnalyzerHelper
|
4
4
|
private
|
5
5
|
|
6
|
+
def original_starts_with_plus?
|
7
|
+
original_s[0] == Core::PLUS_SIGN
|
8
|
+
end
|
9
|
+
|
10
|
+
# converts symbols in phone to numbers
|
6
11
|
def vanity_converted(phone)
|
7
12
|
return phone unless Phonelib.vanity_conversion
|
8
13
|
|
9
|
-
(phone || '').gsub(
|
14
|
+
(phone || '').gsub(cr('[a-zA-Z]')) do |c|
|
10
15
|
c.upcase!
|
11
16
|
# subtract "A"
|
12
17
|
n = (c.ord - 65) / 3
|
13
18
|
# account for #7 & #9 which have 4 chars
|
14
|
-
n -= 1 if c
|
19
|
+
n -= 1 if c.match(Core::VANITY_4_LETTERS_KEYS_REGEX)
|
15
20
|
(n + 2).to_s
|
16
21
|
end
|
17
22
|
end
|
18
23
|
|
19
|
-
|
20
24
|
# defines if to validate against single country or not
|
21
25
|
def passed_country(country)
|
22
26
|
code = country_prefix(country)
|
23
|
-
if @original
|
27
|
+
if Core::PLUS_SIGN == @original[0] && code && !sanitized.start_with?(code)
|
24
28
|
# in case number passed with + but it doesn't start with passed
|
25
29
|
# country prefix
|
26
30
|
country = nil
|
@@ -37,17 +41,18 @@ module Phonelib
|
|
37
41
|
|
38
42
|
# caches regular expression, reusing it for later lookups
|
39
43
|
def cr(regexp)
|
40
|
-
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(regexp)
|
44
|
+
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(regexp).freeze
|
41
45
|
end
|
42
46
|
|
43
47
|
# defines whether country can have double country prefix in number
|
44
|
-
def
|
48
|
+
def country_can_dp?(country)
|
45
49
|
Phonelib.phone_data[country] &&
|
46
|
-
Phonelib.phone_data[country][Core::DOUBLE_COUNTRY_PREFIX_FLAG]
|
50
|
+
Phonelib.phone_data[country][Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
51
|
+
!original_starts_with_plus?
|
47
52
|
end
|
48
53
|
|
49
54
|
# changes phone to with/without double country prefix
|
50
|
-
def
|
55
|
+
def changed_dp_phone(country, phone)
|
51
56
|
data = Phonelib.phone_data[country]
|
52
57
|
return if data.nil? || data[Core::DOUBLE_COUNTRY_PREFIX_FLAG].nil?
|
53
58
|
|
@@ -67,14 +72,15 @@ module Phonelib
|
|
67
72
|
# * +phone+ - phone number being parsed
|
68
73
|
# * +parsed+ - parsed regex match for phone
|
69
74
|
def double_prefix_allowed?(data, phone, parsed = {})
|
70
|
-
data
|
75
|
+
data[Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
|
71
76
|
phone =~ cr("^#{data[Core::COUNTRY_CODE]}") &&
|
72
|
-
parsed && (parsed[:valid].nil? || parsed[:valid].empty?)
|
77
|
+
parsed && (parsed[:valid].nil? || parsed[:valid].empty?) &&
|
78
|
+
!original_starts_with_plus?
|
73
79
|
end
|
74
80
|
|
75
81
|
# Returns original number passed if it's a string or empty string otherwise
|
76
|
-
def
|
77
|
-
@original.is_a?(String) ? @original : ''
|
82
|
+
def original_s
|
83
|
+
@original_s ||= @original.is_a?(String) ? @original : ''
|
78
84
|
end
|
79
85
|
|
80
86
|
# Get country that was provided or default country in needable format
|
@@ -83,7 +89,7 @@ module Phonelib
|
|
83
89
|
#
|
84
90
|
# * +country+ - country passed for parsing
|
85
91
|
def country_or_default_country(country)
|
86
|
-
country ||= (
|
92
|
+
country ||= (original_starts_with_plus? ? nil : Phonelib.default_country)
|
87
93
|
country && country.to_s.upcase
|
88
94
|
end
|
89
95
|
|
@@ -97,11 +103,7 @@ module Phonelib
|
|
97
103
|
def full_regex_for_data(data, type, country_optional = true)
|
98
104
|
regex = []
|
99
105
|
regex << "(#{data[Core::INTERNATIONAL_PREFIX]})?"
|
100
|
-
regex <<
|
101
|
-
"(#{data[Core::COUNTRY_CODE]})?"
|
102
|
-
else
|
103
|
-
data[Core::COUNTRY_CODE]
|
104
|
-
end
|
106
|
+
regex << "(#{data[Core::COUNTRY_CODE]})#{country_optional ? '?' : ''}"
|
105
107
|
regex << "(#{data[Core::NATIONAL_PREFIX_FOR_PARSING] || data[Core::NATIONAL_PREFIX]})?"
|
106
108
|
regex << "(#{data[Core::TYPES][Core::GENERAL][type]})"
|
107
109
|
|
@@ -165,22 +167,27 @@ module Phonelib
|
|
165
167
|
# ==== Attributes
|
166
168
|
#
|
167
169
|
# * +number+ - phone number for validation
|
168
|
-
# * +
|
169
|
-
# * +
|
170
|
+
# * +p_regex+ - possible regex pattern for validation
|
171
|
+
# * +v_regex+ - valid regex pattern for validation
|
170
172
|
# * +not_valid+ - specifies that number is not valid by general desc pattern
|
171
|
-
def number_valid_and_possible?(number,
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
valid = false
|
177
|
-
if !not_valid && possible
|
178
|
-
# doing national pattern match only in case possible matches
|
179
|
-
national_match = number.match(cr("^(?:#{national_pattern})$"))
|
180
|
-
valid = national_match && national_match.to_s.length == number.length
|
181
|
-
end
|
173
|
+
def number_valid_and_possible?(number, p_regex, v_regex, not_valid = false)
|
174
|
+
possible = number_match?(number, p_regex)
|
175
|
+
|
176
|
+
return [!not_valid && possible, possible] if p_regex == v_regex
|
177
|
+
valid = !not_valid && possible && number_match?(number, v_regex)
|
182
178
|
|
183
179
|
[valid && possible, possible]
|
184
180
|
end
|
181
|
+
|
182
|
+
# Checks number against regex and compares match length
|
183
|
+
#
|
184
|
+
# ==== Attributes
|
185
|
+
#
|
186
|
+
# * +number+ - phone number for validation
|
187
|
+
# * +regex+ - regex for perfoming a validation
|
188
|
+
def number_match?(number, regex)
|
189
|
+
match = number.match(cr("^(?:#{regex})$"))
|
190
|
+
match && match.to_s.length == number.length
|
191
|
+
end
|
185
192
|
end
|
186
193
|
end
|
@@ -32,24 +32,23 @@ module Phonelib
|
|
32
32
|
# Returns the country code from the original phone number.
|
33
33
|
# @return [String] matched country phone code
|
34
34
|
def country_code
|
35
|
-
Phonelib.phone_data[country] && \
|
36
|
-
|
35
|
+
@country_code ||= Phonelib.phone_data[country] && \
|
36
|
+
Phonelib.phone_data[country][Core::COUNTRY_CODE]
|
37
37
|
end
|
38
38
|
|
39
39
|
# Returns e164 formatted phone number
|
40
40
|
# @param formatted [Boolean] whether to return numbers only or formatted
|
41
41
|
# @return [String] formatted international number
|
42
42
|
def international(formatted = true)
|
43
|
-
return nil if sanitized.
|
43
|
+
return nil if sanitized.empty?
|
44
44
|
return "+#{country_prefix_or_not}#{sanitized}" unless valid?
|
45
|
-
return
|
45
|
+
return country_code + @national_number unless formatted
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
fmt = @data[country][:format]
|
48
|
+
national = @national_number
|
49
|
+
if (matches = @national_number.match(cr(fmt[Core::PATTERN])))
|
50
|
+
fmt = fmt[:intl_format] || fmt[:format]
|
50
51
|
national = fmt.gsub(/\$\d/) { |el| matches[el[1].to_i] }
|
51
|
-
else
|
52
|
-
national = @national_number
|
53
52
|
end
|
54
53
|
|
55
54
|
"+#{country_code} #{national}"
|
@@ -88,7 +87,7 @@ module Phonelib
|
|
88
87
|
format_match, _format_string = formatting_data
|
89
88
|
take_group = 1
|
90
89
|
if type == Core::MOBILE && Core::AREA_CODE_MOBILE_TOKENS[country] && \
|
91
|
-
|
90
|
+
format_match[1] == Core::AREA_CODE_MOBILE_TOKENS[country]
|
92
91
|
take_group = 2
|
93
92
|
end
|
94
93
|
format_match[take_group]
|
@@ -98,7 +97,7 @@ module Phonelib
|
|
98
97
|
|
99
98
|
# @private defines if phone can have area code
|
100
99
|
def area_code_possible?
|
101
|
-
return false
|
100
|
+
return false if impossible?
|
102
101
|
|
103
102
|
# has national prefix
|
104
103
|
return false unless @data[country][Core::NATIONAL_PREFIX] || country == 'IT'
|
@@ -126,10 +125,11 @@ module Phonelib
|
|
126
125
|
def formatting_data
|
127
126
|
return @formatting_data if defined?(@formatting_data)
|
128
127
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
128
|
+
data = @data[country]
|
129
|
+
format = data[:format]
|
130
|
+
prefix = data[Core::NATIONAL_PREFIX]
|
131
|
+
rule = format[Core::NATIONAL_PREFIX_RULE] ||
|
132
|
+
data[Core::NATIONAL_PREFIX_RULE] || '$1'
|
133
133
|
|
134
134
|
# change rule's constants to values
|
135
135
|
rule.gsub!(/(\$NP|\$FG)/, '$NP' => prefix, '$FG' => '$1')
|
data/lib/phonelib/version.rb
CHANGED
@@ -43,21 +43,32 @@ class PhoneValidator < ActiveModel::EachValidator
|
|
43
43
|
# Validation method
|
44
44
|
def validate_each(record, attribute, value)
|
45
45
|
return if options[:allow_blank] && value.blank?
|
46
|
-
country = options[:country_specifier].call(record) if options[:country_specifier]
|
47
46
|
|
48
|
-
phone = parse(value,
|
47
|
+
phone = parse(value, specified_country(record))
|
49
48
|
valid = if simple_validation?
|
50
|
-
|
51
|
-
phone.send(method)
|
49
|
+
phone.send(validate_method)
|
52
50
|
else
|
53
51
|
(phone_types(phone) & types).size > 0
|
54
52
|
end
|
55
53
|
|
56
|
-
record.errors.add(attribute,
|
54
|
+
record.errors.add(attribute, message, options) unless valid
|
57
55
|
end
|
58
56
|
|
59
57
|
private
|
60
58
|
|
59
|
+
def message
|
60
|
+
options[:message] || :invalid
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_method
|
64
|
+
options[:possible] ? :possible? : :valid?
|
65
|
+
end
|
66
|
+
|
67
|
+
def specified_country(record)
|
68
|
+
return unless options[:country_specifier]
|
69
|
+
options[:country_specifier].call(record)
|
70
|
+
end
|
71
|
+
|
61
72
|
# @private
|
62
73
|
def simple_validation?
|
63
74
|
options[:types].nil?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phonelib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.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: 2017-
|
11
|
+
date: 2017-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|