postkode 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/postkode.rb +43 -20
  3. data/lib/strrand.rb +311 -0
  4. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2fd1287a7b3abfa42ef8bc62d07106dd7190ec66
4
- data.tar.gz: 441a7a4b106ec9b10e3612f88a2a95234feaa63a
3
+ metadata.gz: bca6338640f4dc4a6aedb862d8065b681e2bb6ca
4
+ data.tar.gz: 279c9e6c14171ee348bf6fb90abb0523dab4471b
5
5
  SHA512:
6
- metadata.gz: 31c53d909ff7b2388d19d46980ab95e5a775d0693850cc720f50b89f935a3257d72fe33611db3d81311d110d5baa2e7f59a7b3724483b928552110d8083c05b1
7
- data.tar.gz: edaf91965c9454e5997234fdc928eb216974e8104d86730de38d78297ef12db083af602c75b764c2e961316b7cfdedc03ce46b48659f89d9d006891a5680fccf
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, LD, SM, SR, WC, WN, ZE (although WC is always subdivided by a further letter, e.g. WC1A).
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 the only area to have both a district 0 and a district 10).
14
- # The following central London single-digit districts have been further divided by inserting a letter after the digit and before the space: EC1–EC4 (but not EC50), SW1, W1, WC1, WC2, and part of E1 (E1W), N1 (N1C and N1P), NW1 (NW1W) and SE1 (SE1P).
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 the structure starts with A9A.
18
- # The only letters to appear in the fourth position are ABEHMNPRVWXY when the structure starts with AA9A.
19
- # The final two letters do not use the letters CIKMOV, so as not to resemble digits or each other when hand-written.
20
- # Post code sectors are one of ten digits: 0 to 9 with 0 only used once 9 has been used in a post town, save for Croydon and Newport (see above).
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 = "[A-PR-UWYZ]"
23
- A2 = "[A-HK-Y]"
24
- A3 = "[A-HJKPS-UW]"
25
- A4 = "[ABEHMNPRV-XY]"
26
- A5 = "[ABD-HJLN-UW-Z]"
27
- N = "[0-9]"
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 == "" || string == false
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
- self.get_outcode(string)
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.3.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: 2014-11-06 00:00:00.000000000 Z
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.2
43
+ rubygems_version: 2.4.5
43
44
  signing_key:
44
45
  specification_version: 4
45
46
  summary: postkode