russian_phone 0.1.1 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -4,8 +4,6 @@
4
4
 
5
5
  Гем для разбора и нормализации русских телефонных номеров
6
6
 
7
- ## Внимание: гем находится в разработке и пока не готов для использования
8
-
9
7
  ## Альтернативные решения
10
8
 
11
9
  https://github.com/floere/phony
@@ -14,10 +12,13 @@ https://github.com/joost/phony_rails
14
12
 
15
13
  https://github.com/carr/phone
16
14
 
17
- Эти решения поддерживают несколько стран
15
+ Указанные решения поддерживают несколько стран
18
16
 
19
17
  russian_phone поддерживает и будет поддерживать только российские телефонные номера
20
18
 
19
+ Целью данного гема было скорее получение номера в чистом виде (10 значного) из введенных пользователем данных, и
20
+ возможный минимум отбраковки верных номеров, которые сможет разобрать человек, чем 100% отбраковка неверных номеров.
21
+
21
22
  ## Installation
22
23
 
23
24
  Add this line to your application's Gemfile:
@@ -34,7 +35,43 @@ Or install it yourself as:
34
35
 
35
36
  ## Usage
36
37
 
37
- TODO: Write usage instructions here
38
+ Использованиие для разбора телефонных номеров:
39
+
40
+ # phone = RussianPhone::Number.new('(906) 111-11-11', default_country: 7)
41
+ => "+7 (906) 111-11-11"
42
+ # phone.country
43
+ => "7"
44
+ # phone.city
45
+ => "906"
46
+ # phone.subscriber
47
+ => "1111111"
48
+ # phone.full
49
+ => "+7 (906) 111-11-11"
50
+ # phone.cell?
51
+ => true
52
+ # phone.free?
53
+ => false
54
+ # phone.valid?
55
+ => true
56
+
57
+ Использование с Mongoid:
58
+
59
+ class User
60
+ include Mongoid::Document
61
+ field :phone, type: RussianPhone.field(default_country: 7, allowed_cities: [495]), validate: true
62
+ end
63
+
64
+ # u = User.new(phone: '495 1111111')
65
+ # u.phone
66
+ => '+7 (495) 111-11-11'
67
+ # u.phone.valid?
68
+ => true
69
+
70
+ Обратите внимание, по умолчанию *валидация телефонного номера выключена*, это значит что номер будет
71
+ сохраняться в базу даже если гем не смог его разобрать. Включите валидацию, установив validate:true.
72
+
73
+ В базе телефоны храняться в виде строки в полном виде, если телефон удалось разобрать, и в том виде как
74
+ введено пользователем, если не удалось разобрать и validate == false
38
75
 
39
76
  ## Contributing
40
77
 
@@ -10,7 +10,7 @@ module RussianPhone
10
10
 
11
11
  ::Mongoid::Fields.option :validate do |model, field, value|
12
12
  if value
13
- model.validates_with RussianPhone::Validator, fields: [field.name]
13
+ model.validates_with(RussianPhone::Validator, fields: [field.name])
14
14
  end
15
15
  end
16
16
 
@@ -34,6 +34,7 @@ module RussianPhone
34
34
  @subscriber = data[:subscriber].to_s
35
35
  @city = data[:city].to_s
36
36
  @country = data[:country].to_s
37
+ @extra = data[:extra].to_s
37
38
  end
38
39
 
39
40
  data.has_key?(field) ? data[field] : nil
@@ -59,6 +60,10 @@ module RussianPhone
59
60
  @country ||= parse(:country)
60
61
  end
61
62
 
63
+ def extra
64
+ @extra ||= parse(:extra)
65
+ end
66
+
62
67
  def split(format, number)
63
68
  number = number.dup
64
69
  format.inject([]) do |result, size|
@@ -82,10 +87,10 @@ module RussianPhone
82
87
 
83
88
  def full
84
89
  if valid?
