phone 1.2.3 → 1.3.0.beta0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.document +3 -0
  3. data/.gitignore +5 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +14 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +50 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +183 -0
  11. data/Rakefile +38 -0
  12. data/data/{phone_countries.yml → phone/countries.yml} +16 -14
  13. data/lib/phone.rb +75 -53
  14. data/lib/phone/country.rb +45 -0
  15. data/lib/{errors.rb → phone/errors.rb} +0 -0
  16. data/lib/phone/version.rb +4 -0
  17. data/phone.gemspec +25 -0
  18. data/test/countries/au_test.rb +11 -11
  19. data/test/countries/ba_test.rb +4 -4
  20. data/test/countries/be_test.rb +12 -11
  21. data/test/countries/de_test.rb +7 -7
  22. data/test/countries/es_test.rb +33 -0
  23. data/test/countries/fr_test.rb +5 -5
  24. data/test/countries/gb_test.rb +42 -41
  25. data/test/countries/hr_test.rb +14 -14
  26. data/test/countries/hu_test.rb +5 -5
  27. data/test/countries/ie_test.rb +20 -0
  28. data/test/countries/nl_test.rb +43 -43
  29. data/test/countries/nz_test.rb +8 -0
  30. data/test/countries/pt_test.rb +18 -18
  31. data/test/countries/rs_test.rb +7 -7
  32. data/test/countries/se_test.rb +44 -43
  33. data/test/countries/si_test.rb +7 -7
  34. data/test/countries/ua_test.rb +4 -4
  35. data/test/countries/us_test.rb +6 -6
  36. data/test/countries/uy_test.rb +22 -0
  37. data/test/countries/za_test.rb +4 -4
  38. data/test/extension_test.rb +3 -3
  39. data/test/helper.rb +41 -0
  40. data/test/phone_test.rb +99 -53
  41. data/test_usa_phones_with_extensions.csv +99 -0
  42. metadata +112 -30
  43. data/LICENSE +0 -19
  44. data/Readme.rdoc +0 -126
  45. data/lib/country.rb +0 -36
  46. data/lib/support.rb +0 -78
  47. data/test/test_helper.rb +0 -15
@@ -1,5 +1,5 @@
1
1
  # An object representing a phone number.
2
- #
2
+ #
3
3
  # The phone number is recorded in 3 separate parts:
4
4
  # * country_code - e.g. '385', '386'
5
5
  # * area_code - e.g. '91', '47'
@@ -9,34 +9,55 @@
9
9
  # Phone.default_country_code
10
10
  # Phone.default_area_code
11
11
  #
12
- require File.join(File.dirname(__FILE__), 'support') unless defined? ActiveSupport
13
- require File.join(File.dirname(__FILE__), 'country')
14
- require File.join(File.dirname(__FILE__), 'errors')
12
+ require "phone/country"
13
+ require "phone/errors"
15
14
 
16
15
  module Phoner
17
16
  class Phone
18
- NUMBER = '([0-9]{1,8})$'
19
- DEFAULT_AREA_CODE = '[0-9][0-9][0-9]' # any 3 digits
17
+ module ClassAttributes
18
+ attr_accessor :default_country_code
19
+ attr_accessor :default_area_code
20
+ attr_accessor :named_formats
21
+ attr_accessor :n1_length
22
+ end
23
+ extend ClassAttributes
20
24
 
21
- attr_accessor :country_code, :area_code, :number, :extension
25
+ module ClassAttributeAccessors
26
+ def default_country_code
27
+ self.class.default_country_code
28
+ end
22
29
 
23
- cattr_accessor :default_country_code
24
- cattr_accessor :default_area_code
25
- cattr_accessor :named_formats
30
+ def default_area_code
31
+ self.class.default_area_code
32
+ end
26
33
 
