phonie 2.1.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -5
- data/Changelog.md +14 -0
- data/Readme.rdoc +33 -21
- data/lib/phonie.rb +4 -7
- data/lib/phonie/configuration.rb +22 -0
- data/lib/phonie/country.rb +48 -84
- data/lib/phonie/formatter.rb +54 -0
- data/lib/phonie/parser.rb +125 -0
- data/lib/phonie/phone.rb +56 -79
- data/lib/phonie/railties/validator.rb +2 -10
- data/lib/phonie/version.rb +1 -1
- data/phonie.gemspec +1 -2
- data/test/countries/bd_test.rb +1 -1
- data/test/countries/ca_test.rb +3 -3
- data/test/countries/hr_test.rb +7 -8
- data/test/countries/in_test.rb +4 -4
- data/test/countries/pt_test.rb +5 -6
- data/test/countries/us_test.rb +5 -3
- data/test/phone_test.rb +7 -17
- data/test/railties/validator_test.rb +12 -10
- data/test/test_helper.rb +4 -2
- metadata +9 -6
- data/lib/phonie/support.rb +0 -78
data/.travis.yml
CHANGED
data/Changelog.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
3.0.0
|
2
|
+
=====
|
3
|
+
|
4
|
+
* ActiveModel validator no longer modifies phone numbers on validation. Use a before_save to do that!
|
5
|
+
* configuration changes from Phonie::Phone.default_country_code to Phonie.configuration.default_country_code
|
6
|
+
* adds Phone#possible_valid_number? and Phone#is_valid_number?
|
7
|
+
* removes Phone#matches_full_number? Phone#matches_local_number_with_area_code? and Phone#matches_local_number?
|
8
|
+
|
9
|
+
2.1.2
|
10
|
+
=====
|
11
|
+
|
12
|
+
* fixes Mauritius parsing
|
13
|
+
* Support new Ecuador mobile numbers
|
14
|
+
* fix for Russian mobile number detection
|
data/Readme.rdoc
CHANGED
@@ -3,7 +3,6 @@ Parsing, validating and creating phone numbers
|
|
3
3
|
|
4
4
|
== Install
|
5
5
|
You can install the phone library as a gem
|
6
|
-
gem sources -a http://gemcutter.org
|
7
6
|
gem install phonie
|
8
7
|
|
9
8
|
== Initializing
|
@@ -11,7 +10,7 @@ You can initialize a new phone object with the number, area code, country code a
|
|
11
10
|
|
12
11
|
Phonie::Phone.new('5125486', '91', '385')
|
13
12
|
or
|
14
|
-
Phonie::Phone.new(:
|
13
|
+
Phonie::Phone.new(number: '5125486', area_code: '91', country_code: '385', extension: '143')
|
15
14
|
|
16
15
|
== Parsing
|
17
16
|
You can create a new phone object by parsing from a string. Phonie::Phone does it's best to detect the country and area codes:
|
@@ -19,31 +18,29 @@ You can create a new phone object by parsing from a string. Phonie::Phone does i
|
|
19
18
|
Phonie::Phone.parse '00385915125486'
|
20
19
|
|
21
20
|
If the country or area code isn't given in the string, you must set it, otherwise it doesn't work:
|
22
|
-
Phonie::Phone.parse '091/512-5486', :
|
23
|
-
Phonie::Phone.parse '(091) 512 5486', :
|
21
|
+
Phonie::Phone.parse '091/512-5486', country_code: '385'
|
22
|
+
Phonie::Phone.parse '(091) 512 5486', country_code: '385'
|
24
23
|
|
25
24
|
If you feel that it's tedious, set the default country code once (in your config/environment.rb):
|
26
|
-
Phonie
|
25
|
+
Phonie.configuration.default_country_code = '385'
|
27
26
|
Phonie::Phone.parse '091/512-5486'
|
28
27
|
Phonie::Phone.parse '(091) 512 5486'
|
29
28
|
|
30
29
|
Same goes for the area code:
|
31
|
-
Phonie::Phone.parse '451-588', :
|
32
|
-
|
33
|
-
|
34
|
-
Phonie
|
30
|
+
Phonie::Phone.parse '451-588', country_code: '385', area_code: '47'
|
31
|
+
|
32
|
+
Alternatively Phonie can be configured via a block
|
33
|
+
Phonie.configure do |config|
|
34
|
+
config.default_country_code = '385'
|
35
|
+
config.default_area_code = '47'
|
36
|
+
end
|
35
37
|
|
36
38
|
Phonie::Phone.parse '451-588'
|
37
39
|
|
38
40
|
=== Automatic country and area code detection
|
39
|
-
Like it's stated above, Phone does it's best to automatically detect the country and area code while parsing.
|
40
|
-
phone uses data stored in <tt>data/countries.yml</tt>.
|
41
|
-
|
42
|
-
Each country code can have a regular expression named <tt>area_code</tt> that describes how the area code for that
|
43
|
-
particular country looks like.
|
41
|
+
Like it's stated above, Phone does it's best to automatically detect the country and area code while parsing. To do this, phone uses data stored in <tt>data/countries.yml</tt>.
|
44
42
|
|
45
|
-
|
46
|
-
the US) is used.
|
43
|
+
Each country code can have a regular expression named <tt>area_code</tt> that describes how the area code for that particular country looks like.
|
47
44
|
|
48
45
|
== Validating
|
49
46
|
Validating is very relaxed, basically it strips out everything that's not a number or '+' character:
|
@@ -58,7 +55,7 @@ When given a string, it interpolates the string with the following fields:
|
|
58
55
|
* %a - area_code (91)
|
59
56
|
* %A - area_code with leading zero (091)
|
60
57
|
* %n - number (5125486)
|
61
|
-
* %f - first @@n1_length characters of number (configured through Phonie
|
58
|
+
* %f - first @@n1_length characters of number (configured through Phonie.n1_length), default is 3 (512)
|
62
59
|
* %l - last characters of number (5486)
|
63
60
|
* %x - the extension number
|
64
61
|
|
@@ -76,8 +73,25 @@ You can add your own custom named formats like so:
|
|
76
73
|
Phonie::Phone.named_formats[:short] = '%A/%n1-%n2'
|
77
74
|
pn.format(:short) # => 091/512-5486
|
78
75
|
|
76
|
+
== ActiveModel validator
|
77
|
+
|
78
|
+
Phonie includes an ActiveModel validator. If you are using ActiveModel you can validate phone numbers like so:
|
79
|
+
|
80
|
+
class SomeModel
|
81
|
+
include ActiveModel::Validations
|
82
|
+
|
83
|
+
validates :phone_number, phone: true
|
84
|
+
end
|
85
|
+
|
86
|
+
model = SomeModel.new(phone_number: '')
|
87
|
+
model.valid? # false
|
88
|
+
|
89
|
+
model = SomeModel.new(phone_number: '+1 251 123 4567')
|
90
|
+
model.valid? # true
|
91
|
+
|
92
|
+
|
79
93
|
= TODO
|
80
|
-
|
94
|
+
Add definitions for more countries
|
81
95
|
|
82
96
|
Currently tested on:
|
83
97
|
[AE] UAE
|
@@ -176,9 +190,7 @@ More testing is needed to add support for missing countries, and improve support
|
|
176
190
|
The best places to start is to read through the country tests and data/phone_countries.rb
|
177
191
|
|
178
192
|
= Other libraries
|
179
|
-
This is based off a fork of the Phone gem (https://github.com/carr/phone), and was extensively modified for better support of country detection
|
180
|
-
|
181
|
-
Another fork based off this one is available at https://github.com/capitainetrain/phoner It explictly requires ActiveSupport.
|
193
|
+
This is based off a fork of the Phone gem (https://github.com/carr/phone), and was extensively modified for better support of country detection, and supports far more countries.
|
182
194
|
|
183
195
|
= Contributors
|
184
196
|
Tomislav Carr, Don Morrison, Michael Squires, Todd Eichel (Fooala, Inc.), chipiga, Etienne Samson, Luke Randall, Wesley Moxam
|
data/lib/phonie.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
require "phonie/version"
|
2
|
-
require "phonie/
|
2
|
+
require "phonie/configuration"
|
3
|
+
require "phonie/formatter"
|
3
4
|
require "phonie/phone"
|
5
|
+
require "phonie/parser"
|
4
6
|
require "phonie/country"
|
5
|
-
require "phonie/railties/validator"
|
6
|
-
|
7
|
-
module Phonie
|
8
|
-
end
|
9
|
-
|
10
|
-
Phonie::Country.load
|
7
|
+
require "phonie/railties/validator" if defined? ActiveModel
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Phonie
|
4
|
+
class Configuration
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
attr_accessor :data_file_path, :default_area_code, :default_country_code, :n1_length
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@data_file_path = File.join(File.dirname(__FILE__), 'data', 'phone_countries.yml')
|
11
|
+
@n1_length = 3
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.configure(&block)
|
16
|
+
yield configuration
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.configuration
|
20
|
+
Configuration.instance
|
21
|
+
end
|
22
|
+
end
|
data/lib/phonie/country.rb
CHANGED
@@ -1,115 +1,79 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'yaml'
|
3
|
+
|
1
4
|
module Phonie
|
2
|
-
class Country
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
class Country
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :name, :country_code, :char_2_code, :iso_3166_code, :parser
|
9
|
+
|
10
|
+
def initialize(params)
|
11
|
+
@name = params[:name]
|
12
|
+
@country_code = params[:country_code]
|
13
|
+
@char_2_code = params[:char_2_code]
|
14
|
+
@iso_3166_code = params[:iso_3166_code]
|
15
|
+
@parser = Phonie::Parser.new(params)
|
16
|
+
@national_dialing_prefix = params[:national_dialing_prefix]
|
17
|
+
end
|
18
|
+
|
19
|
+
def_delegators :parser, :is_mobile?, :possible_valid_number?,
|
20
|
+
:is_valid_number?, :parse
|
21
|
+
|
22
|
+
def self.all
|
23
|
+
@@all ||= begin
|
24
|
+
YAML.load_file(Phonie.configuration.data_file_path).collect do |country_params|
|
25
|
+
Country.new(country_params)
|
26
|
+
end.select {|country| country.valid? }
|
10
27
|
end
|
11
|
-
all
|
12
28
|
end
|
13
29
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
30
|
+
def self.all_by_phone_code
|
31
|
+
@@all_by_phone_code ||= all.inject(Hash.new){|h, c| (h[c.country_code] ||= []) << c; h }
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.all_by_country_code
|
35
|
+
@@all_by_country_name ||= Hash[*all.map{|c| [c.iso_3166_code.downcase, c] }.flatten]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.all_by_name
|
39
|
+
@@all_by_name ||= Hash[*all.map{|c| [c.name.downcase, c] }.flatten]
|
40
|
+
end
|
18
41
|
|
19
42
|
def self.find_all_by_phone_code(code)
|
20
|
-
|
43
|
+
all_by_phone_code[code] || []
|
21
44
|
end
|
22
45
|
|
23
46
|
def self.find_by_country_code(code)
|
24
|
-
|
47
|
+
all_by_country_code[code.downcase] if code
|
25
48
|
end
|
26
49
|
|
27
50
|
def self.find_by_name(name)
|
28
|
-
|
51
|
+
all_by_name[name.downcase] if name
|
29
52
|
end
|
30
53
|
|
31
|
-
# detect country from the
|
32
|
-
def self.detect(
|
54
|
+
# detect country from the passed phone number
|
55
|
+
def self.detect(phone_number, default_country_code, default_area_code)
|
33
56
|
# use the default_country_code to try for a quick match
|
34
|
-
country = find_all_by_phone_code(default_country_code).find do |
|
35
|
-
|
36
|
-
country.matches_local_number_with_area_code?(string) ||
|
37
|
-
country.matches_local_number?(string, default_area_code)
|
57
|
+
country = find_all_by_phone_code(default_country_code).find do |c|
|
58
|
+
c.possible_valid_number?(phone_number, default_area_code)
|
38
59
|
end
|
39
60
|
|
40
61
|
# then search all for a full match
|
41
|
-
country ||
|
62
|
+
country || all.find {|country| country.is_valid_number?(phone_number) }
|
42
63
|
end
|
43
64
|
|
44
65
|
def to_s
|
45
66
|
name
|
46
67
|
end
|
47
68
|
|
48
|
-
def is_mobile?(number)
|
49
|
-
return true if mobile_format.nil?
|
50
|
-
number =~ mobile_number_regex ? true : false
|
51
|
-
end
|
52
|
-
|
53
|
-
# true if string contains country_code + area_code + local_number
|
54
|
-
def matches_full_number?(string)
|
55
|
-
string =~ full_number_regex && string =~ number_format_regex
|
56
|
-
end
|
57
|
-
|
58
|
-
# true if string contains area_code + local_number
|
59
|
-
def matches_local_number_with_area_code?(string)
|
60
|
-
string =~ area_code_number_regex && string =~ number_format_regex
|
61
|
-
end
|
62
|
-
|
63
|
-
# true if string contains only the local_number, but the default_area_code is valid
|
64
|
-
def matches_local_number?(string, default_area_code)
|
65
|
-
string =~ number_regex && default_area_code =~ area_code_regex
|
66
|
-
end
|
67
|
-
|
68
|
-
def parse(number, default_area_code)
|
69
|
-
if md = number.match(full_number_regex)
|
70
|
-
{:area_code => md[2], :number => md[-1]}
|
71
|
-
elsif md = number.match(area_code_number_regex)
|
72
|
-
{:area_code => md[1], :number => md[-1]}
|
73
|
-
elsif md = number.match(number_regex)
|
74
|
-
{:area_code => default_area_code, :number => md[1]}
|
75
|
-
else
|
76
|
-
{}
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
69
|
def national_dialing_prefix
|
81
|
-
|
82
|
-
if prefix == "None"
|
83
|
-
nil
|
84
|
-
else
|
85
|
-
prefix
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def number_format_regex
|
92
|
-
@number_format_regex ||= Regexp.new("^[+0]?(#{country_code})?(#{number_format})$")
|
93
|
-
end
|
94
|
-
|
95
|
-
def full_number_regex
|
96
|
-
@full_number_regex ||= Regexp.new("^[+]?(#{country_code})(#{area_code})(#{local_number_format})$")
|
97
|
-
end
|
98
|
-
|
99
|
-
def area_code_number_regex
|
100
|
-
@area_code_number_regex ||= Regexp.new("^0?(#{area_code})(#{local_number_format})$")
|
101
|
-
end
|
102
|
-
|
103
|
-
def area_code_regex
|
104
|
-
@area_code_regex ||= Regexp.new("^0?(#{area_code})$")
|
105
|
-
end
|
70
|
+
return nil if @national_dialing_prefix == "None"
|
106
71
|
|
107
|
-
|
108
|
-
@mobile_number_regex ||= Regexp.new("^(#{mobile_format})$")
|
72
|
+
@national_dialing_prefix
|
109
73
|
end
|
110
74
|
|
111
|
-
def
|
112
|
-
|
75
|
+
def valid?
|
76
|
+
!!(name && parser.valid?)
|
113
77
|
end
|
114
78
|
end
|
115
79
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Formats the phone number.
|
2
|
+
#
|
3
|
+
# if the method argument is a String, it is used as a format string, with the following fields being interpolated:
|
4
|
+
#
|
5
|
+
# * %c - country_code (385)
|
6
|
+
# * %a - area_code (91)
|
7
|
+
# * %A - area_code with leading zero (091)
|
8
|
+
# * %n - number (5125486)
|
9
|
+
# * %f - first n1_length characters of number (configured through Phone.n1_length), default is 3 (512)
|
10
|
+
# * %l - last characters of number (5486)
|
11
|
+
# * %x - entire extension
|
12
|
+
#
|
13
|
+
# if the method argument is a Symbol, it is used as a lookup key for a format String in Phone.named_formats
|
14
|
+
# pn.format(:europe)
|
15
|
+
module Phonie
|
16
|
+
class Formatter
|
17
|
+
attr_reader :format, :phone_number
|
18
|
+
|
19
|
+
def initialize(params)
|
20
|
+
@phone_number = params[:phone_number]
|
21
|
+
|
22
|
+
format = params[:format]
|
23
|
+
@format = if format.respond_to?(:gsub)
|
24
|
+
format
|
25
|
+
else
|
26
|
+
self.class.named_formats[format]
|
27
|
+
end
|
28
|
+
|
29
|
+
raise ArgumentError.new("No valid format provided") if @format.nil?
|
30
|
+
raise ArgumentError.new("No valid phone number provided") if @phone_number.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.named_formats
|
34
|
+
{
|
35
|
+
default: "+%c%a%n",
|
36
|
+
default_with_extension: "+%c%a%nx%x",
|
37
|
+
europe: '+%c (0) %a %f %l',
|
38
|
+
us: "(%a) %f-%l"
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
pn = phone_number
|
44
|
+
|
45
|
+
format.gsub("%c", pn.country_code.to_s).
|
46
|
+
gsub("%a", pn.area_code.to_s).
|
47
|
+
gsub("%A", pn.area_code_long.to_s).
|
48
|
+
gsub("%n", pn.number.to_s).
|
49
|
+
gsub("%f", pn.number1.to_s).
|
50
|
+
gsub("%l", pn.number2.to_s).
|
51
|
+
gsub("%x", pn.extension.to_s)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Phonie
|
2
|
+
# Parser is responsible for parsing and verifying phone numbers
|
3
|
+
class Parser
|
4
|
+
attr_reader :area_code, :country_code, :local_number_format, :mobile_format, :number_format
|
5
|
+
|
6
|
+
# Creates a new phone number parser based on a hash of +params+
|
7
|
+
# The +params+ hash requires
|
8
|
+
# * area_code
|
9
|
+
# * country_code - a number representing the International Subscriber Dialling code
|
10
|
+
# * local_number_format - a regex describing a local phone number (minus country and area code)
|
11
|
+
# * number_format - a regex describing a valid phone number
|
12
|
+
# * mobile_format (optional) - a regex describing a mobile phone number
|
13
|
+
def initialize(params)
|
14
|
+
@area_code = params[:area_code]
|
15
|
+
@country_code = params[:country_code]
|
16
|
+
@local_number_format = params[:local_number_format]
|
17
|
+
@mobile_format = params[:mobile_format]
|
18
|
+
@number_format = params[:number_format]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Test if a phone +number+ might be for a mobile phone
|
22
|
+
# Can produce false positives, and some countries have
|
23
|
+
# portable numbers between landlines and mobile phones
|
24
|
+
# in which case this will always be true
|
25
|
+
def is_mobile?(number)
|
26
|
+
return true if mobile_format.nil?
|
27
|
+
number =~ mobile_number_regex ? true : false
|
28
|
+
end
|
29
|
+
|
30
|
+
# Test if +phone_number+ is valid
|
31
|
+
def is_valid_number?(phone_number)
|
32
|
+
matches_full_number?(phone_number)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Parse a pass phone +number+ returning it's area_code & number as a
|
36
|
+
# hash if valid.
|
37
|
+
# Optionally a +default_area_code+ may be passed in order to
|
38
|
+
# allow parsing local number numbers for a known area code
|
39
|
+
def parse(number, default_area_code = nil)
|
40
|
+
parse_full_match(number) ||
|
41
|
+
parse_area_code_match(number) ||
|
42
|
+
parse_with_default(number, default_area_code) ||
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Test if a phone number could possibly be valid by testing
|
47
|
+
# if it's matches a number without a country code, and optionally
|
48
|
+
# testing for a local number against a default_area_code
|
49
|
+
def possible_valid_number?(phone_number, default_area_code = nil)
|
50
|
+
matches_full_number?(phone_number) ||
|
51
|
+
matches_local_number_with_area_code?(phone_number) ||
|
52
|
+
matches_local_number?(phone_number, default_area_code)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Test that a parser is valid and is capable of parsing phone numbers
|
56
|
+
def valid?
|
57
|
+
!!(country_code && area_code && local_number_format && number_format)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# true if string contains country_code + area_code + local_number
|
63
|
+
def matches_full_number?(string)
|
64
|
+
string =~ full_number_regex && string =~ number_format_regex
|
65
|
+
end
|
66
|
+
|
67
|
+
# true if string contains area_code + local_number
|
68
|
+
def matches_local_number_with_area_code?(string)
|
69
|
+
string =~ area_code_number_regex && string =~ number_format_regex
|
70
|
+
end
|
71
|
+
|
72
|
+
# true if string contains only the local_number, but the default_area_code is valid
|
73
|
+
def matches_local_number?(string, default_area_code)
|
74
|
+
string =~ number_regex && default_area_code =~ area_code_regex
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_full_match(number)
|
78
|
+
match = number.match(full_number_regex)
|
79
|
+
return nil unless match
|
80
|
+
|
81
|
+
{ area_code: match[2],
|
82
|
+
number: match[-1] }
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_area_code_match(number)
|
86
|
+
match = number.match(area_code_number_regex)
|
87
|
+
return nil unless match
|
88
|
+
|
89
|
+
{ area_code: match[1],
|
90
|
+
number: match[-1] }
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_with_default(number, default_area_code)
|
94
|
+
match = number.match(number_regex)
|
95
|
+
return nil unless match
|
96
|
+
|
97
|
+
{ area_code: default_area_code,
|
98
|
+
number: match[1] }
|
99
|
+
end
|
100
|
+
|
101
|
+
def number_format_regex
|
102
|
+
@number_format_regex ||= Regexp.new("^[+0]?(#{country_code})?(#{number_format})$")
|
103
|
+
end
|
104
|
+
|
105
|
+
def full_number_regex
|
106
|
+
@full_number_regex ||= Regexp.new("^[+]?(#{country_code})(#{area_code})(#{local_number_format})$")
|
107
|
+
end
|
108
|
+
|
109
|
+
def area_code_number_regex
|
110
|
+
@area_code_number_regex ||= Regexp.new("^0?(#{area_code})(#{local_number_format})$")
|
111
|
+
end
|
112
|
+
|
113
|
+
def area_code_regex
|
114
|
+
@area_code_regex ||= Regexp.new("^0?(#{area_code})$")
|
115
|
+
end
|
116
|
+
|
117
|
+
def mobile_number_regex
|
118
|
+
@mobile_number_regex ||= Regexp.new("^(#{mobile_format})$")
|
119
|
+
end
|
120
|
+
|
121
|
+
def number_regex
|
122
|
+
@number_regex ||= Regexp.new("^(#{local_number_format})$")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/phonie/phone.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_model'
|
2
|
-
|
3
1
|
# An object representing a phone number.
|
4
2
|
#
|
5
3
|
# The phone number is recorded in 3 separate parts:
|
@@ -8,64 +6,40 @@ require 'active_model'
|
|
8
6
|
# * number - e.g. '5125486', '451588'
|
9
7
|
#
|
10
8
|
# All parts are mandatory, but country code and area code can be set for all phone numbers using
|
11
|
-
#
|
12
|
-
#
|
9
|
+
# Phonie.configuration.default_country_code
|
10
|
+
# Phonie.configuration.default_area_code
|
13
11
|
#
|
12
|
+
|
14
13
|
module Phonie
|
15
14
|
class Phone
|
16
15
|
EXTENSION = /[ ]*(ext|ex|x|xt|#|:)+[^0-9]*\(*([-0-9]{1,})\)*#?$/i
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
cattr_accessor :named_formats
|
23
|
-
|
24
|
-
# length of first number part (using multi number format)
|
25
|
-
cattr_accessor :n1_length
|
26
|
-
# default length of first number part
|
27
|
-
@@n1_length = 3
|
28
|
-
|
29
|
-
@@named_formats = {
|
30
|
-
:default => "+%c%a%n",
|
31
|
-
:default_with_extension => "+%c%a%nx%x",
|
32
|
-
:europe => '+%c (0) %a %f %l',
|
33
|
-
:us => "(%a) %f-%l"
|
34
|
-
}
|
35
|
-
|
36
|
-
include ActiveModel::Validations
|
37
|
-
validates :country_code, :presence => true
|
38
|
-
validates :area_code, :presence => true
|
39
|
-
validates :number, :presence => true
|
40
|
-
|
41
|
-
def initialize(*hash_or_args)
|
42
|
-
if hash_or_args.first.is_a?(Hash)
|
43
|
-
hash_or_args = hash_or_args.first
|
44
|
-
keys = {:country => :country, :number => :number, :area_code => :area_code, :country_code => :country_code, :extension => :extension}
|
45
|
-
else
|
46
|
-
keys = {:number => 0, :area_code => 1, :country_code => 2, :extension => 3, :country => 4}
|
47
|
-
end
|
17
|
+
attr_reader :country_code, :area_code, :errors, :number, :extension, :country
|
18
|
+
|
19
|
+
def initialize(*args)
|
20
|
+
params = normalize_args(args)
|
48
21
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
22
|
+
@number = params[:number]
|
23
|
+
@area_code = params[:area_code]
|
24
|
+
@country_code = params[:country_code]
|
25
|
+
@extension = params[:extension]
|
26
|
+
@country = params[:country]
|
27
|
+
@errors = {}
|
54
28
|
end
|
55
29
|
|
56
30
|
def self.parse!(string, options = {})
|
57
|
-
|
58
|
-
raise ArgumentError.new("#{string} is not a valid phone number") unless
|
59
|
-
|
31
|
+
phone_number = parse(string, options)
|
32
|
+
raise ArgumentError.new("#{string} is not a valid phone number") unless phone_number && phone_number.valid?
|
33
|
+
phone_number
|
60
34
|
end
|
61
35
|
|
62
36
|
# create a new phone number by parsing a string
|
63
37
|
# the format of the string is detect automatically (from FORMATS)
|
64
38
|
def self.parse(string, options = {})
|
65
|
-
return
|
39
|
+
return if string.nil?
|
66
40
|
|
67
|
-
options[:country_code] ||=
|
68
|
-
options[:area_code] ||=
|
41
|
+
options[:country_code] ||= Phonie.configuration.default_country_code
|
42
|
+
options[:area_code] ||= Phonie.configuration.default_area_code
|
69
43
|
|
70
44
|
extension = extract_extension(string)
|
71
45
|
normalized = normalize(string)
|
@@ -101,36 +75,17 @@ module Phonie
|
|
101
75
|
|
102
76
|
# first n characters of :number
|
103
77
|
def number1
|
104
|
-
number[0...
|
78
|
+
number[0...Phonie.configuration.n1_length]
|
105
79
|
end
|
106
80
|
|
107
81
|
# everything left from number after the first n characters (see number1)
|
108
82
|
def number2
|
109
|
-
n2_length = number.size -
|
83
|
+
n2_length = number.size - Phonie.configuration.n1_length
|
110
84
|
number[-n2_length, n2_length]
|
111
85
|
end
|
112
86
|
|
113
|
-
# Formats the phone number.
|
114
|
-
#
|
115
|
-
# if the method argument is a String, it is used as a format string, with the following fields being interpolated:
|
116
|
-
#
|
117
|
-
# * %c - country_code (385)
|
118
|
-
# * %a - area_code (91)
|
119
|
-
# * %A - area_code with leading zero (091)
|
120
|
-
# * %n - number (5125486)
|
121
|
-
# * %f - first @@n1_length characters of number (configured through Phone.n1_length), default is 3 (512)
|
122
|
-
# * %l - last characters of number (5486)
|
123
|
-
# * %x - entire extension
|
124
|
-
#
|
125
|
-
# if the method argument is a Symbol, it is used as a lookup key for a format String in Phone.named_formats
|
126
|
-
# pn.format(:europe)
|
127
87
|
def format(fmt)
|
128
|
-
|
129
|
-
raise ArgumentError.new("The format #{fmt} doesn't exist") unless named_formats.has_key?(fmt)
|
130
|
-
format_number named_formats[fmt]
|
131
|
-
else
|
132
|
-
format_number(fmt)
|
133
|
-
end
|
88
|
+
Formatter.new(format: fmt, phone_number: self).to_s
|
134
89
|
end
|
135
90
|
|
136
91
|
# the default format is "+%c%a%n"
|
@@ -140,12 +95,17 @@ module Phonie
|
|
140
95
|
|
141
96
|
# does this number belong to the default country code?
|
142
97
|
def has_default_country_code?
|
143
|
-
country_code ==
|
98
|
+
country_code == Phonie.configuration.default_country_code
|
144
99
|
end
|
145
100
|
|
146
101
|
# does this number belong to the default area code?
|
147
102
|
def has_default_area_code?
|
148
|
-
area_code ==
|
103
|
+
area_code == Phonie.configuration.default_area_code
|
104
|
+
end
|
105
|
+
|
106
|
+
def valid?
|
107
|
+
validate
|
108
|
+
errors.empty?
|
149
109
|
end
|
150
110
|
|
151
111
|
# comparison of 2 phone objects
|
@@ -156,6 +116,33 @@ module Phonie
|
|
156
116
|
|
157
117
|
private
|
158
118
|
|
119
|
+
def defaults
|
120
|
+
{ area_code: Phonie.configuration.default_area_code,
|
121
|
+
country_code: Phonie.configuration.default_country_code }
|
122
|
+
end
|
123
|
+
|
124
|
+
def normalize_args(args)
|
125
|
+
hash = if args.first.respond_to?(:key?)
|
126
|
+
args.first
|
127
|
+
else
|
128
|
+
{}.tap do |h|
|
129
|
+
h[:number] = args[0] if args.length > 0
|
130
|
+
h[:area_code] = args[1] if args.length > 1
|
131
|
+
h[:country_code] = args[2] if args.length > 2
|
132
|
+
h[:extension] = args[3] if args.length > 3
|
133
|
+
h[:country] = args[4] if args.length > 4
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
defaults.merge(hash)
|
138
|
+
end
|
139
|
+
|
140
|
+
def validate
|
141
|
+
[:country_code, :area_code, :number].each do |field|
|
142
|
+
errors[field] = ["can't be blank"] if send(field).to_s == ''
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
159
146
|
# split string into hash with keys :country_code, :area_code and :number
|
160
147
|
def self.split_to_parts(string, options = {})
|
161
148
|
country = Country.detect(string, options[:country_code], options[:area_code])
|
@@ -172,15 +159,5 @@ module Phonie
|
|
172
159
|
return unless string && string.match(EXTENSION)
|
173
160
|
Regexp.last_match[2]
|
174
161
|
end
|
175
|
-
|
176
|
-
def format_number(fmt)
|
177
|
-
fmt.gsub("%c", country_code || "").
|
178
|
-
gsub("%a", area_code || "").
|
179
|
-
gsub("%A", area_code_long || "").
|
180
|
-
gsub("%n", number || "").
|
181
|
-
gsub("%f", number1 || "").
|
182
|
-
gsub("%l", number2 || "").
|
183
|
-
gsub("%x", extension || "")
|
184
|
-
end
|
185
162
|
end
|
186
163
|
end
|
@@ -2,14 +2,6 @@ I18n.load_path += Dir.glob( File.expand_path('../locales/*.{rb,yml}', __FILE__)
|
|
2
2
|
|
3
3
|
class PhoneValidator < ActiveModel::EachValidator
|
4
4
|
def validate_each(object, attribute, value)
|
5
|
-
|
6
|
-
phone = Phonie::Phone.parse(value)
|
7
|
-
|
8
|
-
if phone.nil?
|
9
|
-
object.errors.add(attribute, :invalid_phone_number)
|
10
|
-
else
|
11
|
-
formated = phone.format( phone.extension ? :default_with_extension : :default)
|
12
|
-
object.send("#{attribute}=", formated) if object.respond_to?("#{attribute}=")
|
13
|
-
end
|
5
|
+
object.errors.add(attribute, :invalid_phone_number) unless Phonie::Phone.valid?(value)
|
14
6
|
end
|
15
|
-
end
|
7
|
+
end
|
data/lib/phonie/version.rb
CHANGED
data/phonie.gemspec
CHANGED
@@ -16,8 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
|
19
|
-
s.
|
20
|
-
|
19
|
+
s.add_development_dependency 'activemodel'
|
21
20
|
s.add_development_dependency 'rake'
|
22
21
|
s.add_development_dependency 'nokogiri'
|
23
22
|
end
|
data/test/countries/bd_test.rb
CHANGED
@@ -9,7 +9,7 @@ class BDTest < Phonie::TestCase
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_with_default_country
|
12
|
-
Phonie
|
12
|
+
Phonie.configuration.default_country_code = '880'
|
13
13
|
parse_test('1715379982', '880', '171', '5379982', 'Bangladesh', true)
|
14
14
|
parse_test('1191573647', '880', '119', '1573647', 'Bangladesh', true)
|
15
15
|
parse_test('21915736', '880', '2', '1915736', 'Bangladesh', false)
|
data/test/countries/ca_test.rb
CHANGED
@@ -8,13 +8,13 @@ class CATest < Phonie::TestCase
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_long_with_default_country_code
|
11
|
-
Phonie
|
11
|
+
Phonie.configuration.default_country_code = '1'
|
12
12
|
parse_test('9059735100', '1', '905', '9735100', 'Canada')
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_short_with_default_country_code_and_area_code
|
16
|
-
Phonie
|
17
|
-
Phonie
|
16
|
+
Phonie.configuration.default_country_code = '1'
|
17
|
+
Phonie.configuration.default_area_code = '416'
|
18
18
|
parse_test('9735100', '1', '416', '9735100', 'Canada')
|
19
19
|
end
|
20
20
|
end
|
data/test/countries/hr_test.rb
CHANGED
@@ -29,43 +29,42 @@ class HRTest < Phonie::TestCase
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_short_without_special_characters_with_country
|
32
|
-
Phonie
|
32
|
+
Phonie.configuration.default_country_code = '385'
|
33
33
|
parse_test('044885047', '385', '44', '885047')
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_zagreb_short_without_special_characters_with_country
|
37
|
-
Phonie
|
37
|
+
Phonie.configuration.default_country_code = '385'
|
38
38
|
parse_test('013668734', '385', '1', '3668734')
|
39
39
|
end
|
40
40
|
|
41
41
|
def test_long_with_zero_in_brackets
|
42
|
-
Phonie
|
42
|
+
Phonie.configuration.default_country_code = nil
|
43
43
|
parse_test('+385 (0)1 366 8111', '385', '1', '3668111')
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_has_default_country_code
|
47
|
-
Phonie
|
47
|
+
Phonie.configuration.default_country_code = '385'
|
48
48
|
|
49
49
|
assert_equal Phonie::Phone.parse('+38547451588').has_default_country_code?, true
|
50
50
|
assert_equal Phonie::Phone.parse('+16472228242').has_default_country_code?, false
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_has_default_area_code
|
54
|
-
Phonie
|
55
|
-
Phonie
|
54
|
+
Phonie.configuration.default_country_code = '385'
|
55
|
+
Phonie.configuration.default_area_code = '47'
|
56
56
|
|
57
57
|
assert_equal Phonie::Phone.parse('047/451-588').has_default_area_code?, true
|
58
58
|
assert_equal Phonie::Phone.parse('032/336-1456').has_default_area_code?, false
|
59
59
|
end
|
60
60
|
|
61
61
|
def test_validates
|
62
|
-
Phonie::Phone.default_country_code = nil
|
63
62
|
assert_equal Phonie::Phone.valid?('00385915125486'), true
|
64
63
|
assert_equal Phonie::Phone.valid?('+385915125486'), true
|
65
64
|
assert_equal Phonie::Phone.valid?('+385 (91) 512 5486'), true
|
66
65
|
assert_equal Phonie::Phone.valid?('+38547451588'), true
|
67
66
|
|
68
|
-
Phonie
|
67
|
+
Phonie.configuration.default_country_code = '385'
|
69
68
|
assert_equal Phonie::Phone.valid?('0915125486'), true
|
70
69
|
assert_equal Phonie::Phone.valid?('091/512-5486'), true
|
71
70
|
assert_equal Phonie::Phone.valid?('091/512-5486'), true
|
data/test/countries/in_test.rb
CHANGED
@@ -14,18 +14,18 @@ class INTest < Phonie::TestCase
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_long_with_default_country_code
|
17
|
-
Phonie
|
17
|
+
Phonie.configuration.default_country_code = '91'
|
18
18
|
parse_test('9124459000', '91', '9124', '459000')
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_short_with_default_country_code_and_area_code
|
22
|
-
Phonie
|
23
|
-
Phonie
|
22
|
+
Phonie.configuration.default_country_code = '91'
|
23
|
+
Phonie.configuration.default_area_code = '9124'
|
24
24
|
parse_test('4529000', '91', '9124', '4529000')
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_lengths
|
28
|
-
Phonie
|
28
|
+
Phonie.configuration.default_country_code = '91'
|
29
29
|
|
30
30
|
phone = Phonie::Phone.parse("919812344")
|
31
31
|
assert_nil phone
|
data/test/countries/pt_test.rb
CHANGED
@@ -51,7 +51,7 @@ class PTTest < Phonie::TestCase
|
|
51
51
|
|
52
52
|
# 707-708: Premium Numbers
|
53
53
|
def test_707
|
54
|
-
Phonie
|
54
|
+
Phonie.configuration.default_country_code = '351'
|
55
55
|
parse_test('707 123 456', '351', '707', '123456')
|
56
56
|
end
|
57
57
|
|
@@ -59,17 +59,17 @@ class PTTest < Phonie::TestCase
|
|
59
59
|
|
60
60
|
# 800: Numero verde ("Green Number")
|
61
61
|
def test_800
|
62
|
-
Phonie
|
62
|
+
Phonie.configuration.default_country_code = '351'
|
63
63
|
parse_test('800 123 456', '351', '800', '123456')
|
64
64
|
end
|
65
65
|
# 808: Numero azul ("Blue Number")
|
66
66
|
def test_808
|
67
|
-
Phonie
|
67
|
+
Phonie.configuration.default_country_code = '351'
|
68
68
|
parse_test('808 123 456', '351', '808', '123456')
|
69
69
|
end
|
70
70
|
# 809: Custo partilhado ("Shared cost")
|
71
71
|
def test_809
|
72
|
-
Phonie
|
72
|
+
Phonie.configuration.default_country_code = '351'
|
73
73
|
parse_test('809 123 456', '351', '809', '123456', 'Portugal', false)
|
74
74
|
end
|
75
75
|
|
@@ -108,14 +108,13 @@ class PTTest < Phonie::TestCase
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def test_validates
|
111
|
-
Phonie::Phone.default_country_code = nil
|
112
111
|
assert_equal Phonie::Phone.valid?('00351211234567'), true
|
113
112
|
assert_equal Phonie::Phone.valid?('00351911234567'), true
|
114
113
|
assert_equal Phonie::Phone.valid?('+351931234567'), true
|
115
114
|
assert_equal Phonie::Phone.valid?('+351 (911) 123 456'), true
|
116
115
|
assert_equal Phonie::Phone.valid?('+351921123456'), true
|
117
116
|
|
118
|
-
Phonie
|
117
|
+
Phonie.configuration.default_country_code = '351'
|
119
118
|
assert_equal Phonie::Phone.valid?('(931) 234-567'), true
|
120
119
|
assert_equal Phonie::Phone.valid?('(211) 234 567'), true
|
121
120
|
assert_equal Phonie::Phone.valid?('232-123-456'), true
|
data/test/countries/us_test.rb
CHANGED
@@ -12,13 +12,15 @@ class USTest < Phonie::TestCase
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_long_with_default_country_code
|
15
|
-
Phonie
|
15
|
+
Phonie.configuration.default_country_code = '1'
|
16
16
|
parse_test('2069735100', '1', '206', '9735100', 'United States')
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_short_with_default_country_code_and_area_code
|
20
|
-
Phonie
|
21
|
-
|
20
|
+
Phonie.configure do |config|
|
21
|
+
config.default_country_code = '1'
|
22
|
+
config.default_area_code = '206'
|
23
|
+
end
|
22
24
|
parse_test('9735100', '1', '206', '9735100', 'United States')
|
23
25
|
end
|
24
26
|
end
|
data/test/phone_test.rb
CHANGED
@@ -7,16 +7,13 @@ class PhoneTest < Phonie::TestCase
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def test_number_without_country_code_initialize
|
10
|
-
Phonie::Phone.default_country_code = nil
|
11
|
-
|
12
10
|
pn = Phonie::Phone.new '5125486', '91'
|
13
11
|
assert !pn.valid?
|
14
12
|
assert_equal ["can't be blank"], pn.errors[:country_code]
|
15
13
|
end
|
16
14
|
|
17
15
|
def test_number_without_area_code_initialize
|
18
|
-
Phonie
|
19
|
-
Phonie::Phone.default_area_code = nil
|
16
|
+
Phonie.configuration.default_country_code = '1'
|
20
17
|
|
21
18
|
pn = Phonie::Phone.new '451588'
|
22
19
|
assert !pn.valid?
|
@@ -24,8 +21,8 @@ class PhoneTest < Phonie::TestCase
|
|
24
21
|
end
|
25
22
|
|
26
23
|
def test_number_with_default_area_code_initialize
|
27
|
-
Phonie
|
28
|
-
Phonie
|
24
|
+
Phonie.configuration.default_country_code = '385'
|
25
|
+
Phonie.configuration.default_area_code = '47'
|
29
26
|
|
30
27
|
pn = Phonie::Phone.new '451588'
|
31
28
|
assert pn.number == '451588'
|
@@ -34,7 +31,7 @@ class PhoneTest < Phonie::TestCase
|
|
34
31
|
end
|
35
32
|
|
36
33
|
def test_number_with_default_country_code_initialize
|
37
|
-
Phonie
|
34
|
+
Phonie.configuration.default_country_code = '386'
|
38
35
|
|
39
36
|
pn = Phonie::Phone.new '5125486', '91'
|
40
37
|
assert pn.number == '5125486'
|
@@ -43,7 +40,7 @@ class PhoneTest < Phonie::TestCase
|
|
43
40
|
end
|
44
41
|
|
45
42
|
def test_number_with_country_code_initialize
|
46
|
-
Phonie
|
43
|
+
Phonie.configuration.default_country_code = '387'
|
47
44
|
|
48
45
|
pn = Phonie::Phone.new '5125486', '91', '385'
|
49
46
|
assert pn.number == '5125486'
|
@@ -57,8 +54,6 @@ class PhoneTest < Phonie::TestCase
|
|
57
54
|
end
|
58
55
|
|
59
56
|
def test_parse_short_without_special_characters_without_country
|
60
|
-
Phonie::Phone.default_country_code = nil
|
61
|
-
|
62
57
|
assert_nil Phonie::Phone.parse "0915125486"
|
63
58
|
|
64
59
|
assert_raise ArgumentError do
|
@@ -67,8 +62,6 @@ class PhoneTest < Phonie::TestCase
|
|
67
62
|
end
|
68
63
|
|
69
64
|
def test_parse_short_with_special_characters_without_country
|
70
|
-
Phonie::Phone.default_country_code = nil
|
71
|
-
|
72
65
|
assert_nil Phonie::Phone.parse "091/512-5486"
|
73
66
|
|
74
67
|
assert_raise ArgumentError do
|
@@ -77,31 +70,28 @@ class PhoneTest < Phonie::TestCase
|
|
77
70
|
end
|
78
71
|
|
79
72
|
def test_to_s
|
80
|
-
Phonie::Phone.default_country_code = nil
|
81
73
|
pn = Phonie::Phone.new '5125486', '91', '385'
|
82
74
|
assert pn.to_s == '+385915125486'
|
83
75
|
end
|
84
76
|
|
85
77
|
def test_to_s_without_country_code
|
86
|
-
Phonie
|
78
|
+
Phonie.configuration.default_country_code = '385'
|
87
79
|
pn = Phonie::Phone.new '5125486', '91'
|
88
80
|
assert pn.format("0%a%n") == '0915125486'
|
89
81
|
end
|
90
82
|
|
91
83
|
def test_format_special_with_country_code
|
92
|
-
Phonie::Phone.default_country_code = nil
|
93
84
|
pn = Phonie::Phone.new '5125486', '91', '385'
|
94
85
|
assert pn.format("+ %c (%a) %n") == '+ 385 (91) 5125486'
|
95
86
|
end
|
96
87
|
|
97
88
|
def test_format_special_without_country_code
|
98
|
-
Phonie
|
89
|
+
Phonie.configuration.default_country_code = '385'
|
99
90
|
pn = Phonie::Phone.new '5125486', '91'
|
100
91
|
assert_equal '091/512-5486', pn.format("%A/%f-%l")
|
101
92
|
end
|
102
93
|
|
103
94
|
def test_format_with_symbol_specifier
|
104
|
-
Phonie::Phone.default_country_code = nil
|
105
95
|
pn = Phonie::Phone.new '5125486', '91', '385'
|
106
96
|
assert_equal '+385 (0) 91 512 5486', pn.format(:europe)
|
107
97
|
end
|
@@ -1,37 +1,39 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
-
|
3
2
|
require 'active_model'
|
4
3
|
require 'phonie/railties/validator'
|
5
|
-
|
4
|
+
|
5
|
+
class SomeModel < Struct.new(:phone_number)
|
6
6
|
include ActiveModel::Validations
|
7
|
-
validates :
|
7
|
+
validates :phone_number, phone: true
|
8
8
|
end
|
9
9
|
|
10
|
+
class SomeOtherModel < Struct.new(:phone_number)
|
11
|
+
include ActiveModel::Validations
|
12
|
+
validates :phone_number, phone: true, allow_blank: true
|
13
|
+
end
|
10
14
|
|
11
15
|
class PhoneValidatorTest < Phonie::TestCase
|
12
16
|
def test_blank_phone
|
13
|
-
assert SomeModel.new(nil).
|
14
|
-
assert SomeModel.new('').
|
17
|
+
assert SomeModel.new(nil).invalid?
|
18
|
+
assert SomeModel.new('').invalid?
|
19
|
+
assert SomeOtherModel.new(nil).valid?
|
20
|
+
assert SomeOtherModel.new('').valid?
|
15
21
|
end
|
16
22
|
|
17
23
|
def test_valid_model
|
18
24
|
model = SomeModel.new('+1 251 123 4567')
|
19
25
|
assert model.valid?
|
20
|
-
|
21
|
-
assert model.phone == '+12511234567'
|
22
26
|
end
|
23
27
|
|
24
28
|
def test_valid_number_with_extension
|
25
29
|
model = SomeModel.new('+1 251 123 4567 ex 1234')
|
26
30
|
assert model.valid?
|
27
|
-
|
28
|
-
assert model.phone == '+12511234567x1234'
|
29
31
|
end
|
30
32
|
|
31
33
|
def test_invalid_model
|
32
34
|
model = SomeModel.new('+1 251 123 456')
|
33
35
|
assert model.invalid?
|
34
36
|
|
35
|
-
assert !model.errors[:
|
37
|
+
assert !model.errors[:phone_number].empty?
|
36
38
|
end
|
37
39
|
end
|
data/test/test_helper.rb
CHANGED
@@ -23,8 +23,10 @@ end
|
|
23
23
|
class Phonie::TestCase < Test::Unit::TestCase
|
24
24
|
|
25
25
|
def setup
|
26
|
-
Phonie
|
27
|
-
|
26
|
+
Phonie.configure do |config|
|
27
|
+
config.default_country_code = nil
|
28
|
+
config.default_area_code = nil
|
29
|
+
end
|
28
30
|
end
|
29
31
|
|
30
32
|
def default_test
|
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:
|
4
|
+
version: 3.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date:
|
16
|
+
date: 2014-01-23 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: activemodel
|
@@ -23,7 +23,7 @@ dependencies:
|
|
23
23
|
- - ! '>='
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '0'
|
26
|
-
type: :
|
26
|
+
type: :development
|
27
27
|
prerelease: false
|
28
28
|
version_requirements: !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
@@ -76,18 +76,21 @@ extra_rdoc_files: []
|
|
76
76
|
files:
|
77
77
|
- .gitignore
|
78
78
|
- .travis.yml
|
79
|
+
- Changelog.md
|
79
80
|
- Gemfile
|
80
81
|
- LICENSE
|
81
82
|
- Rakefile
|
82
83
|
- Readme.rdoc
|
83
84
|
- lib/phonie.rb
|
85
|
+
- lib/phonie/configuration.rb
|
84
86
|
- lib/phonie/country.rb
|
85
87
|
- lib/phonie/data/phone_countries.yml
|
88
|
+
- lib/phonie/formatter.rb
|
89
|
+
- lib/phonie/parser.rb
|
86
90
|
- lib/phonie/phone.rb
|
87
91
|
- lib/phonie/railties/locales/en.yml
|
88
92
|
- lib/phonie/railties/locales/es.yml
|
89
93
|
- lib/phonie/railties/validator.rb
|
90
|
-
- lib/phonie/support.rb
|
91
94
|
- lib/phonie/version.rb
|
92
95
|
- phonie.gemspec
|
93
96
|
- tasks/tests.rake
|
@@ -214,7 +217,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
214
217
|
version: '0'
|
215
218
|
segments:
|
216
219
|
- 0
|
217
|
-
hash:
|
220
|
+
hash: 2987221699007223305
|
218
221
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
222
|
none: false
|
220
223
|
requirements:
|
@@ -223,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
223
226
|
version: '0'
|
224
227
|
segments:
|
225
228
|
- 0
|
226
|
-
hash:
|
229
|
+
hash: 2987221699007223305
|
227
230
|
requirements: []
|
228
231
|
rubyforge_project:
|
229
232
|
rubygems_version: 1.8.23
|
data/lib/phonie/support.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
# support methods to remove dependencies on ActiveSupport
|
3
|
-
class String
|
4
|
-
def present?
|
5
|
-
!blank?
|
6
|
-
end
|
7
|
-
|
8
|
-
def blank?
|
9
|
-
if respond_to?(:empty?) && respond_to?(:strip)
|
10
|
-
empty? or strip.empty?
|
11
|
-
elsif respond_to?(:empty?)
|
12
|
-
empty?
|
13
|
-
else
|
14
|
-
!self
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class Hash
|
20
|
-
alias_method :blank?, :empty?
|
21
|
-
|
22
|
-
def present?
|
23
|
-
!blank?
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Object
|
28
|
-
def present?
|
29
|
-
self.class!=NilClass
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class NilClass #:nodoc:
|
34
|
-
def blank?
|
35
|
-
true
|
36
|
-
end
|
37
|
-
|
38
|
-
def present?
|
39
|
-
false
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
module Accessorize
|
44
|
-
module ClassMethods
|
45
|
-
def cattr_accessor(*syms)
|
46
|
-
syms.flatten.each do |sym|
|
47
|
-
class_eval(<<-EOS, __FILE__, __LINE__)
|
48
|
-
unless defined? @@#{sym}
|
49
|
-
@@#{sym} = nil
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.#{sym}
|
53
|
-
@@#{sym}
|
54
|
-
end
|
55
|
-
|
56
|
-
def #{sym}=(value)
|
57
|
-
@@#{sym} = value
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.#{sym}=(value)
|
61
|
-
@@#{sym} = value
|
62
|
-
end
|
63
|
-
|
64
|
-
def #{sym}
|
65
|
-
@@#{sym}
|
66
|
-
end
|
67
|
-
EOS
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
def self.included(receiver)
|
74
|
-
receiver.extend ClassMethods
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
Object.send(:include, Accessorize)
|