85
- if free?
90
+ if free? && extra == ''
86
91
  "8-#{city}-#{format.join('-')}"
87
92
  else
88
- "+#{country} (#{city}) #{format.join('-')}"
93
+ "+#{country} (#{city}) #{format.join('-')}#{extra == '' ? '' : ' ' + extra}"
89
94
  end
90
95
  else
91
96
  ''
@@ -118,8 +123,11 @@ module RussianPhone
118
123
  end
119
124
 
120
125
  #def mongoize
121
- # valid? ? full : @phone
126
+ # @phone
122
127
  #end
128
+ # alias_method(:as_json, :mongoize)
129
+
130
+ alias_method(:as_json, :to_s)
123
131
  alias_method(:mongoize, :to_s)
124
132
 
125
133
  class << self
@@ -127,14 +135,36 @@ module RussianPhone
127
135
  string.tr('^0-9', '')
128
136
  end
129
137
 
138
+ #def _extract(string, subscriber_digits, code_digits)
139
+ # [string[-(subscriber_digits + code_digits), code_digits], string[-subscriber_digits,subscriber_digits]]
140
+ #end
141
+
130
142
  def _extract(string, subscriber_digits, code_digits)
131
- [string[-(subscriber_digits + code_digits), code_digits], string[-subscriber_digits,subscriber_digits]]
143
+ [string[0, code_digits], string[code_digits, subscriber_digits]]
132
144
  end
133
145
 
134
146
  def extract(string, subscriber_digits, code_digits)
135
147
  _extract(clean(string), subscriber_digits, code_digits)
136
148
  end
137
149
 
150
+ def _extra(string, position)
151
+ i = 0
152
+ digits = 0
153
+
154
+ string.each_char do |char|
155
+ if char.match(/[0-9]/)
156
+ digits += 1
157
+ end
158
+ i += 1
159
+ # puts "#{char} #{digits} #{i} #{position}"
160
+ if digits >= position
161
+ return string[i..-1].strip
162
+ end
163
+ end
164
+
165
+ ''
166
+ end
167
+
138
168
  def country_code(string)
139
169
  clean(string)[-10, 1]
140
170
  end
@@ -166,10 +196,6 @@ module RussianPhone
166
196
 
167
197
  clean_string = clean(string)
168
198
 
169
- if clean_string.length > 11
170
- return nil
171
- end
172
-
173
199
  if clean_string.length < 10
174
200
  if opts[:default_city].nil?
175
201
  return nil
@@ -186,22 +212,65 @@ module RussianPhone
186
212
  end
187
213
  end
188
214
 
215
+ extra_after = 10
216
+
217
+ if clean_string.length > 10 && string.starts_with?('+7') || string.starts_with?('8 ') || string.starts_with?('8(') || string.starts_with?('8-')
218
+ clean_string[0] = ''
219
+ extra_after += 1
220
+ end
221
+
222
+ if clean_string.length == 11 && string.starts_with?('7')
223
+ clean_string[0] = ''
224
+ extra_after += 1
225
+ end
226
+
227
+ if clean_string.length == 11 && string.starts_with?('8')
228
+ clean_string[0] = ''
229
+ extra_after += 1
230
+ end
231
+
232
+ # handles stuff like 89061010101 д. 123
233
+ if string.split(' ').length > 1 && string.split(/\D/)[0].length > 10
234
+ if string.starts_with?('7')
235
+ clean_string[0] = ''
236
+ extra_after += 1
237
+ end
238
+
239
+ if string.starts_with?('8')
240
+ clean_string[0] = ''
241
+ extra_after += 1
242
+ end
243
+ end
244
+
245
+ # handle 7906123-12-12 доб 123
246
+ if clean_string.length > 10 && extra_after == 10 && (clean_string[0] == '7' || clean_string[0] == '8')
247
+ result = ''
248
+ string.split(/\D/).each do |segm|
249
+ result += segm
250
+ break if result.length >= 10
251
+ end
252
+ if result.length > 10
253
+ clean_string[0] = ''
254
+ extra_after += 1
255
+ end
256
+ end
257
+
189
258
  code_3_digit, phone_7_digit = _extract(clean_string, 7, 3)