27
- # length of first number part (using multi number format)
28
- cattr_accessor :n1_length
29
- # default length of first number part
30
- @@n1_length = 3
34
+ def named_formats
35
+ self.class.named_formats
36
+ end
37
+
38
+ def n1_length
39
+ self.class.n1_length
40
+ end
41
+ end
42
+ include ClassAttributeAccessors
43
+
44
+ NUMBER = '([0-9]{1,8})$'
45
+ DEFAULT_AREA_CODE = '[0-9][0-9][0-9]' # any 3 digits
31
46
 
32
- @@named_formats = {
47
+ attr_accessor :country_code, :area_code, :number, :extension
48
+
49
+ self.named_formats = {
33
50
  :default => "+%c%a%n",
34
51
  :default_with_extension => "+%c%a%nx%x",
35
52
  :europe => '+%c (0) %a %f %l',
36
53
  :us => "(%a) %f-%l"
37
54
  }
38
55
 
39
- def initialize(*hash_or_args)
56
+ # length of first number part (using multi number format)
57
+ # default length of first number part
58
+ self.n1_length = 3
59
+
60
+ def initialize(*hash_or_args)
40
61
  if hash_or_args.first.is_a?(Hash)
41
62
  hash_or_args = hash_or_args.first
42
63
  keys = {:number => :number, :area_code => :area_code, :country_code => :country_code, :extension => :extension}
@@ -44,41 +65,39 @@ module Phoner
44
65
  keys = {:number => 0, :area_code => 1, :country_code => 2, :extension => 3}
45
66
  end
46
67
 
47
- self.number = hash_or_args[ keys[:number] ]
48
- self.area_code = hash_or_args[ keys[:area_code] ] || self.default_area_code
49
- self.country_code = hash_or_args[ keys[:country_code] ] || self.default_country_code
68
+ self.number = hash_or_args[ keys[:number] ].to_s.strip
69
+ self.area_code = (hash_or_args[ keys[:area_code] ] || self.default_area_code).to_s.strip
70
+ self.country_code = (hash_or_args[ keys[:country_code] ] || self.default_country_code).to_s.strip
50
71
  self.extension = hash_or_args[ keys[:extension] ]
51
72
 
52
- raise NumberError, "Must enter number" if self.number.blank?
53
- raise AreaCodeError, "Must enter area code or set default area code" if self.area_code.blank?
54
- raise CountryCodeError, "Must enter country code or set default country code" if self.country_code.blank?
73
+ raise BlankNumberError, "Must enter number" if self.number.empty?
74
+ raise AreaCodeError, "Must enter area code or set default area code" if self.area_code.empty?
75
+ raise CountryCodeError, "Must enter country code or set default country code" if self.country_code.empty?
55
76
  end
56
77
 
57
78
  # create a new phone number by parsing a string
58
79
  # the format of the string is detect automatically (from FORMATS)
59
- def self.parse(string, options={})
60
- if string.present?
61
- Country.load
62
- extension = extract_extension(string)
63
- string = normalize(string)
80
+ def self.parse(string, options={})
81
+ return if string.to_s.empty?
64
82
 
65
- options[:country_code] ||= self.default_country_code
66
- options[:area_code] ||= self.default_area_code
83
+ Country.load
84
+ extension = extract_extension(string)
85
+ string = normalize(string)
67
86
 
68
- parts = split_to_parts(string, options)
87
+ options[:country_code] ||= self.default_country_code
88
+ options[:area_code] ||= self.default_area_code
69
89
 
70
- pn = Phone.new(parts) if parts
71
- if pn.present? and extension.present?
72
- pn.extension = extension
73
- end
74
- return pn
75
- end
90
+ parts = split_to_parts(string, options)
91
+
92
+ pn = Phone.new(parts.merge(:extension => extension)) if parts
93
+ return pn
76
94
  end
77
95
 
96
+
78
97
  # is this string a valid phone number?
79
- def self.valid?(string)
98
+ def self.valid?(string, options = {})
80
99
  begin
81
- parse(string).present?
100
+ !parse(string, options).nil?
82
101
  # if we encountered exceptions (missing country code, missing area code etc)
83
102
  rescue PhoneError
84
103
  return false
@@ -90,7 +109,7 @@ module Phoner
90
109
  country = detect_country(string)
91
110
 
92
111
  if country
93
- options[:country_code] = country.country_code
112
+ options[:country_code] = country.country_code
94
113
  string = string.gsub(country.country_code_regexp, '0')
95
114
  else
96
115
  if options[:country_code]
@@ -107,17 +126,20 @@ module Phoner
107
126
  end
108
127
 
109
128
  format = detect_format(string, country)
129
+ return nil if format.nil?
110
130
 
111
- return nil if format.nil?
131
+ # Override the format IF overriding options are not present
132
+ format = :short if options[:area_code].nil?
112
133
 
113
134
  parts = string.match formats(country)[format]
135
+ return nil if parts.nil?
114
136
 
115
- case format
137
+ case format
116
138
  when :short
117
- {:number => parts[2], :area_code => parts[1], :country_code => options[:country_code]}
139
+ {:number => parts[2], :area_code => parts[1], :country_code => options[:country_code]}
118
140
  when :really_short
119
- {:number => parts[1], :area_code => options[:area_code], :country_code => options[:country_code]}
120
- end
141
+ {:number => parts[1], :area_code => options[:area_code], :country_code => options[:country_code]}
142
+ end
121
143
  end
122
144
 
123
145
  # detect country from the string entered
@@ -129,7 +151,7 @@ module Phoner
129
151
  detected_country = country
130
152
  end
131
153
  end
132
- detected_country
154
+ detected_country
133
155
  end
134
156
 
135
157
  def self.formats(country)
@@ -139,7 +161,7 @@ module Phoner
139
161
  :short => Regexp.new('^0?(' + area_code_regexp + ')' + NUMBER),
140
162
  # 451588
141
163
  :really_short => Regexp.new('^' + NUMBER)
142
- }
164
+ }
143
165
  end
