phonelib 0.10.1 → 0.10.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f74da621590af3b6ba09374d12dcad33370f71e38d8ed5122baafd63ad12e32
4
- data.tar.gz: bc722c0d765c923f5f815e77a9803635f80ca58d8dd0792190484d00a4340933
3
+ metadata.gz: 2178528a04a3e53cee77692cdce4a75faec804fcdeb34a3659e0cfa529c91ee6
4
+ data.tar.gz: c4831b3a37d873162fd2bca9fbc6307b1337b5f63a9409b353acfbe42cdfd28f
5
5
  SHA512:
6
- metadata.gz: 1c17592015bf5060d0cafa50227a6c8f3f6a97bef71d8993e1915e1a2a6b0e118bb54f26f5ef0f509d631ca8938fccfb3465fcc3561829dd8e30db6652989f5f
7
- data.tar.gz: 11fe31137e6468adafe4daecfbefd91c9a246489bcf02f483c1066e41f9a6a343849cd43e0f080187cb6d4eb5fdf924bad0c7d03e7e653c25de7928645856c72
6
+ metadata.gz: 93d78757e419afdbc970963aac89fbd7a5c418819f23921dd745a39e13f3f34146b02d0e8c371c0d07ff4a49f9494b2accbf27e9c96fe6655504c2436fb29ac8
7
+ data.tar.gz: 0bfadc4703286355c340f917ca9832598ca661a49436eae8fca5c31cf10d4c2738b64ad075adf66b8be46fc233ca21542b0f424035eaad38fea4f1a2c0201335
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://rawgit.com/googlei18n/libphonenumber/master/javascript/i18n/phonenumbers/demo-compiled.html) and in case of same parse result [open an issue for them](https://issuetracker.google.com/issues/new?component=192347&template=829703). This gem's data is based on it.
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
Binary file
data/data/phone_data.dat CHANGED
Binary file
data/lib/phonelib/core.rb CHANGED
@@ -202,7 +202,7 @@ module Phonelib
202
202
  end
203
203
 
204
204
  def add_additional_regex(country, type, national_regex)
205
- return unless Phonelib::Core::TYPES_DESC.keys.include?(type.to_sym)
205
+ return unless Phonelib::Core::TYPES_DESC.key?(type.to_sym)
206
206
  return unless national_regex.is_a?(String)
207
207
  @@phone_data = @@data_by_country_codes = nil
208
208
  @@additional_regexes[country.to_s.upcase] ||= {}
@@ -284,7 +284,7 @@ module Phonelib
284
284
  CARRIER_SPECIFIC = :carrier_specific
285
285
  # @private SMS Services only type
286
286
  SMS_SERVICES = :sms_services
287
- # @private expendad emergency type
287
+ # @private expanded emergency type
288
288
  EXPANDED_EMERGENCY = :expanded_emergency
289
289
  # @private no international dialling type
290
290
  NO_INTERNATIONAL_DIALING = :no_international_dialling
@@ -381,6 +381,7 @@ module Phonelib
381
381
  carrier_selection_codes: 'Carrier Selection codes',
382
382
  area_code_optional: 'Are code optional'
383
383
  }.freeze
384
+ TYPES_DESC_KEYS = TYPES_DESC.keys.freeze
384
385
 
385
386
  # @private short codes types keys