190
259
  if code_3_digit == '800' || Codes.cell_codes.include?(code_3_digit) || Codes.ndcs_with_7_subscriber_digits.include?(code_3_digit)
191
- return {country: opts[:default_country], city: code_3_digit, subscriber: phone_7_digit}
260
+ return {country: opts[:default_country], city: code_3_digit, subscriber: phone_7_digit, extra: _extra(string, extra_after)}
192
261
  end
193
262
 
194
263
  code_4_digit, phone_6_digit = _extract(clean_string, 6, 4)
195
264
  if Codes.ndcs_with_6_subscriber_digits.include? code_4_digit
196
- return {country: opts[:default_country], city: code_4_digit, subscriber: phone_6_digit}
265
+ return {country: opts[:default_country], city: code_4_digit, subscriber: phone_6_digit, extra: _extra(string, extra_after)}
197
266
  end
198
267
 
199
268
  code_5_digit, phone_5_digit = _extract(clean_string, 5, 5)
200
269
  if Codes.ndcs_with_5_subscriber_digits.include? code_5_digit
201
- return {country: opts[:default_country], city: code_5_digit, subscriber: phone_5_digit}
270
+ return {country: opts[:default_country], city: code_5_digit, subscriber: phone_5_digit, extra: _extra(string, extra_after)}
202
271
  end
203
272
 
204
- nil
273
+ return {country: opts[:default_country], city: code_3_digit, subscriber: phone_7_digit, extra: _extra(string, extra_after)}
205
274
  end
206
275
  end
207
276
  end
@@ -1,3 +1,3 @@
1
1
  module RussianPhone
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -121,7 +121,21 @@ describe RussianPhone do
121
121
  phone.full.should eq '+7 (4912) 12-34-56'
122
122
  end
123
123
 
124
- it 'should parse 12-34-56 number whith default city' do
124
+ it 'should parse 2-34-56 number whith default city' do
125
+ phone = RussianPhone::Number.new('2-34-56', default_country: 7, default_city: 87252)
126
+ phone.should_not be_nil
127
+ phone.valid?.should be_true
128
+
129
+ phone.cell?.should be_false
130
+ phone.free?.should be_false
131
+
132
+ phone.city.should eq '87252'
133
+ phone.country.should eq '7'
134
+ phone.subscriber.should eq '23456'
135
+ phone.full.should eq '+7 (87252) 2-34-56'
136
+ end
137
+
138
+ it 'should parse 12-34-56 number whith default city' do
125
139
  phone = RussianPhone::Number.new('12-34-56', default_country: 7, default_city: 4912)
126
140
  phone.should_not be_nil
127
141
  phone.valid?.should be_true
@@ -151,6 +165,92 @@ describe RussianPhone do
151
165
  phone.full.should eq '+7 (343) 565-67-89'
152
166
  end
153
167
 
