phonelib 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,4 @@
1
+ # main module definition
1
2
  module Phonelib
2
3
  # load gem classes
3
4
  autoload :Core, 'phonelib/core'
@@ -8,6 +9,6 @@ module Phonelib
8
9
  }
9
10
  end
10
11
 
11
- if defined?(Rails)
12
+ if defined?(ActiveModel)
12
13
  autoload :PhoneValidator, 'validators/phone_validator'
13
14
  end
@@ -16,25 +16,25 @@ module Phonelib
16
16
  def default_country=(country)
17
17
  @@default_country = country
18
18
  end
19
-
19
+
20
20
  # gem constants definition
21
21
  # constants for phone types
22
22
 
23
23
  # Validation patterns keys constants
24
24
  # General pattern for country key
25
- GENERAL = :generalDesc
25
+ GENERAL = :general_desc
26
26
  # Freephone line pattern key
27
- PREMIUM_RATE = :premiumRate
27
+ PREMIUM_RATE = :premium_rate
28
28
  # Freephone line pattern key
29
- TOLL_FREE = :tollFree
29
+ TOLL_FREE = :toll_free
30
30
  # Shared cost pattern key. The cost of this call is shared between caller
31
31
  # and recipient, and is hence typically less than PREMIUM_RATE calls
32
- SHARED_COST = :sharedCost
33
- # Voice over IP pattern key. This includes TSoIP (Telephony Service over IP)
32
+ SHARED_COST = :shared_cost
33
+ # VoIP pattern key. This includes TSoIP (Telephony Service over IP)
34
34
  VOIP = :voip
35
35
  # A personal number is associated with a particular person, and may be
36
36
  # routed to either a MOBILE or FIXED_LINE number.
37
- PERSONAL_NUMBER = :personalNumber
37
+ PERSONAL_NUMBER = :personal_number
38
38
  # Pager phone number pattern key
39
39
  PAGER = :pager
40
40
  # Used for 'Universal Access Numbers' or 'Company Numbers'. They may be
@@ -44,48 +44,48 @@ module Phonelib
44
44
  # Used for 'Voice Mail Access Numbers'.
45
45
  VOICEMAIL = :voicemail
46
46
  # Fixed line pattern key
47
- FIXED_LINE = :fixedLine
47
+ FIXED_LINE = :fixed_line
48
48
  # Mobile phone number pattern key
49
49
  MOBILE = :mobile
50
50
  # In case MOBILE and FIXED patterns are the same, this type is returned
51
- FIXED_OR_MOBILE = :fixedOrMobile
51
+ FIXED_OR_MOBILE = :fixed_or_mobile
52
52
 
53
53
  # Internal use keys for validations
54
54
  # Valid regex pattern key
55
- VALID_PATTERN = :nationalNumberPattern
55
+ VALID_PATTERN = :national_number_pattern
56
56
  # Possible regex pattern key
57
- POSSIBLE_PATTERN = :possibleNumberPattern
57
+ POSSIBLE_PATTERN = :possible_number_pattern
58
58
  # National prefix key
59
- NATIONAL_PREFIX = :nationalPrefix
59
+ NATIONAL_PREFIX = :national_prefix
60
60
  # National prefix rule key
61
- NATIONAL_PREFIX_RULE = :nationalPrefixFormattingRule
61
+ NATIONAL_PREFIX_RULE = :national_prefix_formatting_rule
62
62
  # Country code key
63
- COUNTRY_CODE = :countryCode
63
+ COUNTRY_CODE = :country_code
64
64
 
65
65
  # Default number formatting data hash
66
66
  DEFAULT_NUMBER_FORMAT = {
67
- pattern: "(\\d+)(\\d{3})(\\d{4})",
68
- format: "$1 $2 $3"
67
+ pattern: '(\\d+)(\\d{3})(\\d{4})',
68
+ format: '$1 $2 $3'
69
69
  }
70
70
 
71
71
  # hash of all phone types with human representation
