phonie 1.0.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/phonie/phone.rb CHANGED
@@ -1,3 +1,7 @@
1
+ require 'active_model/naming'
2
+ require 'active_model/translation'
3
+ require 'active_model/validations'
4
+
1
5
  # An object representing a phone number.
2
6
  #
3
7
  # The phone number is recorded in 3 separate parts:
@@ -11,6 +15,8 @@
11
15
  #
12
16
  module Phonie
13
17
  class Phone
18
+ EXTENSION = /[ ]*(ext|ex|x|xt|#|:)+[^0-9]*\(*([-0-9]{1,})\)*#?$/i
19
+
14
20
  attr_accessor :country_code, :area_code, :number, :extension, :country
15
21
 
16
22
  cattr_accessor :default_country_code
@@ -29,6 +35,11 @@ module Phonie
29
35
  :us => "(%a) %f-%l"
30
36
  }
31
37
 
38
+ include ActiveModel::Validations
39
+ validates :country_code, :presence => true
40
+ validates :area_code, :presence => true
41
+ validates :number, :presence => true
42
+
32
43
  def initialize(*hash_or_args)
33
44
  if hash_or_args.first.is_a?(Hash)
34
45
  hash_or_args = hash_or_args.first
@@ -37,103 +48,49 @@ module Phonie
37
48
  keys = {:number => 0, :area_code => 1, :country_code => 2, :extension => 3, :country => 4}
38
49
  end
39
50
 
40
- self.number = hash_or_args[ keys[:number] ]
41
- self.area_code = hash_or_args[ keys[:area_code] ] || self.default_area_code
51
+ self.number = hash_or_args[ keys[:number] ]
52
+ self.area_code = hash_or_args[ keys[:area_code] ] || self.default_area_code
42
53
  self.country_code = hash_or_args[ keys[:country_code] ] || self.default_country_code
43
- self.extension = hash_or_args[ keys[:extension] ]
44
- self.country = hash_or_args[ keys[:country] ]
45
-
46
- # Santity checks
47
- raise "Must enter number" if self.number.blank?
48
- raise "Must enter area code or set default area code" if self.area_code.blank?
49
- raise "Must enter country code or set default country code" if self.country_code.blank?
54
+ self.extension = hash_or_args[ keys[:extension] ]
55
+ self.country = hash_or_args[ keys[:country] ]
50
56
  end
51
57
 
52
- def self.parse!(string, options={})
53
- parse(string, options.merge(:raise_exception_on_error => true))
58
+ def self.parse!(string, options = {})
59
+ pn = parse(string, options)
60
+ raise ArgumentError.new("#{string} is not a valid phone number") unless pn && pn.valid?
61
+ pn
54
62
  end
55
63
 
56
64
  # create a new phone number by parsing a string
57
65
  # the format of the string is detect automatically (from FORMATS)
58
- def self.parse(string, options={})
59
- return nil unless string.present?
66
+ def self.parse(string, options = {})
67
+ return unless string.present?
60
68
 
61
- Country.load
69
+ options[:country_code] ||= self.default_country_code
70
+ options[:area_code] ||= self.default_area_code
62
71
 
63
72
  extension = extract_extension(string)
64
73
  normalized = normalize(string)
65
74
 
66
- options[:country_code] ||= self.default_country_code
67
- options[:area_code] ||= self.default_area_code
68
-
69
- parts = split_to_parts(normalized, options)
70
-
71
- pn = Phone.new(parts) if parts
72
- if pn.present? and extension.present?
73
- pn.extension = extension
74
- end
75
- pn
75
+ return unless country = Country.detect(normalized, options[:country_code], options[:area_code])
76
+ parts = country.parse(normalized, options[:area_code])
77
+ parts[:country] = country
78
+ parts[:country_code] = country.country_code
79
+ parts[:extension] = extension
80
+ new(parts)
76
81
  end
77
82
 
78
83
  # is this string a valid phone number?
79
84
  def self.valid?(string, options = {})
80
- begin
81
- parse(string, options).present?
82
- rescue
83
- false # don't raise exceptions on parse errors
84
- end
85
+ pn = parse(string, options)
86
+ pn && pn.valid?
85
87
  end
86
88
 
