phonie 2.1.2 → 3.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/.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)
|