phonelib 0.2.4 → 0.2.5

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