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 =

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



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

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

#phoneObject (readonly)

Returns the value of attribute phone.



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

def phone
  @phone
end

Class Method Details

.ensure_phonelib!Object



127
128
129
130
131
132
133
134
135
136
137
# File 'app/lib/phone_number.rb', line 127

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



100
101
102
103
104
105
106
107
# File 'app/lib/phone_number.rb', line 100

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

rubocop:disable Lint/UnusedMethodArgument



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

def self.parse_and_format(phone_string, display_format: :e164, country_iso: nil) # rubocop:disable Lint/UnusedMethodArgument
  return unless (p = parse(phone_string))

  p.format(display_format)
end

.possible?(phone_string) ⇒ Boolean

Returns:

  • (Boolean)


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

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

.shortcode?(phone_string) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
# File 'app/lib/phone_number.rb', line 120

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

.valid?(phone_string) ⇒ Boolean

Returns:

  • (Boolean)


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

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:



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

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:



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

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:



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

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

#country_isoObject



86
87
88
# File 'app/lib/phone_number.rb', line 86

def country_iso
  @phone.country
end

#dial_stringObject



109
110
111
# File 'app/lib/phone_number.rb', line 109

def dial_string
  format(:pbx_dial)
end

#extensionObject

Alias for Phone#extension

Returns:

  • (Object)

    Phone#extension

See Also:



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

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



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

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:



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

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:



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

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:



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

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:



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

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:



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

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)


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

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:



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

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

#to_sObject



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

def to_s
  @phone.full_e164
end

#typeObject

Alias for Phone#type

Returns:

  • (Object)

    Phone#type

See Also:



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

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:



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

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:



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

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