144
166
 
145
167
  # detect format (from FORMATS) of input string
@@ -201,8 +223,8 @@ module Phoner
201
223
  end
202
224
 
203
225
  # Formats the phone number.
204
- #
205
- # if the method argument is a String, it is used as a format string, with the following fields being interpolated:
226
+ #
227
+ # if the method argument is a String, it is used as a format string, with the following fields being interpolated:
206
228
  #
207
229
  # * %c - country_code (385)
208
230
  # * %a - area_code (91)
@@ -214,7 +236,7 @@ module Phoner
214
236
  #
215
237
  # if the method argument is a Symbol, it is used as a lookup key for a format String in Phone.named_formats
216
238
  # pn.format(:europe)
217
- def format(fmt)
239
+ def format(fmt)
218
240
  if fmt.is_a?(Symbol)
219
241
  raise "The format #{fmt} doesn't exist'" unless named_formats.has_key?(fmt)
220
242
  format_number named_formats[fmt]
@@ -237,24 +259,24 @@ module Phoner
237
259
  def has_default_area_code?
238
260
  area_code == self.class.default_area_code
239
261
  end
240
-
262
+
241
263
  # comparison of 2 phone objects
242
264
  def ==(other)
243
265
  methods = [:country_code, :area_code, :number, :extension]
244
266
  methods.all? { |method| other.respond_to?(method) && send(method) == other.send(method) }
245
- end
267
+ end
246
268
 
247
269
  private
248
270
 
249
271
  def format_number(fmt)
250
272
  result = fmt.gsub("%c", country_code || "").
251
273
  gsub("%a", area_code || "").
252
- gsub("%A", area_code_long || "").
274
+ gsub("%A", area_code_long || "").
253
275
  gsub("%n", number || "").
254
276
  gsub("%f", number1 || "").
255
277
  gsub("%l", number2 || "").
256
278
  gsub("%x", extension || "")
257
279
  return result
258
280
  end
259
- end
281
+ end
260
282
  end
