phonelib 0.6.10 → 0.6.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c2862f5e9f97af025f8a5beba5a99c68fa24a85
4
- data.tar.gz: e1d12a6507f085b7c1ee633e8940cf88dbdd1dab
3
+ metadata.gz: fa38e795e6d299330a008f39f359cdd29ba221fc
4
+ data.tar.gz: 9bf8ce164c4838722a06aff72037147852100244
5
5
  SHA512:
6
- metadata.gz: 4e5cb8d9c464042b48411057ff7367bcb5ed5d3893454469dd65ddd96812e5ada4048883c4eaa49dc8a5d0927125e9ee40b7c9ccc484121c67489b0427858b2b
7
- data.tar.gz: e0541b7cf75c20d71a65297263bfa06b6941a6afffd9375aed0968df040e2bb6f2642e650b4535faf15fc54c0c5827ca3957c525c47ee0197eb10877aff6ab03
6
+ metadata.gz: 61367e9387768b79bbb6d43c5b9718098c4a6772281b24111acd9bce2e6dadcfe08b9aa54aa64e4463b4457e593d4fb08e92da6e15d537dcf083df14d3c5e3f1
7
+ data.tar.gz: 58ba6833ce8aec90171faacef75b9fbfd865cd236242cb621c4fff26e457b4dc387c49d9e2293c7b9e0d77eac36d8442cbba830f1fe68011fff4539f54374758
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
- end
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 = get_hash_from_xml(el, :attributes)
101
- country[:types] = {}
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 = get_hash_from_xml(el, :attributes)
115
- country[:types] = {}
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
- if @data[country[:id]][:types][type]
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
- formats = parse_formats(phone_type.children)
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 get_types_and_formats(children)
179
- types = {}
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
- get_hash_from_xml(phone_type, :element)
153
+ result[:types][name2sym(phone_type.name)] =
154
+ hash_from_xml(phone_type, :element)
188
155
  end
189
156
  end
190
157
 
191
- types = add_possible_if_not_exists(types)
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 add_possible_if_not_exists(types)
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
- types
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 = get_hash_from_xml(format, :children)
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.compact
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 get_country_by_code(country_code)
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
- prefixes[prefix.to_i] = {} unless prefixes[prefix.to_i]
14
- prefixes[prefix.to_i][key] = value
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 get_hash_from_xml(data, type)
61
+ def hash_from_xml(data, type)
36
62
  hash = {}
37
63
  case type
38
64
  when :attributes
@@ -42,20 +42,20 @@ module Phonelib
42
42
  def sanitized
43
43
  @sanitized ||=
44
44
  vanity_converted(@original).gsub(
45
- Phonelib.strict_check ? /^\+/.freeze : /[^0-9]+/.freeze,
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 = try_to_parse_country(phone, country)
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 !original_string.start_with?('+') && country_can_double_prefix?(country)
28
+ when country_can_dp?(country)
29
29
  # if country allows double prefix trying modified phone
30
- try_to_parse_country(changed_double_prefixed_phone(country, phone), country)
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
- if result.nil?
40
- return base_result || {}
41
- end
39
+ base_result ||= {}
40
+ return base_result unless result
42
41
 
43
- if base_result.nil? || base_result.empty? || base_result.values.find {|e| e[:possible].any? }.nil?
44
- return result
45
- end
42
+ return result unless base_result.values.find { |e| e[:possible].any? }
46
43
 
47
- if result && result.values.find {|e| e[:valid].any? }
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 try_to_parse_country(phone, country)
56
+ def parse_country(phone, country)
62
57
  data = Phonelib.phone_data[country]
63
- return nil unless country && data
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.delete('+'), nil) if e164[0] == '+'
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(e164, data, valid_match)
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(e164, data, possible_match, true)
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(changed_double_prefixed_phone(key, phone), data)
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
- national_start = phone.length - match.to_a.last.size
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(phone, data, country_match, not_valid = false)
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
- result[:national] = phone[prefix_length..-1] || ''
142
- result[:format] = number_format(result[:national], data[Core::FORMATS])
143
- result.merge! all_number_types(result[:national], data[Core::TYPES],
144
- not_valid)
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
- || national.match(cr("^(#{format[Core::LEADING_DIGITS]})"))) \
183
- && national.match(cr("^(#{format[Core::PATTERN]})$"))
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(/[a-z]/i.freeze) do |c|
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 == 'S'.freeze || c == 'V'.freeze || c >= 'Y'.freeze
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.start_with?('+') && code && !sanitized.start_with?(code)
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 country_can_double_prefix?(country)
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 changed_double_prefixed_phone(country, phone)
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 && data[Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
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 original_string
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 ||= (original_string.start_with?('+') ? nil : Phonelib.default_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 << if country_optional
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
- # * +possible_pattern+ - possible pattern for validation
169
- # * +national_pattern+ - valid pattern for validation
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, possible_pattern, national_pattern, not_valid = false)
172
- possible_match = number.match(cr("^(?:#{possible_pattern})$"))
173
- possible = possible_match && possible_match.to_s.length == number.length
174
-
175
- return [!not_valid && possible, possible] if possible_pattern == national_pattern
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
- Phonelib.phone_data[country][Core::COUNTRY_CODE]
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.nil? || sanitized.empty?
43
+ return nil if sanitized.empty?
44
44
  return "+#{country_prefix_or_not}#{sanitized}" unless valid?
45
- return "#{country_code}#{@national_number}" unless formatted
45
+ return country_code + @national_number unless formatted
46
46
 
47
- format = @data[country][:format]
48
- if (matches = @national_number.match(/#{format[Core::PATTERN]}/))
49
- fmt = format[:intl_format] || format[:format]
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
- format_match[1] == Core::AREA_CODE_MOBILE_TOKENS[country]
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 unless possible?
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
- format = @data[country][:format]
130
- prefix = @data[country][Core::NATIONAL_PREFIX]
131
- rule = (format[Core::NATIONAL_PREFIX_RULE] ||
132
- @data[country][Core::NATIONAL_PREFIX_RULE] || '$1')
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')
@@ -1,4 +1,4 @@
1
1
  module Phonelib
2
2
  # @private
3
- VERSION = '0.6.10'
3
+ VERSION = '0.6.11'
4
4
  end
@@ -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, country)
47
+ phone = parse(value, specified_country(record))
49
48
  valid = if simple_validation?
50
- method = options[:possible] ? :possible? : :valid?
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, options[:message] || :invalid, options) unless valid
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.10
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-03-01 00:00:00.000000000 Z
11
+ date: 2017-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake