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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cc35b2b49e08abb5a2da2075e9ff6a33398b9e07a97fdc1e377ccb273eb3114
4
- data.tar.gz: fee9d2ac1700bbe33c056f0d658e8a260408acf320e76e62b4f993efe31fe940
3
+ metadata.gz: 69fe9f8fd0ceac31311de09cba322da5100f51b00cec1c55ca84cea3f7f94a31
4
+ data.tar.gz: 0053507cf791eb8ad5281d8b28cf7438e526fa240745d109f508a265a2832f64
5
5
  SHA512:
6
- metadata.gz: 0f24a98ff377ab4eda20f1c8c0a3c68a608b103ff90009933ef5094aa68bf019a4fb17b65ea734f17364682666802cddf06906f3850299538b908f9781696f01
7
- data.tar.gz: 7e80f05ab5f94310250700eaea3f8bac83842dba84fca847cb7d6e93cf7da38eb27001af69550417d74f41dc888dbd4781fb5d73ae7ca8bb30912d411f21ee53
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://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
@@ -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.keys.include?(type.to_sym)
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/googlei18n/libphonenumber.git'
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[:double_prefix] = true
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
- 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
105
113
  end
106
114
  data
107
115
  end
@@ -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!(/[^0-9]/, '') if @extension
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
- [:valid, :possible].each do |key|
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(phone, data)
72
- return phone unless data[Core::NATIONAL_PREFIX_TRANSFORM_RULE]
73
- phone = phone.gsub(/^#{data[Core::COUNTRY_CODE]}/, '') if phone.start_with?(data[Core::COUNTRY_CODE])
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
- 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
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, !original_starts_with_plus?)
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 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])
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
- 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]
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
- [nil, nil]
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 original_starts_with_plus?
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
- !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])
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
- !original_starts_with_plus?
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 ||= (original_starts_with_plus? ? nil : Phonelib.default_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
- regex << '0{2}?'
122
- regex << "(#{data[Core::INTERNATIONAL_PREFIX]})?"
123
- regex << "(#{data[Core::COUNTRY_CODE]})#{country_optional ? '?' : ''}"
124
- regex << "(#{data[Core::NATIONAL_PREFIX_FOR_PARSING] || data[Core::NATIONAL_PREFIX]})?"
125
- 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 << "$"
126
131
 
127
- cr("^#{regex.join}$")
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
- regex << data[Core::SHORT][type]
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
- country_code = "#{data[Core::COUNTRY_CODE]}"
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
- fixed_mobile = [Core::FIXED_LINE, Core::MOBILE]
163
- [:possible, :valid].each do |key|
164
- if (fixed_mobile - types[key]).empty?
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::TYPES_DESC.keys - exclude_list + fixed_and_mobile_keys(data)
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
- [Core::FIXED_OR_MOBILE]
192
+ Core::FIXED_OR_MOBILE_ARRAY
191
193
  else
192
- [Core::FIXED_LINE, Core::MOBILE]
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 if number is invalid or
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
- get_ext_name Phonelib::Core::EXT_TIMEZONES,
24
- 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]
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
- res = Phonelib.phone_ext_data[names_key][ext_data[id_key]]
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}#{country_prefix_or_not}#{sanitized}" unless possible?
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!(/(\$NP|\$FG)/, '$NP' => prefix, '$FG' => '$1')
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 $')
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Phonelib
2
4
  # @private
3
- VERSION = '0.9.1'
5
+ VERSION = '0.10.14'
4
6
  end
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
- if RUBY_VERSION >= '3.0.0'
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :phonelib do
2
4
  desc 'Create database for tests in Rails dummy application'
3
5
  task :create_test_db do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Validator class for phone validations
2
4
  #
3
5
  # ==== Examples
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.9.1
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: 2024-07-17 00:00:00.000000000 Z
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