@@ -0,0 +1,45 @@
1
+ require 'yaml'
2
+
3
+ module Phoner
4
+ class Country < Struct.new(:name, :country_code, :char_2_code, :char_3_code, :area_code)
5
+ module All
6
+ attr_accessor :all
7
+ end
8
+ extend All
9
+
10
+ def all
11
+ self.class.all
12
+ end
13
+
14
+ def self.load
15
+ return self.all if !self.all.nil? && !self.all.empty?
16
+
17
+ data_file = File.expand_path(File.join('..','..','data', 'phone', 'countries.yml'), File.dirname(__FILE__))
18
+
19
+ self.all = {}
20
+ YAML.load(File.read(data_file)).each_pair do |key, c|
21
+ self.all[key] = Country.new(c[:name], c[:country_code], c[:char_2_code], c[:char_3_code], c[:area_code])
22
+ end
23
+ self.all
24
+ end
25
+
26
+ def to_s
27
+ name
28
+ end
29
+
30
+ def self.find_by_country_code(code)
31
+ self.all[code]
32
+ end
33
+
34
+ def self.find_by_country_isocode(isocode)
35
+ if country = self.all.detect{|c|c[1].char_3_code.downcase == isocode}
36
+ country[1]
37
+ end
38
+ end
39
+
40
+ def country_code_regexp
41
+ @country_code_regexp ||= Regexp.new("^[+]#{country_code}")
42
+ end
43
+ end
44
+
45
+ end
File without changes
@@ -0,0 +1,4 @@
1
+ module Phone
2
+ # phone version
3
+ VERSION = "1.3.0.beta0"
4
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path("../lib/phone/version", __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "phone"
7
+ gem.version = Phone::VERSION
8
+ gem.summary = %q{Phone number parsing, validation and formatting}
9
+ gem.description = %q{Phone number parsing, validation and formatting in Ruby}
10
+ gem.license = "MIT"
11
+ gem.authors = ["Tomislav Carr", "Todd Eichel", "Don Morrison"]
12
+ gem.email = ["tomislav@infinum.hr", "todd@toddeichel.com", "don@elskwid.net"]
13
+ gem.homepage = "https://github.com/carr/phone#readme"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "bundler", "~> 1.2"
21
+ gem.add_development_dependency "minitest", "~> 5.0"
22
+ gem.add_development_dependency "rake", "~> 10.0"
23
+ gem.add_development_dependency "rubygems-tasks", "~> 0.2"
24
+ gem.add_development_dependency "yard", "~> 0.8"
25
+ end
@@ -1,49 +1,49 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
1
+ require "helper"
2
2
 
3
3
  # 0x 5551 reserved for fictitious use. (not including x=3)
4
4
  # 0x 7010 reserved for fictitious use.
5
5
 
6
6
  ## Australia
7
- class AUTest < Test::Unit::TestCase
8
-
7
+ class AUTest < Minitest::Test
8
+
9
9
  # 00 Emergency and International access
10
10
  # 01 Alternate phone services
11
11
  # 014 Satellite phone services
12
12
  # 016 Paging [+3D or +6D]
13
13
  # 018 Analogue (AMPS) mobile phone - few numbers still in use [+6D]
14
14
  # 0198 Data networks [+2D or +6D] - e.g. 0198 379 000 is the Dial-Up POP number for iiNet
15
-
15
+
16
16
  # 02 Central East region (NSW, ACT)
17
17
  def test_central_east
18
18
  parse_test('+61 2 5551 1234', '61', '2', '55511234')
19
19
  end
20
-
20
+
21
21
  # 03 South-east region (VIC, TAS)
22
22
  def test_south_east
23
23
  parse_test('+61 3 5551 1234', '61', '3', '55511234')
24
24
  end
25
-
25
+
26
26
  # 04 Mobile services (Digital - GSM, CDMA, 3G)
27
27
  def test_mobile
28
28
  parse_test('+61 4 5551 1234', '61', '4', '55511234')
29
29
  end
30
-
30
+
31
31
  # 05 Universal/Personal numberings (uncommon)
32
32
  def test_personal
33
33
  parse_test('+61 5 5551 1234', '61', '5', '55511234')
34
34
  end
35
-
35
+
36
36
  # 07 North-east region (QLD)
37
37
  def test_north_east
38
38
  parse_test('+61 7 5551 1234', '61', '7', '55511234')
39
39
  end
40
-
40
+
41
41
  # 08 Central and West region (SA, NT, WA)
42
42
  def test_central
43
43
  parse_test('+61 8 5551 1234', '61', '8', '55511234')
44
44
  end
45
-
45
+
46
46
  # (Geographical region boundaries do not exactly follow state borders.)
47
47
  # 1 Non-geographic numbers
48
-
48
+
49
49
  end
@@ -1,10 +1,10 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
1
+ require "helper"
2
2
 
3
3
  ## Bosnia and Herzegovina
4
- class BATest < Test::Unit::TestCase
5
-
4
+ class BATest < Minitest::Test
5
+
6
6
  def test_local
7
7
  parse_test('+387 33 25 02 33', '387', '33', '250233')
8
8
  end
9
-
9
+
10
10
  end
@@ -1,8 +1,9 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
1
+ # -*- coding: utf-8 -*-
2
+ require "helper"
2
3
 
3
4
  ## Belgium
4
- class BETest < Test::Unit::TestCase
5
-
5
+ class BETest < Minitest::Test
6
+
6
7
  ## single digit
7
8
  # 02: Brussels (Bruxelles/Brussel)
8
9
  def test_brussels
@@ -20,7 +21,7 @@ class BETest < Test::Unit::TestCase
20
21
  def test_gent
21
22
  parse_test('+32 9 1234567', '32', '9', '1234567')
22
23
  end
23
-
24
+
24
25
  ## two digit
25
26
  # 010: Wavre (Waver)
26
27
  def test_wavre
@@ -45,7 +46,7 @@ class BETest < Test::Unit::TestCase
45
46
  def test_waremme
46
47
  parse_test('+32 19 123456', '32', '19', '123456')
47
48
  end
48
-
49
+
49
50
  # 050: Brugge (Bruges), Zeebrugge
50
51
  def test_brugge
51
52
  parse_test('+32 50 123456', '32', '50', '123456')
@@ -65,7 +66,7 @@ class BETest < Test::Unit::TestCase
65
66
  def test_oostende
66
67
  parse_test('+32 59 123456', '32', '59', '123456')
67
68
  end
68
-
69
+
69
70
  # 060: Chimay
70
71
  def test_chimay
71
72
  parse_test('+32 60 123456', '32', '60', '123456')
@@ -80,13 +81,13 @@ class BETest < Test::Unit::TestCase
80
81
  end
81
82
  # 068: Ath (Aat)
82
83
  # 069: Tournai (Doornik)
83
-
84
+
84
85
  # 070: Specialty Numbers (i.e. bus information or bank information)
85
86
  def test_specialty
86
87
  parse_test('+32 70 123456', '32', '70', '123456')
87
88
  end
88
89
  # 071: Charleroi
89
-
90
+
90
91
  # 081: Namur (Namen)
91
92
  def test_namur
92
93
  parse_test('+32 81 123456', '32', '81', '123456')
@@ -103,14 +104,14 @@ class BETest < Test::Unit::TestCase
103
104
  def test_toll_free
104
105
  parse_test('+32 800 12345', '32', '800', '12345')
105
106
  end
106
-
107
+
107
108
  # 090x: Premium numbers (0900, 0901, 0902, 0903, 0904, 0905, 0906, 0907, 0908, 0909)
108
109
  def test_premium_900
109
110
  parse_test('+32 900 12345', '32', '900', '12345')
110
111
  end
111
-
112
+
112
113
  def test_premium_901
113
114
  parse_test('+32 901 12345', '32', '901', '12345')
114
115
  end
115
-
116
+
116
117
  end