14

I was using phony to format phone numbers (meaning, if I put in xxx-xxx-xxxx it would convert to a string, and also tell if there is a (1) before to remove it).

But it really doesn't work for us phone number, it's designed for international numbers.

Is there an equivalent?

Thanks.

http://rubygems.org/gems/phony

Satchel
  • 16,414
  • 23
  • 106
  • 192

6 Answers6

21

Earlier this year, I reviewed a bunch of ruby gems that parse and format phone numbers. They fall into a number of groups (see below). TLDR: I used 'phone'. It might work for you because you can specify a default country code that it uses if your phone number doesn't include one.

1) US-centric:

big-phoney (0.1.4)
phone_wrangler (0.1.3)
simple_phone_number (0.1.9)

2) depends on rails or active_record:

phone_number (1.2.0)
validates_and_formats_phones (0.0.7)

3) forks of 'phone' that have been merged back into the trunk:

elskwid-phone (0.9.9.4)
tfe-phone (0.9.9.1)

4) relies on you to know the region ahead of time

phoney (0.1.0)

5) Kind of almost works for me

phone (0.9.9.3)

6) does not contain the substring 'phone' in the gem name (edit: I see you tried this one)

phony (1.6.1)

These groupings may be somewhat unfair or out of date so feel free to comment. I must admit I was a little frustrated at the time at how many people had partially re-invented this particular wheel.

Jason McLaren
  • 2,334
  • 2
  • 17
  • 9
  • The `phone` gem looks a bit dormant to me - several pull requests that haven't had a response. I used this fork instead: https://github.com/g1smd/carrphone as I need to get UK phone numbers right. – Dominic Sayers Jan 15 '13 at 14:16
  • Updated this given the list of options to explore. – Satchel Jan 14 '15 at 03:11
13

I've never seen much in the way of a reliable telephone number formatter because it's just so hard to get it right. Just when you think you've seen everything, some other format comes along and wrecks it.

Ten digit North American numbers are perhaps the easiest to format, you can use a regular expression, but as soon as you encounter extensions you're in trouble. Still, you can kind of hack it yourself if you want:

