postal_code 0.0.1

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.
@@ -0,0 +1,78 @@
1
+ module PostalCode
2
+
3
+ VERSION = '0.0.1'
4
+
5
+ CityOffset = 0
6
+ StateOffset = 1
7
+ DB = File.join File.dirname(__FILE__), '..', 'db', 'us.csv'
8
+ FS = "\t" # field separator
9
+ RS = "\n" # record separator
10
+
11
+ @cache = {}
12
+
13
+ #
14
+ # Load the entire postal code database into memory.
15
+ #
16
+ def self.load_cache
17
+ open(DB) do |db|
18
+ db.each_line do |line|
19
+ pcode, city, state = line.rstrip.split(FS)
20
+ @cache[pcode] = [city, state]
21
+ end
22
+ end
23
+ end
24
+
25
+ #
26
+ # Clear the in-memory postal code cache.
27
+ #
28
+ def self.clear_cache
29
+ @cache.clear
30
+ end
31
+
32
+ #
33
+ # Return the city for the given postal code.
34
+ #
35
+ def self.city postal_code
36
+ fetch(postal_code)[CityOffset]
37
+ end
38
+
39
+ #
40
+ # Return the state (two-letter abbreviation) for the given postal code.
41
+ #
42
+ def self.state postal_code
43
+ fetch(postal_code)[StateOffset]
44
+ end
45
+
46
+ #
47
+ # * All postal code parameters must be strings.
48
+ # * Postal codes are not calculable, therefore should not be numeric.
49
+ # * A postal code with a leading zero would be an octal. However, *08880* is
50
+ # an invalid octal numeric, yet a valid postal code.
51
+ #
52
+ def self.valid_format? postal_code
53
+ postal_code =~ /^\d{5}(-\d{4})?$/
54
+ end
55
+
56
+ #
57
+ # Fetch postal code record.
58
+ #
59
+ def self.fetch postal_code
60
+ return [nil, nil] unless valid_format? postal_code
61
+ pcode = postal_code[0..4] # use first 5 characters; ignore ZIP+4 extension
62
+
63
+ if @cache.has_key? pcode
64
+ @cache[pcode]
65
+ else
66
+ records = %x[grep "^#{pcode}#{FS}" #{DB}].split(RS)
67
+
68
+ if records.empty?
69
+ [nil, nil]
70
+ else
71
+ @cache[pcode] = records.last.split(FS)[1..2]
72
+ end
73
+ end
74
+ end
75
+
76
+ private_class_method :fetch, :valid_format?
77
+
78
+ end
@@ -0,0 +1,56 @@
1
+ #
2
+ # Postal Code resolution service for network clients.
3
+ #
4
+ # Could implement a DRb and TCP/UDP server.
5
+ #
6
+ # Requires in user code would look like this:
7
+ #
8
+ # require 'postal_code/tcpserver'
9
+ #
10
+ # or
11
+ #
12
+ # require 'postal_code/drbserver'
13
+ #
14
+
15
+ __END__
16
+
17
+ require_relative '../postal_code'
18
+
19
+ # DRb
20
+ require 'drb'
21
+
22
+ $SAFE = 1 # disable eval() and friends
23
+ URI = "druby://127.0.0.1:8787"
24
+
25
+ class PostalCode::DRbServer
26
+
27
+ def initialize host, port
28
+ end
29
+
30
+ def postal_code code
31
+ # could return more than one line
32
+ #zip, city, state = %x[grep ^#{code} #{DB}].split("\t")
33
+ #Time.now
34
+ #"#{zip},#{city},#{state}"
35
+ code
36
+ end
37
+
38
+ end
39
+
40
+ DRb.start_service(URI, PCode.new)
41
+ DRb.thread.join
42
+
43
+
44
+ __END__
45
+
46
+ # TCP
47
+ require 'socket'
48
+
49
+ server = TCPServer.new('127.0.0.1', 5100)
50
+ loop do
51
+ client = server.accept
52
+ client.gets
53
+ client.puts 'Hello!'
54
+ client.puts "Time is #{Time.now}"
55
+ client.close
56
+ end
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new('postal_code') do |s|
2
+ s.version = File.read('lib/postal_code.rb')[/VERSION = '(.*)'/, 1]
3
+ s.author = 'Clint Pachl'
4
+ s.email = 'pachl@ecentryx.com'
5
+ s.homepage = 'http://ecentryx.com/gems/postal_code'
6
+ s.license = 'ISC'
7
+ s.summary = 'Postal Code Resolver'
8
+ s.description = 'Resolves a postal code to a corresponding city or state.'
9
+ s.files = Dir['README.rdoc', 'Rakefile', 'postal_code.gemspec',
10
+ 'lib/**/*.rb', 'test/**/*', 'db/*.csv']
11
+ s.test_files = Dir['test/*.rb']
12
+ end
@@ -0,0 +1,4 @@
1
+ 00001 City1 AA
2
+ 10000 City2 BA
3
+ 20000 City3 CA
4
+ 20000 City4 CA
@@ -0,0 +1,87 @@
1
+ gem 'minitest' # minitest in 1.9 stdlib is crufty
2
+ require 'minitest/autorun'
3
+ require 'postal_code'
4
+
5
+ class PostalCodeTest < Minitest::Test
6
+
7
+ ProductionDB = PostalCode::DB.dup
8
+ TestDB = File.dirname(__FILE__) + '/data/us.csv'
9
+ PostalCode::DB.replace(TestDB)
10
+
11
+ def test_database_format
12
+ [ProductionDB, TestDB].each do |db|
13
+ [ %x[head -n1 #{db}],
14
+ %x[tail -n1 #{db}]
15
+ ].each do |tuple|
16
+ assert_match(/^\d{5}\t.+\t[A-Z]{2}$/, tuple)
17
+ end
18
+ end
19
+ end
20
+
21
+ def test_postal_code_with_leading_zeros
22
+ pcode = '00001'
23
+ assert_equal 'City1', PostalCode.city(pcode)
24
+ assert_equal 'AA', PostalCode.state(pcode)
25
+ end
26
+
27
+ def test_postal_code_with_plus_4_extension
28
+ pcode = '00001-1234'
29
+ assert_equal 'City1', PostalCode.city(pcode)
30
+ assert_equal 'AA', PostalCode.state(pcode)
31
+ end
32
+
33
+ def test_postal_code_found_in_single_city
34
+ pcode = '10000'
35
+ assert_equal 'City2', PostalCode.city(pcode)
36
+ assert_equal 'BA', PostalCode.state(pcode)
37
+ end
38
+
39
+ def test_postal_code_found_in_multiple_cities
40
+ pcode = '20000'
41
+ assert_equal 'City4', PostalCode.city(pcode)
42
+ assert_equal 'CA', PostalCode.state(pcode)
43
+ end
44
+
45
+ def test_postal_code_not_found_in_any_city
46
+ pcode = '99999'
47
+ assert_equal nil, PostalCode.city(pcode)
48
+ assert_equal nil, PostalCode.state(pcode)
49
+ end
50
+
51
+ def test_reactive_cache
52
+ PostalCode.clear_cache
53
+ assert_equal 0, PostalCode.instance_variable_get(:@cache).size
54
+ PostalCode.city('00001') # hit
55
+ assert_equal 1, PostalCode.instance_variable_get(:@cache).size
56
+
57
+ PostalCode.clear_cache
58
+ assert_equal 0, PostalCode.instance_variable_get(:@cache).size
59
+ PostalCode.city('99999') # miss
60
+ assert_equal 0, PostalCode.instance_variable_get(:@cache).size
61
+ end
62
+
63
+ def test_proactive_cache
64
+ tuple_cnt = open(PostalCode::DB) {|db| db.readlines.size}
65
+ duplicate_cnt = 1
66
+ uniq_tuple_cnt = tuple_cnt - duplicate_cnt
67
+
68
+ assert_equal 4, tuple_cnt
69
+ PostalCode.clear_cache
70
+ assert_equal 0, PostalCode.instance_variable_get(:@cache).size
71
+ PostalCode.load_cache
72
+ assert_equal uniq_tuple_cnt, PostalCode.instance_variable_get(:@cache).size
73
+ end
74
+
75
+ def test_postal_code_validations
76
+ refute PostalCode.send(:valid_format?, '99999').nil?
77
+ refute PostalCode.send(:valid_format?, '00001').nil?
78
+ refute PostalCode.send(:valid_format?, '00001-1234').nil?
79
+ assert PostalCode.send(:valid_format?, '00001-123').nil?
80
+ assert PostalCode.send(:valid_format?, '00001-').nil?
81
+ assert PostalCode.send(:valid_format?, 99999).nil?
82
+ assert PostalCode.send(:valid_format?, 00001).nil?
83
+ assert PostalCode.send(:valid_format?, '1').nil?
84
+ assert PostalCode.send(:valid_format?, 1).nil?
85
+ end
86
+
87
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: postal_code
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Clint Pachl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-02 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Resolves a postal code to a corresponding city or state.
15
+ email: pachl@ecentryx.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.rdoc
21
+ - Rakefile
22
+ - postal_code.gemspec
23
+ - lib/postal_code/server.rb
24
+ - lib/postal_code.rb
25
+ - test/test_postal_code.rb
26
+ - test/data/us.csv
27
+ - db/us.csv
28
+ homepage: http://ecentryx.com/gems/postal_code
29
+ licenses:
30
+ - ISC
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 1.8.23
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Postal Code Resolver
53
+ test_files:
54
+ - test/test_postal_code.rb