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