def formatted_number(number)
  digits = number.gsub(/\D/, '').split(//)

  if (digits.length == 11 and digits[0] == '1')
    # Strip leading 1
    digits.shift
  end

  if (digits.length == 10)
    # Rejoin for latest Ruby, remove next line if old Ruby
    digits = digits.join
    '(%s) %s-%s' % [ digits[0,3], digits[3,3], digits[6,4] ]
  end
end

This will just wrangle eleven and ten digit numbers into the format you want.

Some examples:

formatted_number("1 (703) 451-5115")
 # => "(703) 451-5115"
formatted_number("555-555-1212")
 # => "(555) 555-1212"
PW Kad
  • 14,953
  • 7
  • 49
  • 82
tadman
  • 208,517
  • 23
  • 234
  • 262
  • what happens though if it is given 4155551212? – Satchel May 07 '11 at 03:20
  • You could try it and find out for yourself, of course! As long as 11 or 10 digits are present, non-digit characters are ignored, it will format the same way. – tadman May 08 '11 at 22:02
  • oh...the non-digit characters are ignoted...I think I need to learn more about regex and what you did with it, that is why I am having problems...I am guessing \D is for non digits? – Satchel May 09 '11 at 05:08
  • `\s` is spaces, `\S` is non-spaces, so the upper-case version for several things is the opposite. See [Rubular](http://rubular.com/) for more examples and a tool to help. – tadman May 09 '11 at 14:21
  • Rubular is very useful. The view is using the edit_in_place plugin so I have to pass the :phone symbol, and it won't allow the helper method.... sorry, didn't mention it....how can I tweak it to work? – Satchel May 13 '11 at 14:05
  • I'm not sure what the context is here, as there's no mention of that in your original question. Maybe post a new one to address that specific issue? You were only asking about number formatting here. – tadman May 13 '11 at 16:41
  • would I implement this as an application_helper? – Satchel Jun 02 '11 at 00:52
  • Helper methods are only used by views, so if this is just a presentation problem then that's a good place to put it. Otherwise I'd put it in a utility class defined either in `config/initializers/` or `lib/` – tadman Jun 02 '11 at 15:59
  • 4
    In my ruby 1.9.3 I had to insert `digits = digits.join` before the line `'(%s) %s-%s' % [ digits[0,3], digits[3,3], digits[6,4] ]` – jwal Jan 27 '12 at 07:34
  • NumberHelper has number_to_phone now: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_phone. The method can easily become a bit more readable: `return phone_number_string unless phone_number_string && phone_number_string.length > 0 digits = phone_number_string.gsub(/\D/, '') return number_to_phone(digits, area_code: true)` – Michael Apr 25 '14 at 18:17
3

I wrote this regex to match NANPA phone numbers with some conventions (e.g. for extensions) for PHP (thank god those days are over) and converted it over to a Rails validator a few months ago for a project. It works great for me, but it is more pragmatic than strictly to spec.

# app/validators/phone_number_validator.rb
class PhoneNumberValidator < ActiveModel::EachValidator
  @@regex = %r{\A(?:1(?:[. -])?)?(?:\((?=\d{3}\)))?([2-9]\d{2})(?:(?<=\(\d{3})\))? ?(?:(?<=\d{3})[.-])?([2-9]\d{2})[. -]?(\d{4})(?: (?:ext|x)\.? ?(\d{1,5}))?\Z}

  def validate_each (object, attribute, value)
    if m = value.match(@@regex)
      # format the phone number consistently
      object.send("#{attribute}=", "(#{m[1]}) #{m[2]}-#{m[3]}")
    else
      object.errors[attribute] << (options[:message] || "is not an appropriately formatted phone number")
    end
  end
end

# app/models/foobar.rb
class Foobar < ActiveRecord::Base
  validates :phone, phone_number: true
end

The saved/outputted format is like this: (888) 888-8888. Currently the output strips off the extension because I didn't need it. You can add it back in and change the format pretty easily (see the object.send line.

coreyward
  • 77,547
  • 20
  • 137
  • 166
1
#RAILS_ROOT/lib/String.rb
class String
  def convert_to_phone
    number = self.gsub(/\D/, '').split(//)

    #US 11-digit numbers
    number = number.drop(1) if (number.count == 11 && number[0] == 1)

    #US 10-digit numbers
    number.to_s if (number.count == 10)

  end


  def format_phone
    return "#{self[0,3]}-#{self[3,3]}-#{self[6,4]}"
  end
end

"585-343-2070".convert_to_phone 
=> "5853432070"

"5853432070".convert_to_phone
=> "5853432070"

"1(585)343-2070".convert_to_phone.format_phone
=> "585-343-2070"

##Everything formatted as requested in Asker's various comments
Kyle Macey
  • 8,074
  • 2
  • 38
  • 78
  • @kyle I see, so this would be used to normalize to the database...then I would use the other response for the display side (to display it nice like xxx-xxx-xxxx> – Satchel May 07 '11 at 18:12
  • Not very DRY, though... I'll update in a bit here with something nicer – Kyle Macey May 07 '11 at 18:19
  • I see..thanks...what would be a better way...I was thinking it should be in the database all the same way and then allow me to format, I think this works, let me play around with it, thank you. – Satchel May 09 '11 at 05:06
  • @Kyle -- hmm, I tried it, and I have been getting an error in my controller saying cannot find method 'convert_to_phone' -- I will restart again the server...I put it into the lib/ folder.... – Satchel May 13 '11 at 14:03
  • Here is the full error: I tried changing it to a Moduule to include in the model, but still nothing: undefined method `convert_to_phone' for "15104762000":String – Satchel May 13 '11 at 14:24
  • Are you testing in the console or the actual application? – Kyle Macey May 13 '11 at 14:44
  • and sorry, it should be string.rb, not String.rb – Kyle Macey May 13 '11 at 14:44
  • hi, yes, I changed it to string.rb before posting it...still no love....I am testing it in the actual application. Let me try in the console – Satchel May 13 '11 at 20:34
  • okay, let me try that...not sure what happened, it looks like it should work...but hasn't been, thanks. – Satchel May 25 '11 at 01:24
  • This is the console errors I get back: irb(main):001:0> "585-343-2070".convert_to_phone NoMethodError: undefined method `convert_to_phone' for "585-343-2070":String from (irb):1 – Satchel Jun 02 '11 at 00:50
  • ah, okay I moved it into initializer instead. It sort of works. This gives e a nil, which it shouldn't: "1(585)343-2070".convert_to_phone – Satchel Jun 02 '11 at 01:13
0

You could use rails number_to_phone method

see here: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_phone

Dan Herman
  • 1,395
  • 1
  • 15
  • 26
0
def format_phone_numbers(n)
    "(#{n[-10..-8]}) #{n[-7..-5]}-#{n[-4..-1]}"
end

format_phone_numbers('555555555555')

"(555) 555-5555"

Andriy Kondzolko
  • 802
  • 10
  • 13