Class: Parse::Phone

Inherits:
Object
  • Object
show all
Defined in:
lib/parse/model/phone.rb

Overview

This class provides E.164 phone number validation and formatting for Parse properties. E.164 is the international telephone numbering format that ensures worldwide uniqueness.

Format: +[country code][subscriber number]

  • Must start with +

  • Country code: 1-3 digits (cannot start with 0)

  • Subscriber number: remaining digits

  • Total length: 8-15 digits (including country code)

Enhanced Validation with phonelib

For comprehensive phone number validation (including carrier validation, number type detection, and accurate country-specific rules), add the ‘phonelib` gem to your Gemfile:

gem 'phonelib'

When phonelib is available, Parse::Phone will use Google’s libphonenumber data for:

  • Accurate validation for all countries and territories

  • Number type detection (mobile, landline, toll-free, etc.)

  • Carrier information

  • Proper formatting per country standards

Without phonelib, basic E.164 format validation is used (sufficient for most use cases).

Examples:

Basic usage

class Contact < Parse::Object
  property :mobile, :phone
  property :work_phone, :phone, required: true
end

contact = Contact.new
contact.mobile = "+14155551234"
contact.mobile.valid?        # => true
contact.mobile.country_code  # => "1"
contact.mobile.national      # => "4155551234"

contact.mobile = "invalid"
contact.mobile.valid?        # => false

contact.mobile = "+1 (415) 555-1234"  # Automatically cleaned
contact.mobile.to_s          # => "+14155551234"

With phonelib (enhanced features)

phone = Parse::Phone.new("+14155551234")
phone.phone_type    # => :mobile (requires phonelib)
phone.carrier       # => "Verizon" (requires phonelib)
phone.possible?     # => true (quick check, requires phonelib)

Version:

  • 3.0.0

Constant Summary collapse

E164_REGEX =

E.164 format regex (strict validation for fallback mode)

  • Starts with +

  • Country code: 1-3 digits, cannot start with 0

  • Total digits: 8-15 (E.164 max is 15 digits total including country code)

/\A\+[1-9]\d{6,14}\z/
STRIP_NON_DIGITS =

Regex to strip non-digit characters (except +)

/[^\d+]/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#new(number) ⇒ Parse::Phone #new(phone) ⇒ Parse::Phone

Creates a new Phone instance.

Examples:

Parse::Phone.new("+14155551234")
Parse::Phone.new("1-415-555-1234")  # Will add + prefix
Parse::Phone.new("+1 (415) 555-1234")  # Will clean formatting

Overloads:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/parse/model/phone.rb', line 113

def initialize(value)
  @raw = nil
  @number = nil
  @phonelib_phone = nil

  if value.is_a?(String)
    @raw = value
    @number = normalize(value)
  elsif value.is_a?(Parse::Phone)
    @raw = value.raw
    @number = value.number
  elsif value.respond_to?(:to_s) && !value.nil?
    @raw = value.to_s
    @number = normalize(@raw)
  end

  # Parse with phonelib if available
  @phonelib_phone = Phonelib.parse(@number) if PHONELIB_AVAILABLE && @number
end

Instance Attribute Details

#numberString (readonly)

Returns the normalized E.164 formatted number (or nil if invalid input).

Returns:

  • (String)

    the normalized E.164 formatted number (or nil if invalid input)



98
99
100
# File 'lib/parse/model/phone.rb', line 98

def number
  @number
end

#rawString (readonly)

Returns the raw input value.

Returns:

  • (String)

    the raw input value



95
96
97
# File 'lib/parse/model/phone.rb', line 95

def raw
  @raw
end

Class Method Details

.phonelib_available?Boolean

Check if phonelib is available for enhanced validation

Returns:

  • (Boolean)

    true if phonelib gem is loaded



77
78
79
# File 'lib/parse/model/phone.rb', line 77

def phonelib_available?
  PHONELIB_AVAILABLE
end

.typecast(value) ⇒ Parse::Phone?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Type casting support for Parse properties. This allows the property system to convert values to Phone instances.

Parameters:

  • value (Object)

    the value to typecast

Returns:



87
88
89
90
91
# File 'lib/parse/model/phone.rb', line 87

def typecast(value)
  return nil if value.nil?
  return value if value.is_a?(Parse::Phone)
  Parse::Phone.new(value)
end

Instance Method Details

#==(other) ⇒ Boolean

Check equality with another phone number.

Parameters:

Returns:

  • (Boolean)

    true if the numbers are equal



339
340
341
342
343
344
345
346
347
# File 'lib/parse/model/phone.rb', line 339

def ==(other)
  if other.is_a?(Parse::Phone)
    @number == other.number
  elsif other.is_a?(String)
    @number == normalize(other)
  else
    false
  end
end

#as_json(*args) ⇒ String?

Returns the E.164 formatted phone number for JSON serialization.

Returns:

  • (String, nil)

    the E.164 formatted phone number for JSON serialization



157
158
159
# File 'lib/parse/model/phone.rb', line 157

def as_json(*args)
  @number
end

#blank?Boolean

Returns true if the phone number is blank/nil.

Returns:

  • (Boolean)

    true if the phone number is blank/nil



350
351
352
# File 'lib/parse/model/phone.rb', line 350

def blank?
  @number.blank?
end

#carrierString?

Get the carrier name for this phone number. Requires phonelib and may not be available for all numbers.

Returns:

  • (String, nil)

    the carrier name or nil



277
278
279
280
# File 'lib/parse/model/phone.rb', line 277

def carrier
  return nil unless PHONELIB_AVAILABLE && @phonelib_phone&.valid?
  @phonelib_phone.carrier
end

#countryString?

Get the two-letter ISO country code. Requires phonelib for accurate detection.

Examples:

Parse::Phone.new("+14155551234").country  # => "US" (with phonelib)

Returns:

  • (String, nil)

    the ISO 3166-1 alpha-2 country code (e.g., “US”, “GB”)



227
228
229
230
# File 'lib/parse/model/phone.rb', line 227

def country
  return nil unless PHONELIB_AVAILABLE && @phonelib_phone&.valid?
  @phonelib_phone.country
end

#country_codeString?

Get the country code portion of the phone number.

Examples:

Parse::Phone.new("+14155551234").country_code  # => "1"
Parse::Phone.new("+442071234567").country_code # => "44"

Returns:

  • (String, nil)

    the country code (without +) or nil if invalid



210
211
212
213
214
215
216
217
218
# File 'lib/parse/model/phone.rb', line 210

def country_code
  return nil unless valid?

  if PHONELIB_AVAILABLE && @phonelib_phone
    @phonelib_phone.country_code
  else
    extract_country_code_fallback
  end
end

#country_nameString?

Get the country/region name for this phone number’s country code.

Examples:

Parse::Phone.new("+14155551234").country_name  # => "United States"
Parse::Phone.new("+442071234567").country_name # => "United Kingdom"

Returns:

  • (String, nil)

    the country/region name or nil if unknown



298
299
300
301
302
303
304
305
306
# File 'lib/parse/model/phone.rb', line 298

def country_name
  if PHONELIB_AVAILABLE && @phonelib_phone&.valid?
    iso_code = @phonelib_phone.country
    ISO_COUNTRY_NAMES[iso_code] if iso_code
  else
    cc = country_code
    FALLBACK_COUNTRY_NAMES[cc] if cc
  end
end

#errorsArray<String>

Get validation errors for this phone number. Useful for providing user feedback.

Returns:



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/parse/model/phone.rb', line 363

def errors
  return [] if valid?
  return ["Phone number is required"] if @number.blank?

  if PHONELIB_AVAILABLE && @phonelib_phone
    result = []
    # Phonelib uses impossible? for basic length/format check
    if @phonelib_phone.impossible?
      sanitized = @phonelib_phone.sanitized
      result << "Phone number is too short" if sanitized.length < 7
      result << "Phone number is too long" if sanitized.length > 15
    end
    result << "Invalid phone number format" if result.empty?
    result
  else
    ["Invalid E.164 phone number format"]
  end
end

#formatted(format = :international) ⇒ String?

Format the phone number for display. When phonelib is available, uses proper country-specific formatting. Otherwise, provides basic formatted version.

Examples:

Parse::Phone.new("+14155551234").formatted             # => "+1 415-555-1234"
Parse::Phone.new("+14155551234").formatted(:national)  # => "(415) 555-1234"

Parameters:

  • format (Symbol) (defaults to: :international)

    :international (default), :national, or :e164

Returns:

  • (String, nil)

    formatted number or nil if invalid



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/parse/model/phone.rb', line 318

def formatted(format = :international)
  return nil unless valid?

  if PHONELIB_AVAILABLE && @phonelib_phone
    case format
    when :national
      @phonelib_phone.national
    when :e164
      @phonelib_phone.e164
    else
      @phonelib_phone.international
    end
  else
    format_fallback
  end
end

#geo_nameString?

Get the geographic area for this phone number. Requires phonelib and may not be available for mobile numbers.

Returns:

  • (String, nil)

    the geographic area or nil



286
287
288
289
# File 'lib/parse/model/phone.rb', line 286

def geo_name
  return nil unless PHONELIB_AVAILABLE && @phonelib_phone&.valid?
  @phonelib_phone.geo_name
end

#invalid?Boolean

Check if the phone number is invalid.

Returns:

  • (Boolean)

    true if the phone number is definitely invalid



199
200
201
# File 'lib/parse/model/phone.rb', line 199

def invalid?
  !valid?
end

#mobile?Boolean?

Check if this is a mobile phone number. Requires phonelib for accurate detection.

Returns:

  • (Boolean, nil)

    true if mobile, false if not, nil if unknown



267
268
269
270
271
# File 'lib/parse/model/phone.rb', line 267

def mobile?
  type = phone_type
  return nil if type.nil?
  [:mobile, :fixed_or_mobile].include?(type)
end

#nationalString?

Get the national (subscriber) number without country code.

Examples:

Parse::Phone.new("+14155551234").national  # => "4155551234"

Returns:

  • (String, nil)

    the national number or nil if invalid



238
239
240
241
242
243
244
245
246
247
248
# File 'lib/parse/model/phone.rb', line 238

def national
  return nil unless valid?

  if PHONELIB_AVAILABLE && @phonelib_phone
    @phonelib_phone.national(false)&.gsub(/\D/, "")
  else
    cc = country_code
    return nil unless cc
    @number[(cc.length + 1)..]  # Skip + and country code
  end
end

#normalize(value) ⇒ String?

Normalize a phone number string to E.164 format. Removes all non-digit characters except leading +.

Parameters:

  • value (String)

    the phone number string

Returns:

  • (String, nil)

    the normalized number or nil if invalid



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/parse/model/phone.rb', line 138

def normalize(value)
  return nil if value.blank?

  # Remove all non-digit characters except +
  cleaned = value.to_s.gsub(STRIP_NON_DIGITS, "")

  # If it doesn't start with +, add it
  cleaned = "+#{cleaned}" unless cleaned.start_with?("+")

  # Return the cleaned value (may still be invalid, but we store it)
  cleaned
end

#phone_typeSymbol?

Get the phone number type (mobile, landline, etc.). Requires phonelib for type detection.

Examples:

Parse::Phone.new("+14155551234").phone_type  # => :mobile (with phonelib)

Returns:

  • (Symbol, nil)

    the number type (:mobile, :fixed_line, :toll_free, etc.)



257
258
259
260
261
# File 'lib/parse/model/phone.rb', line 257

def phone_type
  return nil unless PHONELIB_AVAILABLE && @phonelib_phone&.valid?
  types = @phonelib_phone.types
  types.first if types.any?
end

#possible?Boolean

Check if the phone number is possibly valid (quick check). This is faster than full validation and useful for input feedback. Falls back to valid? when phonelib is not available.

Returns:

  • (Boolean)

    true if the number could be valid



186
187
188
189
190
191
192
193
194
# File 'lib/parse/model/phone.rb', line 186

def possible?
  return false if @number.blank?

  if PHONELIB_AVAILABLE && @phonelib_phone
    @phonelib_phone.possible?
  else
    valid?
  end
end

#present?Boolean

Returns true if the phone number is present.

Returns:

  • (Boolean)

    true if the phone number is present



355
356
357
# File 'lib/parse/model/phone.rb', line 355

def present?
  !blank?
end

#to_sString?

Returns the E.164 formatted phone number.

Returns:

  • (String, nil)

    the E.164 formatted phone number



152
153
154
# File 'lib/parse/model/phone.rb', line 152

def to_s
  @number
end

#valid?Boolean

Check if this phone number is valid E.164 format. When phonelib is available, uses comprehensive validation. Otherwise, uses basic E.164 regex validation.

Examples:

Parse::Phone.new("+14155551234").valid?  # => true
Parse::Phone.new("invalid").valid?       # => false
Parse::Phone.new("+1").valid?            # => false (too short)

Returns:

  • (Boolean)

    true if the phone number is valid



171
172
173
174
175
176
177
178
179
# File 'lib/parse/model/phone.rb', line 171

def valid?
  return false if @number.blank?

  if PHONELIB_AVAILABLE && @phonelib_phone
    @phonelib_phone.valid?
  else
    E164_REGEX.match?(@number)
  end
end