168
+ it 'should parse 7 (906) 123-45-67 number correctly' do
169
+ # это номер в Екатеринбурге, с неправильно указанным кодом города
170
+
171
+ phone = RussianPhone::Number.new('7 (906) 123-45-67', default_country: 7)
172
+ phone.should_not be_nil
173
+ phone.valid?.should be_true
174
+
175
+ phone.cell?.should be_true
176
+ phone.free?.should be_false
177
+ phone.extra.should eq ''
178
+ phone.city.should eq '906'
179
+ phone.country.should eq '7'
180
+ phone.subscriber.should eq '1234567'
181
+ phone.full.should eq '+7 (906) 123-45-67'
182
+ end
183
+
184
+ it 'should handle phones with extra stuff [8 (906) 111-11-11 доб. 123]' do
185
+ phone = RussianPhone::Number.new('8 (906) 111-11-11 доб. 123', default_country: 7)
186
+ phone.should_not be_nil
187
+ phone.valid?.should be_true
188
+
189
+ phone.cell?.should be_true
190
+ phone.free?.should be_false
191
+
192
+ phone.city.should eq '906'
193
+ phone.country.should eq '7'
194
+ phone.subscriber.should eq '1111111'
195
+ phone.full.should eq '+7 (906) 111-11-11 доб. 123'
196
+ end
197
+
198
+ it 'should handle phones with extra stuff [8 (906) 111-11-11 д. 123]' do
199
+ phone = RussianPhone::Number.new('8 (906) 111-11-11 д. 123', default_country: 7)
200
+ phone.should_not be_nil
201
+ phone.valid?.should be_true
202
+
203
+ phone.cell?.should be_true
204
+ phone.free?.should be_false
205
+
206
+ phone.city.should eq '906'
207
+ phone.country.should eq '7'
208
+ phone.subscriber.should eq '1111111'
209
+ phone.full.should eq '+7 (906) 111-11-11 д. 123'
210
+ end
211
+
212
+ it 'should handle phones with extra stuff [89061010101 д. 123]' do
213
+ phone = RussianPhone::Number.new('89061010101 д. 123', default_country: 7)
214
+ phone.should_not be_nil
215
+ phone.valid?.should be_true
216
+
217
+ phone.cell?.should be_true
218
+ phone.free?.should be_false
219
+
220
+ phone.city.should eq '906'
221
+ phone.country.should eq '7'
222
+ phone.subscriber.should eq '1010101'
223
+ phone.full.should eq '+7 (906) 101-01-01 д. 123'
224
+ end
225
+
226
+ it 'should handle phones with extra stuff [89061010101-д. 123]' do
227
+ phone = RussianPhone::Number.new('89061010101-д. 123', default_country: 7)
228
+ phone.should_not be_nil
229
+ phone.valid?.should be_true
230
+
231
+ phone.cell?.should be_true
232
+ phone.free?.should be_false
233
+
234
+ phone.city.should eq '906'
235
+ phone.country.should eq '7'
236
+ phone.subscriber.should eq '1010101'
237
+ phone.full.should eq '+7 (906) 101-01-01 -д. 123'
238
+ end
239
+
240
+ it 'should handle phones with unknown codes [8 (533) 111-11-11]' do
241
+ phone = RussianPhone::Number.new('8 (533) 111-11-11', default_country: 7)
242
+ phone.should_not be_nil
243
+ phone.valid?.should be_true
244
+
245
+ phone.cell?.should be_false
246
+ phone.free?.should be_false
247
+
248
+ phone.city.should eq '533'
249
+ phone.country.should eq '7'
250
+ phone.subscriber.should eq '1111111'
251
+ phone.full.should eq '+7 (533) 111-11-11'
252
+ end
253
+
154
254
  tests = {
155
255
  '+79261234567' => [7, 926, 1234567],
156
256
  '89261234567' => [7, 926, 1234567],
@@ -187,6 +287,8 @@ describe RussianPhone do
187
287
  '7 (927) 12 342 34' => [7, 927, 1234234],
188
288
  '7-(927)-12-342-34' => [7, 927, 1234234],
189
289
  '7 84543 123 12' => [7, 84543, 12312],
290
+ '78454312312' => [7, 84543, 12312],
291
+ '88454312312' => [7, 84543, 12312],
190
292
  }
191
293
 
192
294
  tests.each_pair do |str, result|
@@ -212,10 +314,10 @@ describe RussianPhone do
212
314
 
213
315
  bad_phones = [
214
316
  '123 123',
215
- '11 11 11 11 11 11 11',
317
+ '000000',
216
318
  '123',
217
319
  'пыщ пыщ ололо я водитель НЛО',
218
- '11 11 11 11 1'
320
+ '11 1 11 1'
219
321
  ]
220
322
 
