postkode 0.3.0 → 0.4.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.
- 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
|