87
89
  def self.is_mobile?(string, options = {})
88
90
  pn = parse(string, options)
89
- return false if pn.nil?
90
- pn.is_mobile?
91
- end
92
-
93
- private
94
- # split string into hash with keys :country_code, :area_code and :number
95
- def self.split_to_parts(string, options = {})
96
- country = Country.detect(string, options[:country_code], options[:area_code])
97
-
98
- if country.nil?
99
- raise "Could not determine country" if options[:raise_exception_on_error]
100
- return nil
101
- end
102
-
103
- country.number_parts(string, options[:area_code])
91
+ pn && pn.is_mobile?
104
92
  end
105
93
 
106
- # fix string so it's easier to parse, remove extra characters etc.
107
- def self.normalize(string_with_number)
108
- string_with_number.sub(extension_regex, '').gsub(/\(0\)|[^0-9+]/, '').gsub(/^00/, '+')
109
- end
110
-
111
- def self.extension_regex
112
- /[ ]*(ext|ex|x|xt|#|:)+[^0-9]*\(*([-0-9]{1,})\)*#?$/i
113
- end
114
-
115
- # pull off anything that look like an extension
116
- #
117
- def self.extract_extension(string)
118
- return nil if string.nil?
119
- if string.match extension_regex
120
- extension = $2
121
- return extension
122
- end
123
- #
124
- # We already returned any recognizable extension.
125
- # However, we might still have extra junk to the right
126
- # of the phone number proper, so just chop it off.
127
- #
128
- idx = string.rindex(/[0-9]/)
129
- return nil if idx.nil?
130
- return nil if idx == (string.length - 1) # at the end
131
- string.slice!((idx+1)..-1) # chop it
132
- return nil
133
- end
134
-
135
- public # instance methods
136
-
137
94
  def area_code_long
138
95
  "0" + area_code if area_code
139
96
  end
@@ -157,7 +114,7 @@ module Phonie
157
114
 
158
115
  # Formats the phone number.
159
116
  #
160
- # if the method argument is a String, it is used as a format string, with the following fields being interpolated:
117
+ # if the method argument is a String, it is used as a format string, with the following fields being interpolated:
161
118
  #
162
119
  # * %c - country_code (385)
163
120
  # * %a - area_code (91)
@@ -171,7 +128,7 @@ module Phonie
171
128
  # pn.format(:europe)
172
129
  def format(fmt)
173
130
  if fmt.is_a?(Symbol)
174
- raise "The format #{fmt} doesn't exist'" unless named_formats.has_key?(fmt)
131
+ raise ArgumentError.new("The format #{fmt} doesn't exist") unless named_formats.has_key?(fmt)
175
132
  format_number named_formats[fmt]
176
133
  else
177
134
  format_number(fmt)
@@ -201,15 +158,31 @@ module Phonie
201
158
 
202
159
  private
203
160
 
161
+ # split string into hash with keys :country_code, :area_code and :number
162
+ def self.split_to_parts(string, options = {})
163
+ country = Country.detect(string, options[:country_code], options[:area_code])
164
+ country && country.parse(string, options[:area_code])
165
+ end
166
+
167
+ # fix string so it's easier to parse, remove extra characters etc.
168
+ def self.normalize(string_with_number)
169
+ string_with_number.sub(EXTENSION, '').gsub(/\(0\)|[^0-9+]/, '').gsub(/^00/, '+')
170
+ end
171
+
172
+ # pull off anything that look like an extension
173
+ def self.extract_extension(string)
174
+ return unless string && string.match(EXTENSION)
175
+ Regexp.last_match[2]
176
+ end
177
+
204
178
  def format_number(fmt)
205
- result = fmt.gsub("%c", country_code || "").
206
- gsub("%a", area_code || "").
207
- gsub("%A", area_code_long || "").
208
- gsub("%n", number || "").
209
- gsub("%f", number1 || "").
210
- gsub("%l", number2 || "").
211
- gsub("%x", extension || "")
212
- return result
179
+ fmt.gsub("%c", country_code || "").
180
+ gsub("%a", area_code || "").
181
+ gsub("%A", area_code_long || "").
182
+ gsub("%n", number || "").
183
+ gsub("%f", number1 || "").
184
+ gsub("%l", number2 || "").
185
+ gsub("%x", extension || "")
213
186
  end
214
187
  end
215
188
  end
@@ -1,3 +1,3 @@
1
1
  module Phonie
2
- VERSION = '1.0.4'
2
+ VERSION = '2.0.0'
3
3
  end
data/phonie.gemspec CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
6
6
  s.name = "phonie"
7
7
  s.version = Phonie::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ['Tomislav Car', 'Todd Eichel', 'Don Morrison', 'Wesley Moxam']
10
- s.email = ['tomislav@infinum.hr', 'todd@toddeichel.com', 'elskwid@gmail.com', 'wesley@wmoxam.com']
9
+ s.authors = ['Tomislav Car', 'Todd Eichel', 'Don Morrison', 'Wesley Moxam', 'Lance Ivy']
10
+ s.email = ['tomislav@infinum.hr', 'todd@toddeichel.com', 'elskwid@gmail.com', 'wesley@wmoxam.com', 'lance@kickstarter.com']
11
11
  s.homepage = "http://github.com/wmoxam/phonie"
12
12
  s.summary = %q{Phone number parsing, validation and formatting}
13
13
  s.description = %q{Phone number parsing, validation and formatting}
@@ -15,7 +15,9 @@ Gem::Specification.new do |s|
15
15
  s.files = `git ls-files`.split("\n")
16
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
17
  s.require_paths = ["lib"]
18
+
19
+ s.add_dependency 'activemodel'
20
+
18
21
  s.add_development_dependency 'rake'
19
22
  s.add_development_dependency 'nokogiri'
20
- s.add_development_dependency 'activemodel'
21
23
  end
@@ -4,6 +4,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
4
4
  class GRTest < Phonie::TestCase
5
5
 
6
6
  def test_local
7
- parse_test('+302112345678', '30', '21', '12345678', 'Canada')
7
+ parse_test('+302112345678', '30', '21', '12345678', 'Greece')
8
8
  end
9
9
  end
@@ -1,7 +1,7 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  ## Guam
4
- class GRTest < Phonie::TestCase
4
+ class GUTest < Phonie::TestCase
5
5
 
6
6
  def test_local
7
7
  parse_test('+16711234567', '1', '671', '1234567', 'Guam')
data/test/country_test.rb CHANGED
@@ -3,10 +3,10 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
3
3
  class CountryTest < Phonie::TestCase
4
4
  def test_find_by_country_name
5
5
  country = Phonie::Country.find_by_name('canada')
6
- assert_equal country.name, "Canada"
6
+ assert_equal "Canada", country.name
7
7
 
8
8
  country = Phonie::Country.find_by_name('Canada')
9
- assert_equal country.name, "Canada"
9
+ assert_equal "Canada", country.name
10
10
 
11
11
  assert_nil Phonie::Country.find_by_name(nil)
12
12
  assert_nil Phonie::Country.find_by_country_code(nil)
@@ -15,13 +15,13 @@ class CountryTest < Phonie::TestCase
15
15
 
16
16
  def test_find_by_country_code
17
17
  country = Phonie::Country.find_by_country_code('NO')
18
- assert_equal country.name, "Norway"
18
+ assert_equal "Norway", country.name
19
19
  end
20
20
 
21
21
  def test_find_all_by_phone_code
22
22
  countries = Phonie::Country.find_all_by_phone_code('47')
23
- assert_equal countries.length, 1
24
- assert_equal countries.first.name, "Norway"
23
+ assert_equal 1, countries.length
24
+ assert_equal "Norway", countries.first.name
25
25
  end
26
26
 
27
27
  end
data/test/phone_test.rb CHANGED
@@ -9,18 +9,18 @@ class PhoneTest < Phonie::TestCase
9
9
  def test_number_without_country_code_initialize
10
10
  Phonie::Phone.default_country_code = nil
11
11
 
12
- assert_raise RuntimeError do
13
- pn = Phonie::Phone.new '5125486', '91'
14
- end
12
+ pn = Phonie::Phone.new '5125486', '91'
13
+ assert !pn.valid?
14
+ assert_equal ["can't be blank"], pn.errors[:country_code]
15
15
  end
16
16
 
17
- def test_number_without_country_and_area_code_initialize
18
- Phonie::Phone.default_country_code = nil
17
+ def test_number_without_area_code_initialize
18
+ Phonie::Phone.default_country_code = '1'
19
19
  Phonie::Phone.default_area_code = nil
20
20
 
21
- assert_raise RuntimeError do
22
- pn = Phonie::Phone.new '451588'
23
- end
21
+ pn = Phonie::Phone.new '451588'
22
+ assert !pn.valid?
23
+ assert_equal ["can't be blank"], pn.errors[:area_code]
24
24
  end
25
25
 
26
26
  def test_number_with_default_area_code_initialize
@@ -52,8 +52,8 @@ class PhoneTest < Phonie::TestCase
52
52
  end
53
53
 
54
54
  def test_parse_empty
55
- assert_equal Phonie::Phone.parse(''), nil
56
- assert_equal Phonie::Phone.parse(nil), nil
55
+ assert_equal nil, Phonie::Phone.parse('')
56
+ assert_equal nil, Phonie::Phone.parse(nil)
57
57
  end
58
58
 
59
59
  def test_parse_short_without_special_characters_without_country
@@ -61,7 +61,7 @@ class PhoneTest < Phonie::TestCase
61
61
 
62
62
  assert_nil Phonie::Phone.parse "0915125486"
63
63
 
64
- assert_raise RuntimeError do
64
+ assert_raise ArgumentError do
65
65
  Phonie::Phone.parse! "0915125486"
66
66
  end
67
67
  end
@@ -71,7 +71,7 @@ class PhoneTest < Phonie::TestCase
71
71
 
72
72
  assert_nil Phonie::Phone.parse "091/512-5486"
73
73
 
74
- assert_raise RuntimeError do
74
+ assert_raise ArgumentError do
75
75
  Phonie::Phone.parse! "091/512-5486"
76
76
  end
77
77
  end
@@ -97,23 +97,23 @@ class PhoneTest < Phonie::TestCase
97
97
  def test_format_special_without_country_code
98
98
  Phonie::Phone.default_country_code = '385'
99
99
  pn = Phonie::Phone.new '5125486', '91'
100
- assert_equal pn.format("%A/%f-%l"), '091/512-5486'
100
+ assert_equal '091/512-5486', pn.format("%A/%f-%l")
101
101
  end
102
102
 
103
103
  def test_format_with_symbol_specifier
104
104
  Phonie::Phone.default_country_code = nil
105
105
  pn = Phonie::Phone.new '5125486', '91', '385'
106
- assert_equal pn.format(:europe), '+385 (0) 91 512 5486'
106
+ assert_equal '+385 (0) 91 512 5486', pn.format(:europe)
107
107
  end
108
108
 
109
109
  def test_valid
110
- assert_equal Phonie::Phone.valid?('915125486', :country_code => '385'), true
111
- assert_equal Phonie::Phone.valid?('385915125486'), true
110
+ assert Phonie::Phone.valid?('915125486', :country_code => '385')
111
+ assert Phonie::Phone.valid?('385915125486')
112
112
  end
113
113
 
114
114
  def test_doesnt_validate
115
- assert_equal Phonie::Phone.valid?('asdas'), false
116
- assert_equal Phonie::Phone.valid?('38591512548678'), false
115
+ assert !Phonie::Phone.valid?('asdas')
116
+ assert !Phonie::Phone.valid?('38591512548678')
117
117
  end
118
118
 
119
119
  def test_comparison_true
@@ -129,9 +129,9 @@ class PhoneTest < Phonie::TestCase
129
129
  end
130
130
 
131
131
  def test_parse_number_without_international_code
132
- assert_equal (Phonie::Phone.parse "90123456"), nil
133
- assert_equal (Phonie::Phone.parse "90123456", :country_code => '47').format(:default), "+4790123456"
134
- assert_equal (Phonie::Phone.parse "90123456", :country_code => '47', :area_code => '').format(:default), "+4790123456"
132
+ assert_equal nil, Phonie::Phone.parse("90123456")
133
+ assert_equal "+4790123456", Phonie::Phone.parse("90123456", :country_code => '47').format(:default)
134
+ assert_equal "+4790123456", Phonie::Phone.parse("90123456", :country_code => '47', :area_code => '').format(:default)
135
135
  end
136
136
 
137
137
  end
data/tools/generate CHANGED
@@ -23,15 +23,15 @@ def already_exists?
23
23
  end
24
24
 
25
25
  def get_country_code
26
- YAML.load(File.read(@data_file)).each_pair do |key, c|
27
- return c[:char_3_code].downcase if c[:name].downcase == @country.downcase
26
+ YAML.load(File.read(@data_file)).each do |c|
27
+ return c[:iso_3166_code].downcase if c[:name].downcase == @country.downcase
28
28
  end
29
29
  nil
30
30
  end
31
31
 
32
32
  def get_country_call_code
33
- YAML.load(File.read(@data_file)).each_pair do |key, c|
34
- return key if c[:name].downcase == @country.downcase
33
+ YAML.load(File.read(@data_file)).each do |c|
34
+ return c[:country_code] if c[:name].downcase == @country.downcase
35
35
  end
36
36
  nil
37
37
  end
@@ -48,24 +48,26 @@ class #{@country_code.upcase}Test < Phonie::TestCase
48
48
  def test_local
49
49
  parse_test('+#{country_call_code}', '#{country_call_code}', '', '', '#{@country}', false)
50
50
  end
51
-
51
+
52
52
  def test_mobile
53
53
  parse_test('+#{country_call_code}', '#{country_call_code}', '', '', '#{@country}', true)
54
54
  end
55
- end
55
+ end
56
56
  eof
57
57
  end
58
58
  puts "Create: #{@test_filename}"
59
59
  end
60
60
 
61
61
  def add_missing_fields
62
- hash = YAML.load(File.read(@data_file))
63
- hash[get_country_call_code].merge!({:area_code => ' ',
62
+ arr = YAML.load(File.read(@data_file))
63
+ arr.find{|c| c[:name].downcase == @country.downcase}.merge!(
64
+ :area_code => ' ',
64
65
  :local_number_format => ' ',
65
66
  :mobile_format => ' ',
66
- :number_format => ' '})
67
+ :number_format => ' '
68
+ )
67
69
  File.open(@data_file, 'w') do |f|
68
- f.puts hash.to_yaml
70
+ f.puts arr.to_yaml
69
71
  end
70
72
 
71
73
  puts "Modified: #{@data_file}"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phonie
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,20 +9,21 @@ authors:
9
9
  - Todd Eichel
10
10
  - Don Morrison
11
11
  - Wesley Moxam
12
+ - Lance Ivy
12
13
  autorequire:
13
14
  bindir: bin
14
15
  cert_chain: []
15
- date: 2013-04-21 00:00:00.000000000 Z
16
+ date: 2013-05-01 00:00:00.000000000 Z
16
17
  dependencies:
17
18
  - !ruby/object:Gem::Dependency
18
- name: rake
19
+ name: activemodel
19
20
  requirement: !ruby/object:Gem::Requirement
20
21
  none: false
21
22
  requirements:
22
23
  - - ! '>='
23
24
  - !ruby/object:Gem::Version
24
25
  version: '0'
25
- type: :development
26
+ type: :runtime
26
27
  prerelease: false
27
28
  version_requirements: !ruby/object:Gem::Requirement
28
29
  none: false
@@ -31,7 +32,7 @@ dependencies:
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0'
33
34
  - !ruby/object:Gem::Dependency
34
- name: nokogiri
35
+ name: rake
35
36
  requirement: !ruby/object:Gem::Requirement
36
37
  none: false
37
38
  requirements:
@@ -47,7 +48,7 @@ dependencies:
47
48
  - !ruby/object:Gem::Version
48
49
  version: '0'
49
50
  - !ruby/object:Gem::Dependency
50
- name: activemodel
51
+ name: nokogiri
51
52
  requirement: !ruby/object:Gem::Requirement
52
53
  none: false
53
54
  requirements:
@@ -68,6 +69,7 @@ email:
68
69
  - todd@toddeichel.com
69
70
  - elskwid@gmail.com
70
71
  - wesley@wmoxam.com
72
+ - lance@kickstarter.com
71
73
  executables: []
72
74
  extensions: []
73
75
  extra_rdoc_files: []