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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc68ad6683a9dbc13f88b825e589c1836a31e83b8f67c6e71d54afac44d0fceb
4
- data.tar.gz: 39ed9dfdc4bf02a8850c18068be4e24407141734718a71e8fa4779f0f1c4ff17
3
+ metadata.gz: 700f7d81fef64c646c34a58849db498373d5dbbd02c1325fc34b57129617adab
4
+ data.tar.gz: 4d56e6bc50304ffea7aa9a2910b66db0ae154b6846b34186f86b04c24e12cdde
5
5
  SHA512:
6
- metadata.gz: 21e94b4cd849e0116189ef7b232dd2d6949b02b9e6e295dfe463272b035669a062d90902e7f4aa6c98001c05dfa2c803f9e83320fe1b32e16f654d88305c62ca
7
- data.tar.gz: f05c697263e31b17a6946ac9fda9c52780fa2aaef23b4031ba7eced4b34b5d576a581dbeacda334d962cb64cd46edebc1aa1c0e5fbcdf9661ccc7d0f28448dbd
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://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,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.keys.include?(type.to_sym)
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/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
@@ -197,8 +202,8 @@ module Phonelib
197
202
 
198
203
  require 'open-uri'
199
204
  require 'csv'
200
- io = open('http://api.geonames.org/countryInfoCSV?username=demo&style=full')
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[: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,12 +69,19 @@ 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]
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
- 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
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, !original_starts_with_plus?)
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 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])
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
- 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]
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
- [nil, nil]
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 original_starts_with_plus?
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
- !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])
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
- !original_starts_with_plus?
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 ||= (original_starts_with_plus? ? nil : Phonelib.default_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
- regex << '0{2}?'
121
- regex << "(#{data[Core::INTERNATIONAL_PREFIX]})?"
122
- regex << "(#{data[Core::COUNTRY_CODE]})#{country_optional ? '?' : ''}"
123
- regex << "(#{data[Core::NATIONAL_PREFIX_FOR_PARSING] || data[Core::NATIONAL_PREFIX]})?"
124
- 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 << "$"
125
131
 
126
- cr("^#{regex.join}$")
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
- regex << data[Core::SHORT][type]
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
- country_code = "#{data[Core::COUNTRY_CODE]}"
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
- fixed_mobile = [Core::FIXED_LINE, Core::MOBILE]
162
- [:possible, :valid].each do |key|
163
- if (fixed_mobile - types[key]).empty?
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::TYPES_DESC.keys - exclude_list + fixed_and_mobile_keys(data)
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
- [Core::FIXED_OR_MOBILE]
192
+ Core::FIXED_OR_MOBILE_ARRAY
190
193
  else
191
- [Core::FIXED_LINE, Core::MOBILE]
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 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.8.4'
5
+ VERSION = '0.10.11'
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,9 +14,13 @@ 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'
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
@@ -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.8.4
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: 2023-09-26 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.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.0.9
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