lib/phonelib/phone_analyzer_helper.rb
module Phonelib
# @private helper methods for analyser
module PhoneAnalyzerHelper
private
def decorate_analyze_result(result)
if result.size == 1
result
else
matched_countries = country_or_default_country(nil) & result.keys
result = result.keep_if {|k, _v| matched_countries.include?(k) } if matched_countries
Hash[result.take(1)]
end
end
def original_starts_with_plus?
original_s[0] == Core::PLUS_SIGN
end
# converts symbols in phone to numbers
def vanity_converted(phone)
return phone unless Phonelib.vanity_conversion
(phone || '').gsub(cr('[a-zA-Z]')) do |c|
c.upcase!
# subtract "A"
n = (c.ord - 65) / 3
# account for #7 & #9 which have 4 chars
n -= 1 if c.match(Core::VANITY_4_LETTERS_KEYS_REGEX)
(n + 2).to_s
end
end
# defines if to validate against single country or not
def passed_country(country)
code = country_prefix(country)
if !Phonelib.ignore_plus && Core::PLUS_SIGN == @original[0] && code && !sanitized.start_with?(code)
# in case number passed with + but it doesn't start with passed
# country prefix
country = nil
end
country
end
# returns country prefix for provided country or nil
def country_prefix(country)
country = country.to_s.upcase
Phonelib.phone_data[country] && \
Phonelib.phone_data[country][Core::COUNTRY_CODE]
end
# caches regular expression, reusing it for later lookups
def cr(regexp)
Phonelib.phone_regexp_cache[regexp] ||= Regexp.new(regexp).freeze
end
# defines whether country can have double country prefix in number
def country_can_dp?(country)
Phonelib.phone_data[country] &&
Phonelib.phone_data[country][Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
!original_starts_with_plus? && original_s.start_with?(Phonelib.phone_data[country][Core::COUNTRY_CODE])
end
# changes phone to with/without double country prefix
def changed_dp_phone(country, phone)
data = Phonelib.phone_data[country]
return if data.nil? || data[Core::DOUBLE_COUNTRY_PREFIX_FLAG].nil?
country_code = Phonelib.phone_data[country][Core::COUNTRY_CODE]
if phone.start_with? country_code * 2
# remove double prefix in case it is there
phone.gsub(cr("^#{country_code}"), '')
else
"#{country_code}#{phone}"
end
end
# checks if country can have numbers with double country prefixes
#
# ==== Attributes
#
# * +data+ - country data used for parsing
# * +phone+ - phone number being parsed
# * +parsed+ - parsed regex match for phone
def double_prefix_allowed?(data, phone, parsed = {})
data[Core::DOUBLE_COUNTRY_PREFIX_FLAG] &&
phone =~ cr("^#{data[Core::COUNTRY_CODE]}") &&
parsed && (parsed[:valid].nil? || parsed[:valid].empty?) &&
!original_starts_with_plus?
end
# Returns original number passed if it's a string or empty string otherwise
def original_s
@original_s ||= @original.is_a?(String) ? @original : ''
end
# Get country that was provided or default country in needable format
#
# ==== Attributes
#
# * +country+ - country passed for parsing
def country_or_default_country(country)
country ||= (original_starts_with_plus? ? nil : Phonelib.default_country)
if country.is_a?(Array)
country.compact.map { |e| e.to_s.upcase }
else
[country && country.to_s.upcase]
end
end
# constructs full regex for phone validation for provided phone data
# (international prefix, country code, national prefix, valid number)
#
# ==== Attributes
#
# * +data+ - country data hash
# * +country_optional+ - whether to put country code as optional group
def full_regex_for_data(data, type, country_optional = true)
regex = []
regex << '0{2}?'
regex << "(#{data[Core::INTERNATIONAL_PREFIX]})?"
regex << "(#{data[Core::COUNTRY_CODE]})#{country_optional ? '?' : ''}"
regex << "(#{data[Core::NATIONAL_PREFIX_FOR_PARSING] || data[Core::NATIONAL_PREFIX]})?"
regex << "(#{type_regex(data[Core::TYPES][Core::GENERAL], type)})" if data[Core::TYPES]
cr("^#{regex.join}$")
end
# Returns regex for type with special types if needed
#
# ==== Attributes
#
# * +data+ - country types data for single type
# * +type+ - possible or valid regex type needed
def type_regex(data, type)
regex = [data[type]]
if Phonelib.parse_special && data[Core::SHORT] && data[Core::SHORT][type]
regex << data[Core::SHORT][type]
end
regex.join('|')
end
# Check if phone match country data
#
# ==== Attributes
#
# * +phone+ - phone number for parsing
# * +data+ - country data
def phone_match_data?(phone, data, possible = false)
country_code = "#{data[Core::COUNTRY_CODE]}"
inter_prefix = "(#{data[Core::INTERNATIONAL_PREFIX]})?"
return unless phone.match cr("^0{2}?#{inter_prefix}#{country_code}")
type = possible ? Core::POSSIBLE_PATTERN : Core::VALID_PATTERN
phone.match full_regex_for_data(data, type, false)
end
# checks if types has both :mobile and :fixed_line and replaces it with
# :fixed_or_mobile in case both present
def sanitize_fixed_mobile(types)
fixed_mobile = [Core::FIXED_LINE, Core::MOBILE]
[:possible, :valid].each do |key|
if (fixed_mobile - types[key]).empty?
types[key] = types[key] - fixed_mobile + [Core::FIXED_OR_MOBILE]
end
end
types
end
# returns array of phone types for check for current country data
#
# ==== Attributes
#
# * +data+ - country data hash
def types_for_check(data)
exclude_list = PhoneAnalyzer::NOT_FOR_CHECK
exclude_list += Phonelib::Core::SHORT_CODES unless Phonelib.parse_special
Core::TYPES_DESC.keys - exclude_list + fixed_and_mobile_keys(data)
end
# Checks if fixed line pattern and mobile pattern are the same and returns
# appropriate keys
#
# ==== Attributes
#
# * +data+ - country data
def fixed_and_mobile_keys(data)
if data[Core::FIXED_LINE] == data[Core::MOBILE]
[Core::FIXED_OR_MOBILE]
else
[Core::FIXED_LINE, Core::MOBILE]
end
end
# Checks if passed number matches valid and possible patterns
#
# ==== Attributes
#
# * +number+ - phone number for validation
# * +p_regex+ - possible regex pattern for validation
# * +v_regex+ - valid regex pattern for validation
# * +not_valid+ - specifies that number is not valid by general desc pattern
def number_valid_and_possible?(number, p_regex, v_regex, not_valid = false)
possible = number_match?(number, p_regex)
return [!not_valid && possible, possible] if p_regex == v_regex
valid = !not_valid && possible && number_match?(number, v_regex)
[valid && possible, possible]
end
# Checks number against regex and compares match length
#
# ==== Attributes
#
# * +number+ - phone number for validation
# * +regex+ - regex for perfoming a validation
def number_match?(number, regex)
match = number.match(cr("^(?:#{regex})$"))
match && match.to_s.length == number.length
end
end
end