221
323
  bad_phones.each do |phone|
@@ -228,8 +330,15 @@ describe RussianPhone do
228
330
 
229
331
  describe 'when using ::Field' do
230
332
  it 'should serialize and deserialize correctly' do
333
+ RussianPhone::Number.new('8 (906) 111-11-11 д. 123').mongoize.should eq '+7 (906) 111-11-11 д. 123'
334
+
231
335
  RussianPhone::Number.new('495 111 11 11').mongoize.should eq '+7 (495) 111-11-11'
336
+
232
337
  RussianPhone::Number.demongoize('+7 (495) 111-11-11').mongoize.should eq '+7 (495) 111-11-11'
338
+
339
+ RussianPhone::Field.mongoize(RussianPhone::Number.new('495 111 11 11')).should eq '+7 (495) 111-11-11'
340
+ RussianPhone::Field.evolve(RussianPhone::Number.new('495 111 11 11')).should eq '+7 (495) 111-11-11'
341
+ RussianPhone::Field.evolve(RussianPhone::Number.new('495 111 11 11 доб 123')).should eq '+7 (495) 111-11-11 доб 123'
233
342
  end
234
343
  end
235
344
 
@@ -292,6 +401,81 @@ describe RussianPhone do
292
401
  u = UserWithValidation.new(phone: '495 121 11 11')
293
402
  u.valid?.should be_true
294
403
  u.save.should be_true
404
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 121-11-11'
405
+ UserWithValidation.first.phone.should eq '+7 (495) 121-11-11'
406
+ end
407
+
408
+ it 'should pass validation when validate is on and phone is valid' do
409
+ u = UserWithValidation.new(phone: '7495 121 11 11')
410
+ u.valid?.should be_true
411
+ u.save.should be_true
412
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 121-11-11'
413
+ UserWithValidation.first.phone.should eq '+7 (495) 121-11-11'
414
+ end
415
+
416
+ it 'should pass validation when validate is on and phone is valid' do
417
+ u = UserWithValidation.new(phone: '7 495 121 11 11')
418
+ u.valid?.should be_true
419
+ u.save.should be_true
420
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 121-11-11'
421
+ UserWithValidation.first.phone.should eq '+7 (495) 121-11-11'
422
+ end
423
+
424
+ it 'should pass validation when validate is on and phone is valid' do
425
+ u = UserWithValidation.new(phone: '8495 121 11 11')
426
+ u.valid?.should be_true
427
+ u.save.should be_true
428
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 121-11-11'
429
+ UserWithValidation.first.phone.should eq '+7 (495) 121-11-11'
430
+ end
431
+
432
+ it 'should pass validation when validate is on and phone is valid' do
433
+ u = UserWithValidation.new(phone: '7495123-12-12 доб 123')
434
+
435
+ u.valid?.should be_true
436
+ u.save.should be_true
437
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 123-12-12 доб 123'
438
+ UserWithValidation.first.phone.should eq '+7 (495) 123-12-12 доб 123'
439
+ end
440
+
441
+ it 'should pass validation when phone is valid (unknown city code)' do
442
+ u = UserWithAnyCode.new(phone: '7701123-12-12 доб 123')
443
+ u.valid?.should be_true
444
+ u.save.should be_true
445
+ UserWithAnyCode.first.read_attribute(:phone).should eq '+7 (701) 123-12-12 доб 123'
446
+ UserWithAnyCode.first.phone.should eq '+7 (701) 123-12-12 доб 123'
447
+ end
448
+
449
+ it 'should pass validation when validate is on and phone is valid (unknown city code)' do
450
+ u = UserWithAnyCode.new(phone: '701123-12-12 доб 123')
451
+ u.valid?.should be_true
452
+ u.save.should be_true
453
+ UserWithAnyCode.first.read_attribute(:phone).should eq '+7 (701) 123-12-12 доб 123'
454
+ UserWithAnyCode.first.phone.should eq '+7 (701) 123-12-12 доб 123'
455
+ end
456
+
457
+ it 'should pass validation when validate is on and phone is valid' do
458
+ u = UserWithValidation.new(phone: '8 495 121 11 11')
459
+ u.valid?.should be_true
460
+ u.save.should be_true
461
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 121-11-11'
462
+ UserWithValidation.first.phone.should eq '+7 (495) 121-11-11'
463
+ end
464
+
465
+ it 'should pass validation when validate is on and phone is valid' do
466
+ u = UserWithValidation.new(phone: '+7 495 121 11 11')
467
+ u.valid?.should be_true
468
+ u.save.should be_true
469
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 121-11-11'
470
+ UserWithValidation.first.phone.should eq '+7 (495) 121-11-11'
471
+ end
472
+
473
+ it 'should pass validation when validate is on and phone is valid' do
474
+ u = UserWithValidation.new(phone: '495 121 11 11 доб 123')
475
+ u.valid?.should be_true
476
+ u.save.should be_true
477
+ UserWithValidation.first.read_attribute(:phone).should eq '+7 (495) 121-11-11 доб 123'
478
+ UserWithValidation.first.phone.should eq '+7 (495) 121-11-11 доб 123'
295
479
  end