72
72
  TYPES = {
73
- generalDesc: 'General Pattern',
74
- premiumRate: 'Premium Rate',
75
- tollFree: 'Toll Free',
76
- sharedCost: 'Shared Cost',
73
+ general_desc: 'General Pattern',
74
+ premium_rate: 'Premium Rate',
75
+ toll_free: 'Toll Free',
76
+ shared_cost: 'Shared Cost',
77
77
  voip: 'VoIP',
78
- personalNumber: 'Personal Number',
78
+ personal_number: 'Personal Number',
79
79
  pager: 'Pager',
80
80
  uan: 'UAN',
81
81
  voicemail: 'VoiceMail',
82
- fixedLine: 'Fixed Line',
82
+ fixed_line: 'Fixed Line',
83
83
  mobile: 'Mobile',
84
- fixedOrMobile: 'Fixed Line or Mobile'
84
+ fixed_or_mobile: 'Fixed Line or Mobile'
85
85
  }
86
86
 
87
87
  # array of types not included for validation check in cycle
88
- NOT_FOR_CHECK = [ :generalDesc, :fixedLine, :mobile, :fixedOrMobile ]
88
+ NOT_FOR_CHECK = [:general_desc, :fixed_line, :mobile, :fixed_or_mobile]
89
89
 
90
90
  # method for parsing phone number.
91
91
  # On first run fills @@phone_data with data present in yaml file
@@ -137,6 +137,7 @@ module Phonelib
137
137
  end
138
138
 
139
139
  private
140
+
140
141
  # Load data file into memory
141
142
  def load_data
142
143
  require 'yaml'
@@ -152,18 +153,18 @@ module Phonelib
152
153
 
153
154
  # Get Phone instance for provided phone with country specified
154
155
  def detect_and_parse_by_country(phone, country)
155
- detected = @@phone_data.detect { |data| data[:id] == country }
156
- if !!detected
156
+ detected = @@phone_data.find { |data| data[:id] == country }
157
+ if detected
157
158
  phone = convert_phone_to_e164(phone,
158
- detected[:countryCode],
159
- detected[:nationalPrefix])
159
+ detected[:country_code],
160
+ detected[:national_prefix])
160
161
  end
161
162
  Phonelib::Phone.new(phone, [detected])
162
163
  end
163
164
 
164
165
  # Create phone representation in e164 format
165
166
  def convert_phone_to_e164(phone, prefix, national_prefix)
166
- return phone if phone.gsub('+','').start_with?(prefix)
167
+ return phone if phone.gsub('+', '').start_with?(prefix)
167
168
  if !!national_prefix && phone.start_with?(national_prefix)
168
169
  phone = phone[1..phone.length]
169
170
  end
@@ -21,7 +21,7 @@ module Phonelib
21
21
 
22
22
  # Returns all phone types that matched valid patterns
23
23
  def types
24
- @analyzed_data.flat_map {|iso2, data| data[:valid]}.uniq
24
+ @analyzed_data.flat_map { |iso2, data| data[:valid] }.uniq
25
25
  end
26
26
 
27
27
  # Returns first phone type that matched
@@ -29,21 +29,40 @@ module Phonelib
29
29
  types.first
30
30
  end
31
31
 
32
+ # Returns human representation of all matched phone types
33
+ def human_types
34
+ types.map { |type| Core::TYPES[type] }
35
+ end
36
+
37
+ # Return human representation of phone type
38
+ def human_type
39
+ Core::TYPES[type]
40
+ end
41
+
32
42
  # Returns all countries that matched valid patterns
33
43
  def countries
34
- @analyzed_data.map {|iso2, data| iso2}
44
+ @analyzed_data.map { |iso2, data| iso2 }
45
+ end
46
+
47
+ # Return countries with valid patterns
48
+ def valid_countries
49
+ @valid_countries ||= countries.select do |iso2|
50
+ @analyzed_data[iso2][:valid].any?
51
+ end
35
52
  end
36
53
 
37
54
  # Returns first country that matched valid patterns
38
55
  def country
39
- @country ||= countries.detect do |iso2|
40
- !@analyzed_data[iso2][:valid].empty?
56
+ @country ||= begin
57
+ valid_countries.find do |iso2|
58
+ @analyzed_data[iso2][:main_country_for_code] == 'true'
59
+ end || valid_countries.first
41
60
  end
42
61
  end
43
62
 
44
63
  # Returns whether a current parsed phone number is valid
45
64
  def valid?
46
- @analyzed_data.select {|iso2, data| data[:valid].any? }.any?
65
+ @analyzed_data.select { |iso2, data| data[:valid].any? }.any?
47
66
  end
