phonie 1.0.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []