Class: PhoneNumber

Inherits:
Object
  • Object
show all
Defined in:
app/lib/phone_number.rb

Overview

PhoneNumber

Encapsulates parsing, formatting and validation of international phone numbers.

Usage:

phone = PhoneNumber.new("+15551231234")
phone.valid? # => true
phone.format(:e164) # => "+15551231234"

Valid options for format():
:crm - US/Canada numbers formatted as (555) 123-1234, international as +12345678901234
:crm_no_extension - Same as above but without extension
:e164 - International format +12345678901234
:fax_dial - Formatted for dialing a fax machine
:pbx_dial - Raw digits formatted for dialing on a PBX system

Validation functions:

PhoneNumber.valid?(number) - Returns true if number is valid
PhoneNumber.possible?(number) - Returns true if number is possible

Parse and format in one step:

PhoneNumber.parse_and_format(number, format: :e164)

Information about a parsed number:

phone.country # Country
phone.country_iso # Country ISO code
phone.timezone # Timezone
phone.carrier # Carrier
phone.type # Landline, mobile etc

And more - see docs for Phonelib gem for additional info

Constant Summary collapse

PHONELIB_MUTEX =
Mutex.new

Instance Attribute Summary collapse

Delegated Instance Attributes collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(phone_string, country_iso = nil) ⇒ PhoneNumber

You can pass a phone number formatted in any format
Additionally, you can feed a country_iso (could be an array of preference too)
to help the algorithm figure it out



47
48
49
50
51
52
53
54
55
# File 'app/lib/phone_number.rb', line 47

def initialize(phone_string, country_iso = nil)
  self.class.ensure_phonelib!
  pn = phone_string.dup.to_s
  if country_iso.nil? && pn.first != '+' && pn.length == 10
    #prepend a 1 which will force this to be parsed as a US/CA number
    pn.insert(0,'1')
  end
  @phone = Phonelib.parse(pn, country_iso)
end

Instance Attribute Details

#country_isoObject (readonly)

Returns the value of attribute country_iso.



38
39
40
# File 'app/lib/phone_number.rb', line 38

def country_iso
  @country_iso
end

#phoneObject (readonly)

Returns the value of attribute phone.



38
39
40
# File 'app/lib/phone_number.rb', line 38

def phone
  @phone
end

Class Method Details

.ensure_phonelib!Object



123
124
125
126
127
128
129
130
131
132
# File 'app/lib/phone_number.rb', line 123

def self.ensure_phonelib!
  return if @phonelib_loaded

  PHONELIB_MUTEX.synchronize do
    return if @phonelib_loaded
    require 'phonelib'
    PhonelibConfig.ensure_configured!
    @phonelib_loaded = true
  end
end

.parse(phone_string, country_iso: nil, strict: false) ⇒ Object



98
99
100
101
102
103
104
# File 'app/lib/phone_number.rb', line 98

def self.parse(phone_string, country_iso: nil, strict: false)
  p = new(phone_string, country_iso)
  # By default we only check for possible, but if strict is passed, we have to pass
  # the valid check which goes more in depth
  return nil if !p.possible? || (strict && !p.valid?)
  p
end

.parse_and_format(phone_string, display_format: :e164, country_iso: nil) ⇒ Object



93
94
95
96
# File 'app/lib/phone_number.rb', line 93

def self.parse_and_format(phone_string, display_format: :e164, country_iso: nil)
  return unless (p = parse(phone_string))
  p.format(display_format)
end

.possible?(phone_string) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
# File 'app/lib/phone_number.rb', line 61

def self.possible?(phone_string)
  new(phone_string).possible?
end

.shortcode?(phone_string) ⇒ Boolean

Returns:

  • (Boolean)


117
118
119
# File 'app/lib/phone_number.rb', line 117

def self.shortcode?(phone_string)
  new(phone_string).shortcode?
end

.valid?(phone_string) ⇒ Boolean

Returns:

  • (Boolean)


57
58
59
# File 'app/lib/phone_number.rb', line 57

def self.valid?(phone_string)
  new(phone_string).valid?
end

Instance Method Details

#area_codeObject

Alias for Phone#area_code

Returns:

  • (Object)

    Phone#area_code

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#carrierObject

Alias for Phone#carrier

Returns:

  • (Object)

    Phone#carrier

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#countryObject

Alias for Phone#country

Returns:

  • (Object)

    Phone#country

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#dial_stringObject



106
107
108
# File 'app/lib/phone_number.rb', line 106

def dial_string
  format(:pbx_dial)
end

#extensionObject

Alias for Phone#extension

Returns:

  • (Object)

    Phone#extension

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#format(format_type = nil) ⇒ Object

Formats the parsed national number according to our rules.
With the crm type, a US or canadian number will format as national (123) 456-3456
please note that this should be adapted based on the user's locale instead



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'app/lib/phone_number.rb', line 68

def format(format_type = nil)
  case format_type
  when :crm
    @phone.country_code == '1' ? @phone.full_national : @phone.full_e164
  when :crm_no_extension
    @phone.country_code == '1' ? @phone.national : @phone.e164
  when :pbx_dial, :e164
    @phone.e164
  when :fax_dial
    @phone.international('00').scan(/\d/).join
  when :strict_10
    @phone.national.scan(/\d/).first(10).join
  else
    @phone.full_e164
  end
end

#geo_nameObject

Alias for Phone#geo_name

Returns:

  • (Object)

    Phone#geo_name

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#originalObject

Alias for Phone#original

Returns:

  • (Object)

    Phone#original

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#possible?Object

Alias for Phone#possible?

Returns:

  • (Object)

    Phone#possible?

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#possible_typesObject

Alias for Phone#possible_types

Returns:

  • (Object)

    Phone#possible_types

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#sanitizedObject

Alias for Phone#sanitized

Returns:

  • (Object)

    Phone#sanitized

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#shortcode?Boolean

US/Canada shortcodes are 5-6 digit numbers used by businesses for SMS marketing
and automated messages. We cannot send SMS to shortcodes, only receive from them.

Returns:

  • (Boolean)


112
113
114
115
# File 'app/lib/phone_number.rb', line 112

def shortcode?
  digits = sanitized.to_s.gsub(/\D/, '')
  digits.length.between?(5, 6)
end

#timezoneObject

Alias for Phone#timezone

Returns:

  • (Object)

    Phone#timezone

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#to_sObject



89
90
91
# File 'app/lib/phone_number.rb', line 89

def to_s
  @phone.full_e164
end

#typeObject

Alias for Phone#type

Returns:

  • (Object)

    Phone#type

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#typesObject

Alias for Phone#types

Returns:

  • (Object)

    Phone#types

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone

#valid?Object

Alias for Phone#valid?

Returns:

  • (Object)

    Phone#valid?

See Also:



40
41
42
# File 'app/lib/phone_number.rb', line 40

delegate :valid?, :possible?, :type, :types, :possible_types, :country,
:geo_name, :timezone, :carrier, :area_code, :extension, :sanitized, :original,
to: :phone