48
67
 
49
68
  # Returns whether a current parsed phone number is invalid
@@ -53,7 +72,7 @@ module Phonelib
53
72
 
54
73
  # Returns whether a current parsed phone number is possible
55
74
  def possible?
56
- @analyzed_data.select {|iso2, data| data[:possible].any? }.any?
75
+ @analyzed_data.select { |iso2, data| data[:possible].any? }.any?
57
76
  end
58
77
 
59
78
  # Returns whether a current parsed phone number is impossible
@@ -68,11 +87,11 @@ module Phonelib
68
87
 
69
88
  # add space to format groups, change first group to rule,
70
89
  # change rule's constants to values
71
- format_string = format[:format].gsub(/(\d)\$/, "\\1 $").gsub('$1', rule).
72
- gsub(/(\$NP|\$FG)/, { '$NP' => prefix, '$FG' => '$1' })
90
+ format_string = format[:format].gsub(/(\d)\$/, '\\1 $').gsub('$1', rule)
91
+ .gsub(/(\$NP|\$FG)/, '$NP' => prefix, '$FG' => '$1')
73
92
 
74
93
  matches = @national_number.match(/#{format[:pattern]}/)
75
- format_string.gsub(/\$\d/) {|el| matches[el[1].to_i] }
94
+ format_string.gsub(/\$\d/) { |el| matches[el[1].to_i] }
76
95
  end
77
96
 
78
97
  # Returns e164 formatted phone number
@@ -93,12 +112,14 @@ module Phonelib
93
112
  #
94
113
  # ==== Attributes
95
114
  #
96
- # * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us for United States
115
+ # * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us
116
+ # for United States
97
117
  #
98
118
  def valid_for_country?(country)
99
119
  country = country.to_s.upcase
100
- @analyzed_data.select {|iso2, data| country == iso2 &&
101
- data[:valid].any? }.any?
120
+ @analyzed_data.select do |iso2, data|
121
+ country == iso2 && data[:valid].any?
122
+ end.any?
102
123
  end
103
124
 
104
125
  # Returns whether a current parsed phone number is invalid for specified
@@ -106,13 +127,15 @@ module Phonelib
106
127
  #
107
128
  # ==== Attributes
108
129
  #
109
- # * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us for United States
130
+ # * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us
131
+ # for United States
110
132
  #
111
133
  def invalid_for_country?(country)
112
134
  !valid_for_country?(country)
113
135
  end
114
136
 
115
137
  private
138
+
116
139
  # Get needable data for formatting phone as national number
117
140
  def get_formatting_data
118
141
  format = @analyzed_data[country][:format]
@@ -126,13 +149,15 @@ module Phonelib
126
149
  # Analyze current phone with provided data hash
127
150
  def analyze_phone(country_data)
128
151
  country_data.each do |data|
129
- if @sanitized.start_with? "#{data[:countryCode]}#{data[:leadingDigits]}"
130
- possible, valid = get_patterns(data[:types][Core::GENERAL])
131
- next unless /^#{data[:countryCode]}#{valid}$/ =~ @sanitized
152
+ phone_code = "#{data[:country_code]}#{data[:leading_digits]}"
153
+ if @sanitized.start_with? phone_code
154
+ _possible, valid = get_patterns(data[:types][Core::GENERAL])
155
+ next unless /^#{data[:country_code]}#{valid}$/ =~ @sanitized
132
156
 
133
- @national_number = @sanitized[data[:countryCode].length..-1]
157
+ @national_number = @sanitized[data[:country_code].length..-1]
134
158
  @analyzed_data[data[:id]] = data
135
- @analyzed_data[data[:id]][:format] = get_number_format(data[:formats])
159
+ @analyzed_data[data[:id]][:format] =
160
+ get_number_format(data[:formats])
136
161
  @analyzed_data[data[:id]].merge! all_number_types(data[:types])
137
162
  end
138
163
  end
@@ -141,16 +166,11 @@ module Phonelib
141
166
  # Returns all valid and possible phone number types for currently parsed
142
167
  # phone for provided data hash.
143
168
  def all_number_types(data)
144
- response = {valid: [], possible: []}
145
-
146
- additional_types = check_same_types(data)
147
- (Core::TYPES.keys - Core::NOT_FOR_CHECK + additional_types).each do |type|
148
- check_type = case type
149
- when Core::FIXED_OR_MOBILE
150
- Core::FIXED_LINE
151
- else
152
- type
153
- end
169
+ response = { valid: [], possible: [] }
170
+
171
+ additional = check_same_types(data)
172
+ (Core::TYPES.keys - Core::NOT_FOR_CHECK + additional).each do |type|
173
+ check_type = (type == Core::FIXED_OR_MOBILE ? Core::FIXED_LINE : type)
154
174
  possible, valid = get_patterns(data[check_type])
155
175
 
156
176
  response[:possible] << type if number_possible?(possible)
@@ -164,8 +184,8 @@ module Phonelib
164
184
  def get_number_format(format_data)
165
185
  if format_data
166
186
  format_data.find do |format|
167
- (format[:leadingDigits].nil? \
168
- || /^#{format[:leadingDigits]}/ =~ @national_number) \
187
+ (format[:leading_digits].nil? \
188
+ || /^#{format[:leading_digits]}/ =~ @national_number) \
169
189
  && /^#{format[:pattern]}$/ =~ @national_number
170
190
  end
171
191
  else
@@ -176,9 +196,9 @@ module Phonelib
176
196
  # Checks if fixed line pattern and mobile pattern are the same
177
197
  def check_same_types(data)
178
198
  if data[Core::FIXED_LINE] == data[Core::MOBILE]
179
- [ Core::FIXED_OR_MOBILE ]
199
+ [Core::FIXED_OR_MOBILE]
180
200
  else
181
- [ Core::FIXED_LINE, Core::MOBILE ]
201
+ [Core::FIXED_LINE, Core::MOBILE]
182
202
  end
183
203
  end
184
204
 
@@ -1,4 +1,5 @@
1
+ # :nodoc:
1
2
  module Phonelib
2
3
  # :nodoc:
3
- VERSION = "0.2.4"
4
+ VERSION = '0.2.5'
4
5
  end
@@ -1,6 +1,6 @@
1
1
  namespace :phonelib do
2
2
 
3
- desc "Import and reparse original data file from Google libphonenumber"
3
+ desc 'Import and reparse original data file from Google libphonenumber'
4
4
  task :import_data do
5
5
  require 'net/http'
6
6
  require 'yaml'
@@ -11,7 +11,7 @@ namespace :phonelib do
11
11
  xml_data = Net::HTTP.get_response(URI.parse(url)).body
12
12
 
13
13
  # save in file for debug
14
- File.open("data/PhoneNumberMetaData.xml", "w+") do |f|
14
+ File.open('data/PhoneNumberMetaData.xml', 'w+') do |f|
15
15
  f.write(xml_data)
16
16
  end
17
17
 
@@ -24,20 +24,20 @@ namespace :phonelib do
24
24
  # each country
25
25
  country = {}
26
26
  el.attributes.each do |k, v|
27
- country[k.to_sym] = v.to_s.tr(" \n", "")
27
+ country[camel2snake(k).to_sym] = v.to_s.tr(" \n", '')
28
28
  end
29
29
 
30
30
  country[:types] = {}
31
31
 
32
32
  el.children.each do | phone_type |
33
33
  if !%w(comment text).include?(phone_type.name)
34
- phone_type_sym = phone_type.name.to_sym
34
+ phone_type_sym = camel2snake(phone_type.name).to_sym
35
35
 
36
36
  if phone_type.name != 'availableFormats'
37
37
  country[:types][phone_type_sym] = {}
38
38
  phone_type.elements.each do |pattern|
39
- country[:types][phone_type_sym][pattern.name.to_sym] =
40
- pattern.children.first.to_s.tr(" \n", "")
39
+ country[:types][phone_type_sym][camel2snake(pattern.name).to_sym] =
40
+ pattern.children.first.to_s.tr(" \n", '')
41
41
  end
42
42
  else
43
43
  country[:formats] = []
@@ -46,12 +46,13 @@ namespace :phonelib do
46
46
  if !%w(comment text).include?(format.name)
47
47
  current_format = {}
48
48
  format.each do |f|
49
- current_format[f[0].to_sym] = f[1]
49
+ current_format[camel2snake(f[0]).to_sym] = f[1]
50
50
  end
51
51
 
52
52
  format.children.each do |f|
53
53
  if f.name != 'text'
54
- current_format[f.name.to_sym] = f.children.first.to_s.gsub(/\n\s+/, "")
54
+ current_format[camel2snake(f.name).to_sym] =
55
+ f.children.first.to_s.gsub(/\n\s+/, '')
55
56
  end
56
57
  end
57
58
 
@@ -66,8 +67,12 @@ namespace :phonelib do
66
67
  countries.push(country)
67
68
  end
68
69
  target = 'data/phone_data.yml'
69
- File.open(target, "w+") do |f|
70
+ File.open(target, 'w+') do |f|
70
71
  f.write(countries.to_yaml)
71
72
  end
72
73
  end
74
+
75
+ def camel2snake(s)
76
+ s.gsub(/[A-Z]+/) { |m| "_#{m.downcase}" }
77
+ end
73
78
  end
@@ -24,7 +24,7 @@
24
24
  # class Phone < ActiveRecord::Base
25
25
  # attr_accessible :number
26
26
  # validates :number, phone: { allow_blank: true }
27
- # end
27
+ # end
28
28
  class PhoneValidator < ActiveModel::EachValidator
29
29
  # Include all core methods
30
30
  include Phonelib::Core
@@ -3,5 +3,6 @@
3
3
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4
4
 
5
5
  require File.expand_path('../config/application', __FILE__)
6
-
6
+ require 'rake/dsl_definition'
7
+ require 'rake'
7
8
  Dummy::Application.load_tasks
Binary file
@@ -1,31 +1,19 @@
1
1
  Connecting to database specified by database.yml
2
-  (0.1ms) select sqlite_version(*)
3
-  (7.4ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL)
4
-  (2.8ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
5
-  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations"
6
- Migrating to CreatePhones (20130121173847)
7
-  (0.1ms) begin transaction
8
-  (0.8ms) CREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL)
9
-  (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES ('20130121173847')
10
-  (4.1ms) commit transaction
11
- Migrating to AddPossibleNumberToPhone (20130122075331)
12
-  (0.1ms) begin transaction
13
-  (0.6ms) ALTER TABLE "phones" ADD "possible_number" varchar(255)
14
-  (0.1ms) INSERT INTO "schema_migrations" ("version") VALUES ('20130122075331')
15
-  (2.7ms) commit transaction
16
-  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" 
2
+  (0.9ms) select sqlite_version(*)
3
+  (6.8ms) CREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "possible_number" varchar(255))
4
+  (1.4ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) 
5
+  (1.6ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
6
+  (0.1ms) SELECT version FROM "schema_migrations"
7
+  (3.2ms) INSERT INTO "schema_migrations" (version) VALUES ('20130122075331')
8
+  (1.3ms) INSERT INTO "schema_migrations" (version) VALUES ('20130121173847')
9
+  (0.7ms) SELECT "schema_migrations"."version" FROM "schema_migrations"
17
10
  Connecting to database specified by database.yml
18
-  (0.7ms) select sqlite_version(*)
19
-  (5.1ms) DROP TABLE "phones"
20
-  (3.3ms) CREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "possible_number" varchar(255)) 
21
-  (0.2ms) SELECT version FROM "schema_migrations"
22
-  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" 
23
- Connecting to database specified by database.yml
24
-  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" 
25
-  (0.3ms) select sqlite_version(*)
26
-  (5.1ms) CREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "possible_number" varchar(255)) 
27
-  (2.3ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL)
28
-  (3.4ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
11
+  (0.6ms) SELECT "schema_migrations"."version" FROM "schema_migrations" 
12
+  (0.1ms) select sqlite_version(*)
13
+  (3.7ms) CREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "possible_number" varchar(255)) 
14
+  (1.4ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL)
15
+  (1.7ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
29
16
   (0.1ms) SELECT version FROM "schema_migrations"
30
-  (2.4ms) INSERT INTO "schema_migrations" (version) VALUES ('20130122075331')
31
-  (3.7ms) INSERT INTO "schema_migrations" (version) VALUES ('20130121173847')
17
+  (1.0ms) INSERT INTO "schema_migrations" (version) VALUES ('20130122075331')
18
+  (4.9ms) INSERT INTO "schema_migrations" (version) VALUES ('20130121173847')
19
+ Connecting to database specified by database.yml