296
480
 
297
481
  it 'should fail validation when validate is on and city is not in allowed_cities' do
@@ -0,0 +1,7 @@
1
+ # coding: utf-8
2
+
3
+ class UserWithAnyCode
4
+ include Mongoid::Document
5
+
6
+ field :phone, type: RussianPhone.field(default_country: 7), validate: true
7
+ end
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+
3
+ if ! which md5sum > /dev/null; then
4
+ echo Install md5sum
5
+ exit 1
6
+ fi
7
+
8
+ if ! which curl > /dev/null; then
9
+ echo Install curl
10
+ exit 1
11
+ fi
12
+
13
+ home=$(gem env GEM_HOME)
14
+ cache=$home/cache
15
+
16
+ echo This will take a while...
17
+
18
+ for gem in $cache/*.gem; do
19
+ gemfile=$(basename $gem)
20
+
21
+ local=$(md5sum $gem | awk '{print $1}')
22
+ remote=$(curl -s -D - -X HEAD -H 'Connection:close' http://production.cf.rubygems.org/gems/$gemfile | grep 'ETag' | cut -d '"' -f 2)
23
+
24
+ # echo $gemfile local: $local, remote: $remote
25
+
26
+ if [[ ! $local = $remote ]]; then
27
+ echo $gemfile mismatch. local: $local, remote: $remote
28
+ fi
29
+
30
+
31
+ done
32
+
33
+ echo All done.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: russian_phone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-28 00:00:00.000000000 Z
12
+ date: 2013-02-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongoid
@@ -114,8 +114,10 @@ files:
114
114
  - spec/phone_spec.rb
115
115
  - spec/spec_helper.rb
116
116
  - spec/support/user.rb
117
+ - spec/support/user_with_any_code.rb
117
118
  - spec/support/user_with_validation.rb
118
119
  - spec/support/user_without_validation.rb
120
+ - validate.sh
119
121
  homepage: https://github.com/glebtv/russian_phone
120
122
  licenses: []
121
123
  post_install_message:
@@ -136,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
138
  version: '0'
137
139
  requirements: []
138
140
  rubyforge_project:
139
- rubygems_version: 1.8.25
141
+ rubygems_version: 1.8.24
140
142
  signing_key:
141
143
  specification_version: 3
142
144
  summary: Поле для хранения Российских телефонных номеров в Mongoid
@@ -144,5 +146,6 @@ test_files:
144
146
  - spec/phone_spec.rb
145
147
  - spec/spec_helper.rb
146
148
  - spec/support/user.rb
149
+ - spec/support/user_with_any_code.rb
147
150
  - spec/support/user_with_validation.rb
148
151
  - spec/support/user_without_validation.rb