phone 1.2.3 → 1.3.0.beta0

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.
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