postkode 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/postkode.rb +43 -20
- data/lib/strrand.rb +311 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bca6338640f4dc4a6aedb862d8065b681e2bb6ca
|
4
|
+
data.tar.gz: 279c9e6c14171ee348bf6fb90abb0523dab4471b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf27be6284a05bef246e3851f0ab23592cae2082f603949bcfd27292a50753147402ac7e1d6689d25a3f60308a9537fba8ee43650d2d82c738ce879e95f1fd4d
|
7
|
+
data.tar.gz: 2e9304709f696fc3585ce2dd097d5275ea1d6dac9eeac785b3bb728a65b8242d0ee31222fb5291b6d5d393c25faf51c380d411a55324905ecb46aebbe2a5c190
|
data/lib/postkode.rb
CHANGED
@@ -1,30 +1,40 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require_relative 'strrand'
|
2
3
|
|
3
4
|
# Handles validation of UK-style postcodes
|
4
5
|
class Postkode
|
5
|
-
|
6
6
|
# Validating UK Postcodes
|
7
7
|
# Correct as at 2014-09-15
|
8
8
|
# References below
|
9
9
|
# https://www.gov.uk/government/uploads/system/uploads/attachment_data/file/283357/ILRSpecification2013_14Appendix_C_Dec2012_v1.pdf
|
10
10
|
# http://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation
|
11
|
-
# Areas with only single-digit districts: BR, FY, HA, HD, HG, HR, HS, HX, JE,
|
11
|
+
# Areas with only single-digit districts: BR, FY, HA, HD, HG, HR, HS, HX, JE,
|
12
|
+
# LD, SM, SR, WC, WN, ZE (although WC is always subdivided by a
|
13
|
+
# further letter, e.g. WC1A).
|
12
14
|
# Areas with only double-digit districts: AB, LL, SO.
|
13
|
-
# Areas with a district '0' (zero): BL, BS, CM, CR, FY, HA, PR, SL, SS (BS is
|
14
|
-
#
|
15
|
+
# Areas with a district '0' (zero): BL, BS, CM, CR, FY, HA, PR, SL, SS (BS is
|
16
|
+
# the only area to have both a district 0 and a district 10).
|
17
|
+
# The following central London single-digit districts have been further
|
18
|
+
# divided by inserting a letter after the digit and before the space:
|
19
|
+
# EC1-EC4 (but not EC50), SW1, W1, WC1, WC2, and part of E1 (E1W),
|
20
|
+
# N1 (N1C and N1P), NW1 (NW1W) and SE1 (SE1P).
|
15
21
|
# The letters QVX are not used in the first position.
|
16
22
|
# The letters IJZ are not used in the second position.
|
17
|
-
# The only letters to appear in the third position are ABCDEFGHJKPSTUW when
|
18
|
-
#
|
19
|
-
# The
|
20
|
-
#
|
23
|
+
# The only letters to appear in the third position are ABCDEFGHJKPSTUW when
|
24
|
+
# the structure starts with A9A.
|
25
|
+
# The only letters to appear in the fourth position are ABEHMNPRVWXY when
|
26
|
+
# the structure starts with AA9A.
|
27
|
+
# The final two letters do not use the letters CIKMOV, so as not to resemble
|
28
|
+
# digits or each other when hand-written.
|
29
|
+
# Post code sectors are one of ten digits: 0 to 9 with 0 only used once 9
|
30
|
+
# has been used in a post town, save for Croydon and Newport (above).
|
21
31
|
|
22
|
-
A1 =
|
23
|
-
A2 =
|
24
|
-
A3 =
|
25
|
-
A4 =
|
26
|
-
A5 =
|
27
|
-
N =
|
32
|
+
A1 = '[A-PR-UWYZ]'
|
33
|
+
A2 = '[A-HK-Y]'
|
34
|
+
A3 = '[A-HJKPS-UW]'
|
35
|
+
A4 = '[ABEHMNPRV-XY]'
|
36
|
+
A5 = '[ABD-HJLN-UW-Z]'
|
37
|
+
N = '[0-9]'
|
28
38
|
AANN = A1 + A2 + N + N # the six possible first-part combos
|
29
39
|
AANA = A1 + A2 + N + A4
|
30
40
|
ANA = A1 + N + A3
|
@@ -39,8 +49,8 @@ class Postkode
|
|
39
49
|
NORMAL_PART_POSTCODE_PATTERN = /[ ]*(#{PART_ONE})[ ]*/
|
40
50
|
NORMAL_POSTCODE_RE = Regexp.new(NORMAL_POSTCODE_VALID, Regexp::IGNORECASE)
|
41
51
|
|
42
|
-
def self.validate(string, return_parts=false)
|
43
|
-
return false if string.nil? || string ==
|
52
|
+
def self.validate(string, return_parts = false)
|
53
|
+
return false if string.nil? || string == '' || string == false
|
44
54
|
result = string.match(NORMAL_POSTCODE_RE)
|
45
55
|
return false if result.nil?
|
46
56
|
|
@@ -48,14 +58,14 @@ class Postkode
|
|
48
58
|
end
|
49
59
|
|
50
60
|
def self.get_first_section(string)
|
51
|
-
|
61
|
+
get_outcode(string)
|
52
62
|
end
|
53
63
|
|
54
64
|
def self.get_outcode(string)
|
55
65
|
found = Postkode.validate(string, true)
|
56
66
|
found ? found[0] : nil
|
57
67
|
end
|
58
|
-
|
68
|
+
|
59
69
|
def self.get_inward(string)
|
60
70
|
found = Postkode.validate(string, true)
|
61
71
|
found ? found[1] : nil
|
@@ -63,11 +73,24 @@ class Postkode
|
|
63
73
|
|
64
74
|
def self.find_in_string(string)
|
65
75
|
res = string.scan(NORMAL_POSTCODE_PATTERN)
|
66
|
-
res.length>0 ? res : nil
|
76
|
+
res.length > 0 ? res : nil
|
67
77
|
end
|
68
78
|
|
69
79
|
def self.find_partial_in_string(string)
|
70
80
|
res = string.scan(NORMAL_PART_POSTCODE_PATTERN)
|
71
|
-
res.length>0 ? res : nil
|
81
|
+
res.length > 0 ? res : nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.validate_and_normalize(string)
|
85
|
+
return nil unless validate(string)
|
86
|
+
validate(string, true).
|
87
|
+
map(&:to_s).
|
88
|
+
map(&:upcase).
|
89
|
+
join(" ")
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.random()
|
93
|
+
generator_pattern = "#{[AANN, AANA, ANA, ANN, AAN, AN].sample} #{PART_TWO}"
|
94
|
+
StringRandom.random_regex(generator_pattern)
|
72
95
|
end
|
73
96
|
end
|
data/lib/strrand.rb
ADDED
@@ -0,0 +1,311 @@
|
|
1
|
+
#
|
2
|
+
# = strrand.rb: Generates a random string from a pattern
|
3
|
+
#
|
4
|
+
# Author:: tama <repeatedly@gmail.com>
|
5
|
+
#
|
6
|
+
# StringRandom is derived from the String::Random written in Perl.
|
7
|
+
# See http://search.cpan.org/~steve/String-Random-0.22/
|
8
|
+
#
|
9
|
+
# == Example
|
10
|
+
#
|
11
|
+
# string_random = StringRandom.new
|
12
|
+
# string_random.random_pattern('CCcc!ccn') #=> ZIop$ab1
|
13
|
+
#
|
14
|
+
# refer to test/test_stringrandom.rb
|
15
|
+
#
|
16
|
+
# == Format
|
17
|
+
#
|
18
|
+
# === Regular expression syntax
|
19
|
+
#
|
20
|
+
# *_regex methods use this rule.
|
21
|
+
#
|
22
|
+
# The following regular expression elements are supported.
|
23
|
+
#
|
24
|
+
# [\w] Alphanumeric + "_".
|
25
|
+
# [\d] Digits.
|
26
|
+
# [\W] Printable characters other than those in \w.
|
27
|
+
# [\D] Printable characters other than those in \d.
|
28
|
+
# [.] Printable characters.
|
29
|
+
# [[]] Character classes.
|
30
|
+
# [{}] Repetition.
|
31
|
+
# [*] Same as {0,}.
|
32
|
+
# [+] Same as {1,}
|
33
|
+
# [?] Same as {0,1}.
|
34
|
+
#
|
35
|
+
# === Patterns
|
36
|
+
#
|
37
|
+
# random_pattern and random_string methods use this rule.
|
38
|
+
#
|
39
|
+
# The following patterns are pre-defined.
|
40
|
+
#
|
41
|
+
# [c] Any lowercase character [a-z]
|
42
|
+
# [C] Any uppercase character [A-Z]
|
43
|
+
# [n] Any digit [0-9]
|
44
|
+
# [!] A punctuation character [~`!@$%^&*()-_+={}[]|\:;"'.<>?/#,]
|
45
|
+
# [.] Any of the above
|
46
|
+
# [s] A "salt" character [A-Za-z0-9./]
|
47
|
+
# [b] Any binary data
|
48
|
+
#
|
49
|
+
# Pattern can modify and add as bellow.
|
50
|
+
#
|
51
|
+
# string_random['C'] = ['n']
|
52
|
+
# string_random['A'] = Array('A'..'Z') | Array('a'..'z')
|
53
|
+
#
|
54
|
+
# Pattern must be a flattened array that elements are one character.
|
55
|
+
# Other types cause undefined behavior(raise exception, success, etc...).
|
56
|
+
#
|
57
|
+
class StringRandom
|
58
|
+
Upper = Array('A'..'Z')
|
59
|
+
Lower = Array('a'..'z')
|
60
|
+
Digit = Array('0'..'9')
|
61
|
+
Punct = [33..47, 58..64, 91..96, 123..126].map { |r| r.map { |val| val.chr } }.flatten
|
62
|
+
Any = Upper | Lower | Digit | Punct
|
63
|
+
Salt = Upper | Lower | Digit | ['.', '/']
|
64
|
+
Binary = (0..255).map { |val| val.chr }
|
65
|
+
|
66
|
+
# These are the regex-based patterns.
|
67
|
+
Pattern = {
|
68
|
+
# These are the regex-equivalents.
|
69
|
+
'.' => Any,
|
70
|
+
'\d' => Digit,
|
71
|
+
'\D' => Upper | Lower | Punct,
|
72
|
+
'\w' => Upper | Lower | Digit | ['_'],
|
73
|
+
'\W' => Punct.reject { |val| val == '_' },
|
74
|
+
'\s' => [' ', "\t"],
|
75
|
+
'\S' => Upper | Lower | Digit | Punct,
|
76
|
+
|
77
|
+
# These are translated to their double quoted equivalents.
|
78
|
+
'\t' => ["\t"],
|
79
|
+
'\n' => ["\n"],
|
80
|
+
'\r' => ["\r"],
|
81
|
+
'\f' => ["\f"],
|
82
|
+
'\a' => ["\a"],
|
83
|
+
'\e' => ["\e"]
|
84
|
+
}
|
85
|
+
# These are the old patterns for random_pattern.
|
86
|
+
OldPattern = {
|
87
|
+
'C' => Upper,
|
88
|
+
'c' => Lower,
|
89
|
+
'n' => Digit,
|
90
|
+
'!' => Punct,
|
91
|
+
'.' => Any,
|
92
|
+
's' => Salt,
|
93
|
+
'b' => Binary
|
94
|
+
}
|
95
|
+
|
96
|
+
#
|
97
|
+
# Singleton method version of random_regex.
|
98
|
+
#
|
99
|
+
def self.random_regex(patterns)
|
100
|
+
StringRandom.new.random_regex(patterns)
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Same as StringRandom#random_pattern if single argument.
|
105
|
+
# Optionally, references to lists containing
|
106
|
+
# other patterns can be passed to the function.
|
107
|
+
# Those lists will be used for 0 through 9 in the pattern.
|
108
|
+
#
|
109
|
+
def self.random_string(pattern, *pattern_list)
|
110
|
+
string_random = StringRandom.new
|
111
|
+
|
112
|
+
pattern_list.each_with_index do |new_pattern, i|
|
113
|
+
string_random[i.to_s] = new_pattern
|
114
|
+
end
|
115
|
+
|
116
|
+
string_random.random_pattern(pattern)
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# _max_ is default length for creating random string
|
121
|
+
#
|
122
|
+
def initialize(max = 10)
|
123
|
+
@max = max
|
124
|
+
@map = OldPattern.clone
|
125
|
+
@regch = {
|
126
|
+
"\\" => method(:regch_slash),
|
127
|
+
'.' => method(:regch_dot),
|
128
|
+
'[' => method(:regch_bracket),
|
129
|
+
'*' => method(:regch_asterisk),
|
130
|
+
'+' => method(:regch_plus),
|
131
|
+
'?' => method(:regch_question),
|
132
|
+
'|' => method(:regch_pipe),
|
133
|
+
'{' => method(:regch_brace)
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Returns a random string that will match
|
139
|
+
# the regular expression passed in the list argument.
|
140
|
+
#
|
141
|
+
def random_regex(patterns)
|
142
|
+
return _random_regex(patterns) unless patterns.instance_of?(Array)
|
143
|
+
|
144
|
+
result = []
|
145
|
+
patterns.each do |pattern|
|
146
|
+
result << _random_regex(pattern)
|
147
|
+
end
|
148
|
+
result
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
# Returns a random string based on the concatenation
|
153
|
+
# of all the pattern strings in the list.
|
154
|
+
#
|
155
|
+
def random_pattern(patterns)
|
156
|
+
return _random_pattern(patterns) unless patterns.instance_of?(Array)
|
157
|
+
|
158
|
+
result = []
|
159
|
+
patterns.each do |pattern|
|
160
|
+
result << _random_pattern(pattern)
|
161
|
+
end
|
162
|
+
result
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Returns a random string pattern
|
167
|
+
#
|
168
|
+
def [](key)
|
169
|
+
@map[key]
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Adds a random string pattern
|
174
|
+
#
|
175
|
+
# _pattern_ must be flattened array
|
176
|
+
#
|
177
|
+
def []=(key, pattern)
|
178
|
+
@map[key] = pattern
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def _random_regex(pattern)
|
184
|
+
string = []
|
185
|
+
chars = pattern.split(//)
|
186
|
+
non_ch = /[\$\^\*\(\)\+\{\}\]\?]/ # not supported chars
|
187
|
+
|
188
|
+
while ch = chars.shift
|
189
|
+
if @regch.has_key?(ch)
|
190
|
+
@regch[ch].call(ch, chars, string)
|
191
|
+
else
|
192
|
+
warn "'#{ch}' not implemented. treating literally." if ch =~ non_ch
|
193
|
+
string << [ch]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
result = ''
|
198
|
+
string.each do |ch|
|
199
|
+
result << ch[rand(ch.size)]
|
200
|
+
end
|
201
|
+
result
|
202
|
+
end
|
203
|
+
|
204
|
+
def _random_pattern(pattern)
|
205
|
+
string = ''
|
206
|
+
|
207
|
+
pattern.split(//).each do |ch|
|
208
|
+
raise %Q(Unknown pattern character "#{ch}"!) unless @map.has_key?(ch)
|
209
|
+
string << @map[ch][rand(@map[ch].size)]
|
210
|
+
end
|
211
|
+
|
212
|
+
string
|
213
|
+
end
|
214
|
+
|
215
|
+
#-
|
216
|
+
# The folloing methods are defined for regch.
|
217
|
+
# These characters are treated specially in random_regex.
|
218
|
+
#+
|
219
|
+
|
220
|
+
def regch_slash(ch, chars, string)
|
221
|
+
raise 'regex not terminated' if chars.empty?
|
222
|
+
|
223
|
+
tmp = chars.shift
|
224
|
+
if tmp == 'x'
|
225
|
+
# This is supposed to be a number in hex, so
|
226
|
+
# there had better be at least 2 characters left.
|
227
|
+
tmp = chars.shift + chars.shift
|
228
|
+
string << tmp.hex.chr
|
229
|
+
elsif tmp =~ /[0-7]/
|
230
|
+
warn 'octal parsing not implemented. treating literally.'
|
231
|
+
string << tmp
|
232
|
+
elsif Pattern.has_key?(ch + tmp)
|
233
|
+
string << Pattern[ch + tmp]
|
234
|
+
else
|
235
|
+
warn "'\\#{tmp}' being treated as literal '#{tmp}'"
|
236
|
+
string << tmp
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def regch_dot(ch, chars, string)
|
241
|
+
string << Pattern[ch]
|
242
|
+
end
|
243
|
+
|
244
|
+
def regch_bracket(ch, chars, string)
|
245
|
+
tmp = []
|
246
|
+
|
247
|
+
while ch = chars.shift and ch != ']'
|
248
|
+
if ch == '-' and !chars.empty? and !tmp.empty?
|
249
|
+
max = chars.shift
|
250
|
+
min = tmp.last
|
251
|
+
tmp << min = min.succ while min < max
|
252
|
+
else
|
253
|
+
warn "${ch}' will be treated literally inside []" if ch =~ /\W/
|
254
|
+
tmp << ch
|
255
|
+
end
|
256
|
+
end
|
257
|
+
raise 'unmatched []' if ch != ']'
|
258
|
+
|
259
|
+
string << tmp
|
260
|
+
end
|
261
|
+
|
262
|
+
def regch_asterisk(ch, chars, string)
|
263
|
+
chars = '{0,}'.split('').concat(chars)
|
264
|
+
end
|
265
|
+
|
266
|
+
def regch_pipe(ch, chars, string)
|
267
|
+
puts("CHARS: #{chars.join('/')}")
|
268
|
+
puts("STRING: #{string.join('/')}")
|
269
|
+
puts
|
270
|
+
puts
|
271
|
+
chars = ""
|
272
|
+
end
|
273
|
+
|
274
|
+
def regch_plus(ch, chars, string)
|
275
|
+
chars = '{1,}'.split('').concat(chars)
|
276
|
+
end
|
277
|
+
|
278
|
+
def regch_question(ch, chars, string)
|
279
|
+
chars = '{0,1}'.split('').concat(chars)
|
280
|
+
end
|
281
|
+
|
282
|
+
def regch_brace(ch, chars, string)
|
283
|
+
# { isn't closed, so treat it literally.
|
284
|
+
return string << ch unless chars.include?('}')
|
285
|
+
|
286
|
+
tmp = ''
|
287
|
+
while ch = chars.shift and ch != '}'
|
288
|
+
raise "'#{ch}' inside {} not supported" unless ch =~ /[\d,]/
|
289
|
+
tmp << ch
|
290
|
+
end
|
291
|
+
|
292
|
+
tmp = if tmp =~ /,/
|
293
|
+
raise "malformed range {#{tmp}}" unless tmp =~ /^(\d*),(\d*)$/
|
294
|
+
|
295
|
+
min = $1.length.nonzero? ? $1.to_i : 0
|
296
|
+
max = $2.length.nonzero? ? $2.to_i : @max
|
297
|
+
raise "bad range {#{tmp}}" if min > max
|
298
|
+
|
299
|
+
min == max ? min : min + rand(max - min + 1)
|
300
|
+
else
|
301
|
+
tmp.to_i
|
302
|
+
end
|
303
|
+
|
304
|
+
if tmp.nonzero?
|
305
|
+
last = string.last
|
306
|
+
(tmp - 1).times { string << last }
|
307
|
+
else
|
308
|
+
string.pop
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postkode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- K M Lawrence
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2015-03-28 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: Postcode validation module
|
16
16
|
email: keith.lawrence@upbeatproductions.com
|
@@ -19,6 +19,7 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- lib/postkode.rb
|
22
|
+
- lib/strrand.rb
|
22
23
|
homepage: http://rubygems.org/gems/postkode
|
23
24
|
licenses:
|
24
25
|
- MIT
|
@@ -39,7 +40,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
40
|
version: '0'
|
40
41
|
requirements: []
|
41
42
|
rubyforge_project:
|
42
|
-
rubygems_version: 2.4.
|
43
|
+
rubygems_version: 2.4.5
|
43
44
|
signing_key:
|
44
45
|
specification_version: 4
|
45
46
|
summary: postkode
|