386
387
  SHORT_CODES = [
@@ -407,6 +408,13 @@ module Phonelib
407
408
  # @private Extended data key for carrier in prefixes hash
408
409
  EXT_CARRIER_KEY = :c
409
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
+
410
418
  # method for parsing phone number.
411
419
  # On first run fills @@phone_data with data present in yaml file
412
420
  # @param phone [String] the phone number to be parsed
@@ -8,7 +8,7 @@ module Phonelib
8
8
  require 'nokogiri'
9
9
 
10
10
  # official libphonenumber repo for cloning
11
- REPO = 'https://github.com/googlei18n/libphonenumber.git'
11
+ REPO = 'https://github.com/google/libphonenumber.git'
12
12
 
13
13
  # importing function
14
14
  def self.import
@@ -203,7 +203,7 @@ module Phonelib
203
203
  require 'open-uri'
204
204
  require 'csv'
205
205
  io = URI.open('http://download.geonames.org/export/dump/countryInfo.txt')
206
- csv = CSV.new(io, {col_sep: "\t"})
206
+ csv = CSV.new(io, **{col_sep: "\t"})
207
207
  csv.each do |row|
208
208
  next if row[0].nil? || row[0].start_with?('#') || row[0].empty? || row[0].size != 2
209
209
 
@@ -103,7 +103,13 @@ module Phonelib
103
103
  line = str_clean line, false
104
104
  next if line.empty? || line[0] == '#'
105
105
  prefix, line_data = line.split('|')
106
- data[prefix] = line_data && line_data.strip.split('&')
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
107
113
  end
108
114
  data
109
115
  end
@@ -19,7 +19,6 @@ module Phonelib
19
19
  # country (2 letters) like 'US', 'us' or :us for United States
20
20
  def analyze(phone, passed_country)
21
21
  countries = country_or_default_country passed_country
22
-
23
22
  return analyze_single_country(phone, countries.first, passed_country) if countries.size == 1
24
23
 
25
24
  results = {}
@@ -34,7 +33,7 @@ module Phonelib
34
33
 
35
34
  # pick best result when several countries specified
36
35
  def pick_results(results)
37
- [:valid, :possible].each do |key|
36
+ Core::VALID_POSSIBLE_ARRAY.each do |key|
38
37
  final = results.select { |_k, v| v[key].any? }
39
38
  return decorate_analyze_result(final) if final.size > 0
40
39
  end
@@ -70,9 +69,15 @@ module Phonelib
70
69
  end
71
70
 
72
71
  # replacing national prefix to simplified format
73
- def with_replaced_national_prefix(phone, data)
74
- return phone unless data[Core::NATIONAL_PREFIX_TRANSFORM_RULE]
75
- phone = phone.gsub(/^#{data[Core::COUNTRY_CODE]}/, '') if phone.start_with?(data[Core::COUNTRY_CODE]) && !data[Core::DOUBLE_COUNTRY_PREFIX_FLAG]
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
+
76
81
  pattern = cr("^(?:#{data[Core::NATIONAL_PREFIX_FOR_PARSING]})")
77
82
  match = phone.match pattern
78
83
  if match && match.captures.compact.size > 0
@@ -133,8 +138,13 @@ module Phonelib
133
138
  countries_data.each_with_object({}) do |data, result|
134
139
  key = data[:id]
135
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
136
145
  if (!Phonelib.strict_double_prefix_check || key == country) && double_prefix_allowed?(data, phone, parsed && parsed[key])
137
- parsed = parse_single_country(changed_dp_phone(key, phone), data)
146
+ parsed2 = parse_single_country(changed_dp_phone(key, phone), data)
147
+ parsed = parsed2 if parsed2 && parsed2[key] && parsed2[key][:valid].size > 0
138
148
  end
139
149
  result.merge!(parsed) unless parsed.nil?
140
150
  end.compact
@@ -152,13 +162,13 @@ module Phonelib
152
162
  # * +phone+ - phone number for parsing
153
163
  # * +data+ - country data to be based on for creating e164 representation
154
164
  def convert_to_e164(phone, data)
155
- match = phone.match full_regex_for_data(data, Core::VALID_PATTERN, !original_starts_with_plus?)
165
+ match = phone.match full_regex_for_data(data, Core::VALID_PATTERN, !original_starts_with_plus_or_double_zero?)
156
166
  case
157
167
  when match
158
168
  "#{data[Core::COUNTRY_CODE]}#{match.to_a.last}"
159
169
  when phone.match(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"))
160
170
  phone.sub(cr("^#{data[Core::INTERNATIONAL_PREFIX]}"), Core::PLUS_SIGN)
161
- when original_starts_with_plus? && phone.start_with?(data[Core::COUNTRY_CODE])
171
+ when original_starts_with_plus_or_double_zero? && phone.start_with?(data[Core::COUNTRY_CODE])
162
172
  phone
163
173
  else
164
174
  "#{data[Core::COUNTRY_CODE]}#{phone}"
@@ -174,7 +184,11 @@ module Phonelib
174
184
  # * +not_valid+ - specifies that number is not valid by general desc pattern
175
185
  def national_and_data(data, country_match, not_valid = false)
176
186
  result = data.select { |k, _v| k != :types && k != :formats }
177
- phone = country_match.to_a.last
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]
178
192
  result[:national] = phone
179
193
  result[:format] = number_format(phone, data[Core::FORMATS])
180
194
  result.merge! all_number_types(phone, data[Core::TYPES], not_valid)
@@ -215,7 +229,7 @@ module Phonelib
215
229
  def number_format(national, format_data)
216
230
  format_data && format_data.find do |format|
217
231
  (format[Core::LEADING_DIGITS].nil? || \
218
- national.match(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
232
+ national.match?(cr("^(#{format[Core::LEADING_DIGITS]})"))) && \
219
233
  national.match(cr("^(#{format[Core::PATTERN]})$"))
220
234
  end || Core::DEFAULT_NUMBER_FORMAT
221
235
  end
@@ -236,7 +250,7 @@ module Phonelib
236
250
  type_regex(patterns, Core::VALID_PATTERN)
237
251
  ]
238
252
  else
239
- [nil, nil]
253
+ Core::NIL_RESULT_ARRAY
240
254
  end
241
255
  end
242
256
  end
@@ -16,8 +16,8 @@ module Phonelib
16
16
  end
17
17
  end
18
18
 
19
- def original_starts_with_plus?
20
- 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'
21
21
  end
22
22
 
23
23
  # converts symbols in phone to numbers
@@ -61,7 +61,8 @@ module Phonelib
61
61
  def country_can_dp?(country)
62
62
  Phonelib.phone_data[country] &&
63
63
  Phonelib.phone_data[country][Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
64
- !original_starts_with_plus? && original_s.start_with?(Phonelib.phone_data[country][Core::COUNTRY_CODE])
64
+ !original_starts_with_plus_or_double_zero? &&
65
+ original_s.start_with?(Phonelib.phone_data[country][Core::COUNTRY_CODE])
65
66
  end
66
67
 
67
68
  # changes phone to with/without double country prefix
@@ -89,7 +90,7 @@ module Phonelib
89
90
  data[Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
90
91
  phone =~ cr("^#{data[Core::COUNTRY_CODE]}") &&
91
92
  parsed && (parsed[:valid].nil? || parsed[:valid].empty?) &&
92
- !original_starts_with_plus?
93
+ !original_starts_with_plus_or_double_zero?
93
94
  end
94
95
 
95
96
  # Returns original number passed if it's a string or empty string otherwise
@@ -103,7 +104,8 @@ module Phonelib
103
104
  #
104
105
  # * +country+ - country passed for parsing
105
106
  def country_or_default_country(country)
106
- country ||= (original_starts_with_plus? ? nil : Phonelib.default_country)
107
+ country ||= (original_starts_with_plus_or_double_zero? ? nil : Phonelib.default_country)
108
+
107
109
  if country.is_a?(Array)
108
110
  country.compact.map { |e| e.to_s.upcase }
109
111
  else
@@ -119,14 +121,15 @@ module Phonelib
119
121
  # * +data+ - country data hash
120
122
  # * +country_optional+ - whether to put country code as optional group
121
123
  def full_regex_for_data(data, type, country_optional = true)
122
- regex = []
123
- regex << '0{2}?'
124
- regex << "(#{data[Core::INTERNATIONAL_PREFIX]})?"
125
- regex << "(#{data[Core::COUNTRY_CODE]})#{country_optional ? '?' : ''}"
126
- regex << "(#{data[Core::NATIONAL_PREFIX_FOR_PARSING] || data[Core::NATIONAL_PREFIX]})?"
127
- regex << "(#{type_regex(data[Core::TYPES][Core::GENERAL], type)})" if data[Core::TYPES]
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 << "$"
128
131
 
129
- cr("^#{regex.join}$")
132
+ cr(regex)
130
133
  end
131
134
 
132
135
  # Returns regex for type with special types if needed
@@ -136,11 +139,11 @@ module Phonelib
136
139
  # * +data+ - country types data for single type
137
140
  # * +type+ - possible or valid regex type needed
138
141
  def type_regex(data, type)
139
- regex = [data[type]]
140
142
  if Phonelib.parse_special && data[Core::SHORT] && data[Core::SHORT][type]
141
- regex << data[Core::SHORT][type]
143
+ "#{data[type]}|#{data[Core::SHORT][type]}"
144
+ else
145
+ data[type]
142
146
  end
143
- regex.join('|')
144
147
  end
145
148
 
146
149
  # Check if phone match country data
@@ -150,9 +153,7 @@ module Phonelib
150
153
  # * +phone+ - phone number for parsing
151
154
  # * +data+ - country data
152
155
  def phone_match_data?(phone, data, possible = false)
153
- country_code = "#{data[Core::COUNTRY_CODE]}"
154
- inter_prefix = "(#{data[Core::INTERNATIONAL_PREFIX]})?"
155
- 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]}"))
156
157
 
157
158
  type = possible ? Core::POSSIBLE_PATTERN : Core::VALID_PATTERN
158
159
  phone.match full_regex_for_data(data, type, false)
@@ -161,10 +162,9 @@ module Phonelib
161
162
  # checks if types has both :mobile and :fixed_line and replaces it with
162
163
  # :fixed_or_mobile in case both present
163
164
  def sanitize_fixed_mobile(types)
164
- fixed_mobile = [Core::FIXED_LINE, Core::MOBILE]
165
- [:possible, :valid].each do |key|
166
- if (fixed_mobile - types[key]).empty?
167
- 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
168
168
  end
169
169
  end
170
170
  types
@@ -178,7 +178,7 @@ module Phonelib
178
178
  def types_for_check(data)
179
179
  exclude_list = PhoneAnalyzer::NOT_FOR_CHECK
180
180
  exclude_list += Phonelib::Core::SHORT_CODES unless Phonelib.parse_special
181
- Core::TYPES_DESC.keys - exclude_list + fixed_and_mobile_keys(data)
181
+ Core::TYPES_DESC_KEYS - exclude_list + fixed_and_mobile_keys(data)
182
182
  end
183
183
 
184
184
  # Checks if fixed line pattern and mobile pattern are the same and returns
@@ -189,9 +189,9 @@ module Phonelib
189
189
  # * +data+ - country data
190
190
  def fixed_and_mobile_keys(data)
191
191
  if data[Core::FIXED_LINE] == data[Core::MOBILE]
192
- [Core::FIXED_OR_MOBILE]
192
+ Core::FIXED_OR_MOBILE_ARRAY
193
193
  else
194
- [Core::FIXED_LINE, Core::MOBILE]
194
+ Core::FIXED_LINE_OR_MOBILE_ARRAY
195
195
  end
196
196
  end
197
197
 
@@ -18,12 +18,19 @@ module Phonelib
18
18
  Phonelib::Core::EXT_GEO_NAME_KEY
19
19
  end
20
20
 
21
- # Returns timezone of parsed phone number or nil if number is invalid or
22
- # 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
23
23
  # @return [String|nil] timezone for parsed phone
24
24
  def timezone
25
- get_ext_name Phonelib::Core::EXT_TIMEZONES,
26
- Phonelib::Core::EXT_TIMEZONE_KEY
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]
27
34
  end
28
35
 
29
36
  # Returns carrier of parsed phone number or nil if number is invalid or
@@ -53,9 +60,7 @@ module Phonelib
53
60
  def get_ext_name(names_key, id_key)
54
61
  return nil unless ext_data[id_key]
55
62
 
56
- res = Phonelib.phone_ext_data[names_key][ext_data[id_key]]
57
- return nil unless res
58
- res.size == 1 ? res.first : res
63
+ Phonelib.phone_ext_data[names_key][ext_data[id_key]]
59
64
  end
60
65
 
61
66
  # @private returns extended data ids for current number
@@ -37,7 +37,7 @@ module Phonelib
37
37
  return @country_code if @country_code
38
38
 
39
39
  code = Phonelib.phone_data[country] && Phonelib.phone_data[country][Core::COUNTRY_CODE]
40
- 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] =~ /[\[\|]/
41
41
 
42
42
  match = e164.match(/\A\+(1(#{Phonelib.phone_data[country][Core::LEADING_DIGITS]}))/)
43
43
  if match
@@ -54,7 +54,7 @@ module Phonelib
54
54
  def international(formatted = true, prefix = '+')
55
55
  prefix = formatted if formatted.is_a?(String)
56
56
  return nil if sanitized.empty?
57
- return "#{prefix}#{country_prefix_or_not}#{sanitized}" unless possible?
57
+ return "#{prefix}#{sanitized}" unless possible?
58
58
  return "#{prefix}#{data_country_code}#{@national_number}" unless formatted
59
59
 
60
60
  fmt = @data[country][:format]
@@ -137,12 +137,6 @@ module Phonelib
137
137
  true
138
138
  end
139
139
 
140
- # @private defines whether to put country prefix or not
141
- def country_prefix_or_not
142
- return '' unless data_country_code
143
- sanitized.start_with?(data_country_code) ? '' : data_country_code
144
- end
145
-
146
140
  # @private returns extension with separator defined
147
141
  def formatted_extension
148
142
  return '' if @extension.nil? || @extension.empty?
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Phonelib
4
4
  # @private
5
- VERSION = '0.10.1'
5
+ VERSION = '0.10.16'
6
6
  end
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.10.1
4
+ version: 0.10.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vadim Senderovich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-20 00:00:00.000000000 Z
11
+ date: 2026-02-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Google libphonenumber library was taken as a basis for
@@ -40,7 +40,7 @@ homepage: https://github.com/daddyz/phonelib
40
40
  licenses:
41
41
  - MIT
42
42
  metadata:
43
- changelog_uri: https://github.com/daddyz/phonelib/releases/tag/v0.10.1
43
+ changelog_uri: https://github.com/daddyz/phonelib/releases/tag/v0.10.16
44
44
  post_install_message:
45
45
  rdoc_options:
46
46
  - " --no-private - CHANGELOG.md --readme README.md"