ziplookup 1.0.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.
- data/.project +17 -0
- data/Manifest.txt +9 -0
- data/Rakefile +28 -0
- data/bin/tryme.rb +28 -0
- data/lib/USPS/address.rb +356 -0
- data/lib/USPS/zip_lookup.rb +424 -0
- data/test/tc_address.rb +26 -0
- data/test/tc_standardize.rb +94 -0
- data/test/ts_zip_lookup.rb +8 -0
- metadata +49 -0
data/.project
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<projectDescription>
|
3
|
+
<name>ZipLookup</name>
|
4
|
+
<comment></comment>
|
5
|
+
<projects>
|
6
|
+
</projects>
|
7
|
+
<buildSpec>
|
8
|
+
<buildCommand>
|
9
|
+
<name>org.rubypeople.rdt.core.rubybuilder</name>
|
10
|
+
<arguments>
|
11
|
+
</arguments>
|
12
|
+
</buildCommand>
|
13
|
+
</buildSpec>
|
14
|
+
<natures>
|
15
|
+
<nature>org.rubypeople.rdt.core.rubynature</nature>
|
16
|
+
</natures>
|
17
|
+
</projectDescription>
|
data/Manifest.txt
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
spec = Gem::Specification.new do |s|
|
5
|
+
s.name = 'ziplookup'
|
6
|
+
s.version = '1.0.0'
|
7
|
+
s.summary = 'Classes for standardizing addresses via the USPS web site.'
|
8
|
+
s.description = 'Classes for standardizing addresses via the USPS web site.'
|
9
|
+
s.author = 'Gregor N. Purdy'
|
10
|
+
s.email = 'gregor@focusresearch.com'
|
11
|
+
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.files = File.read('Manifest.txt').split($/)
|
14
|
+
s.require_path = 'lib' # library files go here
|
15
|
+
s.executables = ['tryme.rb'] # must be in bin/
|
16
|
+
end
|
17
|
+
|
18
|
+
#desc 'Generate RDoc'
|
19
|
+
#Rake::RDocTask.new :rdoc do |rd|
|
20
|
+
# rd.rdoc_dir = 'doc'
|
21
|
+
# rd.rdoc_files.add 'lib', 'README', 'LICENSE' # add those files to your RDoc output
|
22
|
+
# rd.main = 'README' # the main page for the RDoc
|
23
|
+
#end
|
24
|
+
|
25
|
+
desc 'Build Gem'
|
26
|
+
Rake::GemPackageTask.new spec do |pkg|
|
27
|
+
pkg.need_tar = true
|
28
|
+
end
|
data/bin/tryme.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "USPS/zip_lookup"
|
2
|
+
require "USPS/address"
|
3
|
+
|
4
|
+
delivery_address = "6216 Eddington Drive"
|
5
|
+
city = "Middletown"
|
6
|
+
state = "OH"
|
7
|
+
|
8
|
+
address = USPS::Address.new
|
9
|
+
address.delivery_address = delivery_address
|
10
|
+
address.city = city
|
11
|
+
address.state = state
|
12
|
+
|
13
|
+
zlu = USPS::ZipLookup.new()
|
14
|
+
#zlu.verbose = true
|
15
|
+
|
16
|
+
matches = zlu.std_addr(address)
|
17
|
+
|
18
|
+
if matches != nil && matches.size > 0
|
19
|
+
printf "\n%d matches:\n", matches.size
|
20
|
+
matches.each { |match|
|
21
|
+
print "-" * 39, "\n"
|
22
|
+
print match.to_dump
|
23
|
+
print "\n"
|
24
|
+
}
|
25
|
+
print "-" * 39, "\n"
|
26
|
+
else
|
27
|
+
print "No matches!\n"
|
28
|
+
end
|
data/lib/USPS/address.rb
ADDED
@@ -0,0 +1,356 @@
|
|
1
|
+
=begin
|
2
|
+
= address.rb
|
3
|
+
Copyright (C) 2006 Gregor N. Purdy. All rights reserved.
|
4
|
+
|
5
|
+
This program is free software. It is subject to the same license as Ruby.
|
6
|
+
|
7
|
+
= Classes
|
8
|
+
* ((<Address>))
|
9
|
+
|
10
|
+
= Address
|
11
|
+
|
12
|
+
== Synopsis
|
13
|
+
require "USPS/ZipLookup"
|
14
|
+
require "USPS/Address"
|
15
|
+
|
16
|
+
address = USPS::Address.new(delivery_address, city, state)
|
17
|
+
|
18
|
+
zlu = USPS::ZipLookup.new()
|
19
|
+
|
20
|
+
matches = zlu.std_addr(address)
|
21
|
+
|
22
|
+
if matches.size > 0
|
23
|
+
printf "\n%d matches:\n", matches.size
|
24
|
+
matches.each { |match|
|
25
|
+
# print "-" x 39, "\n"
|
26
|
+
print match.to_dump
|
27
|
+
print "\n";
|
28
|
+
}
|
29
|
+
# print "-" x 39, "\n"
|
30
|
+
else
|
31
|
+
print "No matches!\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
== Description
|
35
|
+
|
36
|
+
Results from USPS::ZipLookup calls are of this type.
|
37
|
+
|
38
|
+
Class to represent U.S. postal addresses for the purpose of
|
39
|
+
standardizing via the U.S. Postal Service's web site:
|
40
|
+
|
41
|
+
http://www.usps.com/zip4/
|
42
|
+
|
43
|
+
BE SURE TO READ AND UNDERSTAND THE TERMS OF USE SECTION IN THE
|
44
|
+
DOCUMENTATION, WHICH MAY BE FOUND AT THE END OF THIS SOURCE CODE.
|
45
|
+
|
46
|
+
== Class Methods
|
47
|
+
|
48
|
+
== Instance Methods
|
49
|
+
--- Address#dump()
|
50
|
+
--- Address#firm()
|
51
|
+
--- Address#urbanization()
|
52
|
+
--- Address#delivery_address()
|
53
|
+
--- Address#city()
|
54
|
+
--- Address#state()
|
55
|
+
--- Address#zip_code()
|
56
|
+
--- Address#carrier_route()
|
57
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
58
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
59
|
+
--- Address#county()
|
60
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
61
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
62
|
+
--- Address#delivery_point()
|
63
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
64
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
65
|
+
--- Address#check_digit()
|
66
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
67
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
68
|
+
--- Address#lac_indicator()
|
69
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
70
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
71
|
+
--- Address#elot_sequence()
|
72
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
73
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
74
|
+
--- Address#elot_indicator()
|
75
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
76
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
77
|
+
--- Address#record_type()
|
78
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
79
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
80
|
+
--- Address#pmb_designator()
|
81
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
82
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
83
|
+
--- Address#pmb_number()
|
84
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
85
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
86
|
+
--- Address#default_address()
|
87
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
88
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
89
|
+
--- Address#early_warning()
|
90
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
91
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
92
|
+
--- Address#valid()
|
93
|
+
Detailed information (see the U.S. Postal Service web site for definitions at
|
94
|
+
L<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>):
|
95
|
+
|
96
|
+
= History
|
97
|
+
$Id: address.rb 2255 2006-08-07 05:39:57Z gregor $
|
98
|
+
|
99
|
+
= TERMS OF USE
|
100
|
+
|
101
|
+
BE SURE TO READ AND FOLLOW THE UNITED STATES POSTAL SERVICE TERMS OF USE
|
102
|
+
PAGE AT C<http://www.usps.gov/disclaimer.html>. IN PARTICULAR, NOTE THAT THEY
|
103
|
+
DO NOT PERMIT THE USE OF THEIR WEB SITE'S FUNCTIONALITY FOR COMMERCIAL
|
104
|
+
PURPOSES. DO NOT USE THIS CODE IN A WAY THAT VIOLATES THE TERMS OF USE.
|
105
|
+
|
106
|
+
The author believes that the example usage given above does not violate
|
107
|
+
these terms, but sole responsibility for conforming to the terms of use
|
108
|
+
belongs to the user of this code, not the author.
|
109
|
+
|
110
|
+
= Author
|
111
|
+
|
112
|
+
Gregor N. Purdy, C<gregor@focusresearch.com>.
|
113
|
+
|
114
|
+
= Copyright
|
115
|
+
|
116
|
+
Copyright (C) 2006 Gregor N. Purdy. All rights reserved.
|
117
|
+
This program is free software; you can redistribute it and/or
|
118
|
+
modify it under the same terms as Ruby itself.
|
119
|
+
|
120
|
+
=end
|
121
|
+
|
122
|
+
module USPS
|
123
|
+
|
124
|
+
class Address
|
125
|
+
|
126
|
+
@firm = nil
|
127
|
+
@urbanization = nil
|
128
|
+
@delivery_address = nil
|
129
|
+
@city = nil
|
130
|
+
@state = nil
|
131
|
+
@zip_code = nil
|
132
|
+
|
133
|
+
@carrier_route = nil
|
134
|
+
@county = nil
|
135
|
+
@delivery_point = nil
|
136
|
+
@check_digit = nil
|
137
|
+
@lac_indicator = nil
|
138
|
+
@elot_sequence = nil
|
139
|
+
@elot_indicator = nil
|
140
|
+
@record_type = nil
|
141
|
+
@pmb_designator = nil
|
142
|
+
@pmb_number = nil
|
143
|
+
@default_address = nil
|
144
|
+
@early_warning = nil
|
145
|
+
@valid = nil
|
146
|
+
|
147
|
+
attr_reader :firm, :urbanization, :delivery_address, :city, :state, :zip_code,
|
148
|
+
:carrier_route, :county, :delivery_point, :check_digit, :lac_indicator, :elot_sequence,
|
149
|
+
:elot_indicator, :record_type, :pmb_designator, :pmb_number, :default_address, :early_warning,
|
150
|
+
:valid
|
151
|
+
|
152
|
+
attr_writer :firm, :urbanization, :delivery_address, :city, :state, :zip_code,
|
153
|
+
:carrier_route, :county, :delivery_point, :check_digit, :lac_indicator, :elot_sequence,
|
154
|
+
:elot_indicator, :record_type, :pmb_designator, :pmb_number, :default_address, :early_warning,
|
155
|
+
:valid
|
156
|
+
|
157
|
+
@@input_fields = {
|
158
|
+
'Firm' => :firm,
|
159
|
+
'Urbanization' => :urbanization,
|
160
|
+
'Delivery Address' => :delivery_address,
|
161
|
+
'City' => :city,
|
162
|
+
'State' => :state,
|
163
|
+
'Zip Code' => :zip_code,
|
164
|
+
}
|
165
|
+
|
166
|
+
@@output_fields = {
|
167
|
+
'Carrier Route' => :carrier_route,
|
168
|
+
'County' => :county,
|
169
|
+
'Delivery Point' => :delivery_point,
|
170
|
+
'Check Digit' => :check_digit,
|
171
|
+
'LAC Indicator' => :lac_indicator,
|
172
|
+
'eLOT Sequence' => :elot_sequence,
|
173
|
+
'eLOT Indicator' => :elot_indicator,
|
174
|
+
'Record Type' => :record_type,
|
175
|
+
'PMB Designator' => :pmb_designator,
|
176
|
+
'PMB Number' => :pmb_number,
|
177
|
+
'Default Address' => :default_address,
|
178
|
+
'Early Warning' => :early_warning,
|
179
|
+
'Valid' => :valid,
|
180
|
+
}
|
181
|
+
|
182
|
+
@@field_order = [
|
183
|
+
"Firm",
|
184
|
+
"Urbanization",
|
185
|
+
"Delivery Address",
|
186
|
+
"City",
|
187
|
+
"State",
|
188
|
+
"Zip Code",
|
189
|
+
"Carrier Route",
|
190
|
+
"County",
|
191
|
+
"Delivery Point",
|
192
|
+
"Check Digit",
|
193
|
+
"LAC Indicator",
|
194
|
+
"eLot Sequence",
|
195
|
+
"eLot Indicator",
|
196
|
+
"Record Type",
|
197
|
+
"PMB Designator",
|
198
|
+
"PMB Number",
|
199
|
+
"Default Address",
|
200
|
+
"Early Warning",
|
201
|
+
"Valid",
|
202
|
+
|
203
|
+
]
|
204
|
+
|
205
|
+
#
|
206
|
+
# initialize()
|
207
|
+
#
|
208
|
+
#
|
209
|
+
#def initialize(firm, urbanization, delivery_address, city, state, zip_code)
|
210
|
+
# @firm = firm
|
211
|
+
# @urbanization = urbanization
|
212
|
+
# @delivery_address = delivery_address
|
213
|
+
# @city = city
|
214
|
+
# @state = state
|
215
|
+
# @zip_code = zip_code
|
216
|
+
#end
|
217
|
+
#
|
218
|
+
#def initialize(firm, delivery_address, city, state, zip_code)
|
219
|
+
# @firm = firm
|
220
|
+
# @delivery_address = delivery_address
|
221
|
+
# @city = city
|
222
|
+
# @state = state
|
223
|
+
# @zip_code = zip_code
|
224
|
+
#end
|
225
|
+
#
|
226
|
+
#def initialize(delivery_address, city, state, zip_code)
|
227
|
+
# @delivery_address = delivery_address
|
228
|
+
# @city = city
|
229
|
+
# @state = state
|
230
|
+
# @zip_code = zip_code
|
231
|
+
#end
|
232
|
+
#
|
233
|
+
#def initialize(delivery_address, city, state)
|
234
|
+
# @delivery_address = delivery_address
|
235
|
+
# @city = city
|
236
|
+
# @state = state
|
237
|
+
#end
|
238
|
+
#
|
239
|
+
#def initialize(delivery_address, zip_code)
|
240
|
+
# @delivery_address = delivery_address
|
241
|
+
# @zip_code = zip_code
|
242
|
+
#end
|
243
|
+
|
244
|
+
#
|
245
|
+
# fields()
|
246
|
+
#
|
247
|
+
|
248
|
+
def fields
|
249
|
+
return {
|
250
|
+
"Firm" => @firm,
|
251
|
+
"Urbanization" => @urbanization,
|
252
|
+
"Delivery Address" => @delivery_address,
|
253
|
+
"City" => @city,
|
254
|
+
"State" => @state,
|
255
|
+
"Zip Code" => @zip_code,
|
256
|
+
"Carrier Route" => @carrier_route,
|
257
|
+
"County" => @county,
|
258
|
+
"Delivery Point" => @delivery_point,
|
259
|
+
"Check Digit" => @check_digit,
|
260
|
+
"LAC Indicator" => @lac_indicator,
|
261
|
+
"eLot Sequence" => @elot_sequence,
|
262
|
+
"eLot Indicator" => @elot_indicator,
|
263
|
+
"Record Type" => @record_type,
|
264
|
+
"PMB Designator" => @pmb_designator,
|
265
|
+
"PMB Number" => @pmb_number,
|
266
|
+
"Default Address" => @default_address,
|
267
|
+
"Early Warning" => @early_warning,
|
268
|
+
"Valid" => @valid,
|
269
|
+
}
|
270
|
+
end
|
271
|
+
|
272
|
+
#
|
273
|
+
# to_s()
|
274
|
+
#
|
275
|
+
|
276
|
+
def to_s(message = nil)
|
277
|
+
output = ''
|
278
|
+
|
279
|
+
if message != nil
|
280
|
+
output = sprintf "ADDRESS: %s\n", message
|
281
|
+
end
|
282
|
+
|
283
|
+
temp = fields
|
284
|
+
|
285
|
+
@@field_order.each { |key|
|
286
|
+
value = temp[key]
|
287
|
+
next if value == nil
|
288
|
+
line = sprintf " %s => '%s'\n", key, value
|
289
|
+
output += line
|
290
|
+
}
|
291
|
+
|
292
|
+
output += "\n"
|
293
|
+
|
294
|
+
return output
|
295
|
+
end
|
296
|
+
|
297
|
+
#
|
298
|
+
# dump()
|
299
|
+
#
|
300
|
+
|
301
|
+
def dump(message = nil)
|
302
|
+
print to_s(message)
|
303
|
+
end
|
304
|
+
|
305
|
+
#
|
306
|
+
# to_dump()
|
307
|
+
#
|
308
|
+
|
309
|
+
def to_dump
|
310
|
+
return to_s()
|
311
|
+
end
|
312
|
+
|
313
|
+
def query_string
|
314
|
+
require 'cgi'
|
315
|
+
|
316
|
+
args = [
|
317
|
+
[ 'visited' , '1' ], # NOTE: CGI.escape pukes if this doesn't have quotes. It isn't smart enough to convert to String on its own!
|
318
|
+
[ 'pagenumber' , 'all' ],
|
319
|
+
[ 'firmname' , '' ],
|
320
|
+
[ 'address1' , @delivery_address.upcase ],
|
321
|
+
# [ 'address1' , '' ],
|
322
|
+
# [ 'address2' , addr.delivery_address.upcase ],
|
323
|
+
[ 'address2' , '' ],
|
324
|
+
[ 'city' , @city.upcase ],
|
325
|
+
[ 'state' , @state.upcase ],
|
326
|
+
[ 'urbanization' , '' ],
|
327
|
+
[ 'zip5' , @zip_code == nil ? '' : @zip_code.upcase ],
|
328
|
+
]
|
329
|
+
|
330
|
+
result = ''
|
331
|
+
|
332
|
+
args.each { |arg|
|
333
|
+
key = arg[0]
|
334
|
+
value = arg[1]
|
335
|
+
|
336
|
+
if value == nil
|
337
|
+
raise "Value is nil for key '" + key + "'!!!"
|
338
|
+
end
|
339
|
+
|
340
|
+
value = CGI.escape(arg[1])
|
341
|
+
|
342
|
+
temp = sprintf("%s=%s", key, value)
|
343
|
+
|
344
|
+
if result != ''
|
345
|
+
result += "&"
|
346
|
+
end
|
347
|
+
|
348
|
+
result += temp
|
349
|
+
}
|
350
|
+
|
351
|
+
return result
|
352
|
+
end
|
353
|
+
|
354
|
+
end # class Address
|
355
|
+
|
356
|
+
end # module USPS
|
@@ -0,0 +1,424 @@
|
|
1
|
+
=begin
|
2
|
+
= zip_lookup.rb
|
3
|
+
Copyright (C) 2006 Gregor N. Purdy. All rights reserved.
|
4
|
+
|
5
|
+
This program is free software. It is subject to the same license as Ruby.
|
6
|
+
|
7
|
+
= Classes
|
8
|
+
* ((<Address>))
|
9
|
+
|
10
|
+
= ZipLookup
|
11
|
+
|
12
|
+
== Synopsis
|
13
|
+
|
14
|
+
Ruby class to standardize U.S. postal addresses by referencing
|
15
|
+
the U.S. Postal Service's web site:
|
16
|
+
|
17
|
+
http://www.usps.com/zip4/
|
18
|
+
|
19
|
+
#! ruby
|
20
|
+
require "USPS/zip_lookup"
|
21
|
+
require "USPS/address"
|
22
|
+
|
23
|
+
delivery_address = "6216 Eddington Drive"
|
24
|
+
city = "Middletown"
|
25
|
+
state = "OH"
|
26
|
+
|
27
|
+
address = USPS::Address.new
|
28
|
+
address.delivery_address = delivery_address
|
29
|
+
address.city = city
|
30
|
+
address.state = state
|
31
|
+
|
32
|
+
zlu = USPS::ZipLookup.new()
|
33
|
+
|
34
|
+
matches = zlu.std_addr(address)
|
35
|
+
|
36
|
+
if matches != nil && matches.size > 0
|
37
|
+
printf "\n%d matches:\n", matches.size
|
38
|
+
matches.each { |match|
|
39
|
+
print "-" * 39, "\n"
|
40
|
+
print match.to_dump
|
41
|
+
print "\n"
|
42
|
+
}
|
43
|
+
print "-" * 39, "\n"
|
44
|
+
else
|
45
|
+
print "No matches!\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
== Description
|
49
|
+
|
50
|
+
The United States Postal Service (USPS) has on its web site an HTML form at
|
51
|
+
C<http://www.usps.com/zip4/>
|
52
|
+
for standardizing an address. Given a firm, urbanization, street address,
|
53
|
+
city, state, and zip, it will put the address into standard form (provided
|
54
|
+
the address is in their database) and display a page with the resulting
|
55
|
+
address.
|
56
|
+
|
57
|
+
This Ruby module provides a programmatic interface to this service, so you
|
58
|
+
can write a program to process your entire personal address book without
|
59
|
+
having to manually type them all in to the form.
|
60
|
+
|
61
|
+
Because the USPS could change or remove this functionality at any time,
|
62
|
+
be prepared for the possibility that this code may fail to function. In
|
63
|
+
fact, as of this version, there is no error checking in place, so if they
|
64
|
+
do change things, this code will most likely fail in a noisy way. If you
|
65
|
+
discover that the service has changed, please email the author your findings.
|
66
|
+
|
67
|
+
If an error occurs in trying to standardize the address, then no array
|
68
|
+
will be returned. Otherwise, a four-element array will be returned.
|
69
|
+
|
70
|
+
To see debugging output, set zlu.verbose = true
|
71
|
+
|
72
|
+
|
73
|
+
== Fields
|
74
|
+
|
75
|
+
This page at the U.S. Postal Service web site contains definitions of some
|
76
|
+
of the fields: C<http://zip4.usps.com/zip4/pu_mailing_industry_def.htm>
|
77
|
+
|
78
|
+
|
79
|
+
= TERMS OF USE
|
80
|
+
|
81
|
+
BE SURE TO READ AND FOLLOW THE UNITED STATES POSTAL SERVICE TERMS OF USE PAGE
|
82
|
+
(AT C<http://www.usps.com/homearea/docs/termsofuse.htm> AT THE TIME THIS TEXT
|
83
|
+
WAS WRITTEN). IN PARTICULAR, NOTE THAT THEY DO NOT PERMIT THE USE OF THEIR WEB
|
84
|
+
SITE'S FUNCTIONALITY FOR COMMERCIAL PURPOSES. DO NOT USE THIS CODE IN A WAY
|
85
|
+
THAT VIOLATES THE TERMS OF USE.
|
86
|
+
|
87
|
+
As the user of this code, you are responsible for complying with the most
|
88
|
+
recent version of the Terms of Use, whether at the URL provided above or
|
89
|
+
elsewhere if the U.S. Postal Service moves it or updates it. As a convenience,
|
90
|
+
here is a copy of the most relevant paragraph of the Terms of Use as of
|
91
|
+
2006-07-04:
|
92
|
+
|
93
|
+
Material on this site is the copyrighted property of the United States
|
94
|
+
Postal Service� (Postal Service�). All rights reserved. The information
|
95
|
+
and images presented here may not under any circumstances be reproduced
|
96
|
+
or used without prior written permission. Users may view and download
|
97
|
+
material from this site only for the following purposes: (a) for personal,
|
98
|
+
non-commercial home use; (b) where the materials clearly state that these
|
99
|
+
materials may be copied and reproduced according to the terms stated in
|
100
|
+
those particular pages; or (c) with the express written permission of the
|
101
|
+
Postal Service. In all other cases, you will need written permission from
|
102
|
+
the Postal Service to reproduce, republish, upload, post, transmit,
|
103
|
+
distribute or publicly display material from this Web site. Users agree not
|
104
|
+
to use the site for sale, trade or other commercial purposes. Users may not
|
105
|
+
use language that is threatening, abusive, vulgar, discourteous or criminal.
|
106
|
+
Users also may not post or transmit information or materials that would
|
107
|
+
violate rights of any third party or which contains a virus or other harmful
|
108
|
+
component. The Postal Service reserves the right to remove or edit any
|
109
|
+
messages or material submitted by users.
|
110
|
+
|
111
|
+
The author believes that the example usage given above does not violate
|
112
|
+
these terms, but sole responsibility for conforming to the terms of use
|
113
|
+
belongs to the user of this code, not the author.
|
114
|
+
|
115
|
+
|
116
|
+
= BUG REPORTS
|
117
|
+
|
118
|
+
When contacting the author with bug reports, please provide a test address that
|
119
|
+
exhibits the problem, and make sure it is OK to add that address to the test
|
120
|
+
suite.
|
121
|
+
|
122
|
+
Be sure to let me know if you don't want me to mention your name or email
|
123
|
+
address when I document the changes and contributions to the release. Typically
|
124
|
+
I put this information in the CHANGES file.
|
125
|
+
|
126
|
+
= History
|
127
|
+
$Id: zip_lookup.rb 2255 2006-08-07 05:39:57Z gregor $
|
128
|
+
|
129
|
+
= Author
|
130
|
+
|
131
|
+
Gregor N. Purdy, C<gregor@focusresearch.com>.
|
132
|
+
|
133
|
+
= Copyright
|
134
|
+
|
135
|
+
Copyright (C) 2006 Gregor N. Purdy. All rights reserved.
|
136
|
+
This program is free software; you can redistribute it and/or
|
137
|
+
modify it under the same terms as Ruby itself.
|
138
|
+
|
139
|
+
=end
|
140
|
+
|
141
|
+
#require 'mechanize'
|
142
|
+
|
143
|
+
module USPS
|
144
|
+
|
145
|
+
class ZipLookup
|
146
|
+
# @@VERSION = '1.0'
|
147
|
+
@@usps_host = 'zip4.usps.com'
|
148
|
+
|
149
|
+
@@start_path = '/zip4/welcome.jsp'
|
150
|
+
@@start_url = sprintf('http://%s%s', @@usps_host, @@start_path)
|
151
|
+
|
152
|
+
@@post_path = '/zip4/zcl_0_results.jsp'
|
153
|
+
@@post_url = sprintf('http://%s%s', @@usps_host, @@post_path)
|
154
|
+
|
155
|
+
# @@form_name = 'form1'
|
156
|
+
@@form_number = 0
|
157
|
+
|
158
|
+
attr_reader :user_agent, :verbose
|
159
|
+
attr_writer :verbose
|
160
|
+
|
161
|
+
def initialize
|
162
|
+
# @user_agent = WWW::Mechanize.new
|
163
|
+
@verbose = false
|
164
|
+
end
|
165
|
+
|
166
|
+
def std_addr(address)
|
167
|
+
return std_inner(address)
|
168
|
+
end
|
169
|
+
|
170
|
+
def std_addrs(addresses)
|
171
|
+
result = [ ]
|
172
|
+
|
173
|
+
addresses.each { |address|
|
174
|
+
temp = std_inner(address)
|
175
|
+
|
176
|
+
result.push(temp)
|
177
|
+
}
|
178
|
+
|
179
|
+
return result
|
180
|
+
end
|
181
|
+
|
182
|
+
def direct_get(addr)
|
183
|
+
query_string = addr.query_string
|
184
|
+
|
185
|
+
require 'net/http'
|
186
|
+
|
187
|
+
content = nil
|
188
|
+
|
189
|
+
# headers = { 'Referer' => @@start_url, 'Content-Type' => 'application/x-www-form-urlencoded' }
|
190
|
+
headers = { }
|
191
|
+
|
192
|
+
path = @@post_path + "?" + query_string
|
193
|
+
|
194
|
+
session = Net::HTTP.new(@@usps_host)
|
195
|
+
(response, content) = session.get(path, headers)
|
196
|
+
|
197
|
+
if @verbose
|
198
|
+
print "-" * 79, "\n"
|
199
|
+
printf "Direct HTTP GET (of http://%s%s) Body:\n", @@usps_host, path
|
200
|
+
print "HTTP Response:\n"
|
201
|
+
print content
|
202
|
+
end
|
203
|
+
|
204
|
+
return content
|
205
|
+
rescue WWW::Mechanize::ResponseCodeError => error
|
206
|
+
printf "Unhandled response: %s\n", error.response_code
|
207
|
+
raise
|
208
|
+
end
|
209
|
+
|
210
|
+
#
|
211
|
+
# std_inner()
|
212
|
+
#
|
213
|
+
# The inner portion of the process, so it can be shared by
|
214
|
+
# std_addr() and std_addrs().
|
215
|
+
#
|
216
|
+
#
|
217
|
+
def std_inner(addr)
|
218
|
+
if @verbose
|
219
|
+
print ' ', '_' * 77, ' ', "\n"
|
220
|
+
print '/', ' ' * 77, '\\', "\n"
|
221
|
+
addr.dump("Input")
|
222
|
+
print "\n"
|
223
|
+
end
|
224
|
+
|
225
|
+
content = direct_get(addr)
|
226
|
+
|
227
|
+
#
|
228
|
+
# Time to Parse:
|
229
|
+
#
|
230
|
+
# The results look like this:
|
231
|
+
#
|
232
|
+
# <td width="312" background="images/light_blue_bg2.gif" class="mainText">6216 EDDINGTON ST <br>LIBERTY TOWNSHIP OH 45044-9761 <br>
|
233
|
+
#
|
234
|
+
# 1. We find <td header ...> ... </td> to find the data fields.
|
235
|
+
# 2. We strip out <font> and <a>
|
236
|
+
# 3. We replace with space
|
237
|
+
# 4. We strip out leading "...: "
|
238
|
+
# 5. We find <!--< ... />--> to get the field id
|
239
|
+
# 6. We standardize the field id (upper case, alpha only)
|
240
|
+
# 7. We standardize the value (trimming and whitespace coalescing)
|
241
|
+
#
|
242
|
+
# We end up with something like this:
|
243
|
+
#
|
244
|
+
# ADDRESSLINE: 6216 EDDINGTON ST
|
245
|
+
# CITYSTATEZIP: LIBERTY TOWNSHIP OH 45044-9761
|
246
|
+
# CARRIERROUTE: R007
|
247
|
+
# COUNTY: BUTLER
|
248
|
+
# DELIVERYPOINT: 16
|
249
|
+
# CHECKDIGIT: 3
|
250
|
+
#
|
251
|
+
|
252
|
+
matches =[ ]
|
253
|
+
|
254
|
+
content.gsub!(Regexp.new('[\cI\cJ\cM]'), '')
|
255
|
+
content.squeeze!(" ")
|
256
|
+
content.strip!
|
257
|
+
|
258
|
+
raw_matches = content.scan(%r{<td headers="\w+" height="34" valign="top" class="main" style="background:url\(images/table_gray\.gif\); padding:5px 10px;">(.*?)>Mailing Industry Information</a>}mi)
|
259
|
+
|
260
|
+
raw_matches.each { |raw_match|
|
261
|
+
if @verbose
|
262
|
+
print "-" * 79, "\n"
|
263
|
+
print "Raw match:\n"
|
264
|
+
printf("%s\n", raw_match[0])
|
265
|
+
end
|
266
|
+
|
267
|
+
match = parse_match(raw_match[0])
|
268
|
+
|
269
|
+
matches.push(match)
|
270
|
+
}
|
271
|
+
|
272
|
+
print('\\', '_' * 77, '/', "\n") if @verbose
|
273
|
+
|
274
|
+
return matches;
|
275
|
+
end # method std_inner
|
276
|
+
|
277
|
+
def parse_match(raw_match)
|
278
|
+
carrier_route = nil
|
279
|
+
county = nil
|
280
|
+
delivery_point = nil
|
281
|
+
check_digit = nil
|
282
|
+
lac_indicator = nil
|
283
|
+
elot_sequence = nil
|
284
|
+
elot_indicator = nil
|
285
|
+
record_type = nil
|
286
|
+
pmb_designator = nil
|
287
|
+
pmb_number = nil
|
288
|
+
default_address = nil
|
289
|
+
early_warning = nil
|
290
|
+
valid = nil
|
291
|
+
|
292
|
+
#
|
293
|
+
# Looking for some text like this:
|
294
|
+
#
|
295
|
+
# onClick="mailingIndustryPopup2('R007','BUTLER','16','3','','0179','A','S','','','','','Y');"
|
296
|
+
#
|
297
|
+
|
298
|
+
regex = %r{mailingIndustryPopup2\((.*?)\);}i
|
299
|
+
result = regex.match(raw_match)
|
300
|
+
|
301
|
+
if result != nil
|
302
|
+
args = result[1]
|
303
|
+
|
304
|
+
# Reformat to pipe-delimited
|
305
|
+
args.sub!(/^'/, '')
|
306
|
+
args.gsub!(/\s*'?\s*,\s*'?\s*/, '|')
|
307
|
+
args.sub!(/'$/, '')
|
308
|
+
|
309
|
+
args_array = args.split(/\|/)
|
310
|
+
|
311
|
+
carrier_route = (args_array[0] != nil && args_array[0] != '') ? args_array[0] : nil
|
312
|
+
county = (args_array[1] != nil && args_array[1] != '') ? args_array[1] : nil
|
313
|
+
delivery_point = (args_array[2] != nil && args_array[2] != '') ? args_array[2] : nil
|
314
|
+
check_digit = (args_array[3] != nil && args_array[3] != '') ? args_array[3] : nil
|
315
|
+
lac_indicator = (args_array[4] != nil && args_array[4] != '') ? args_array[4] : nil
|
316
|
+
elot_sequence = (args_array[5] != nil && args_array[5] != '') ? args_array[5] : nil
|
317
|
+
elot_indicator = (args_array[6] != nil && args_array[6] != '') ? args_array[6] : nil
|
318
|
+
record_type = (args_array[7] != nil && args_array[7] != '') ? args_array[7] : nil
|
319
|
+
pmb_designator = (args_array[8] != nil && args_array[8] != '') ? args_array[8] : nil
|
320
|
+
pmb_number = (args_array[9] != nil && args_array[9] != '') ? args_array[9] : nil
|
321
|
+
default_address = (args_array[10] != nil && args_array[10] != '') ? args_array[10] : nil
|
322
|
+
early_warning = (args_array[11] != nil && args_array[11] != '') ? args_array[11] : nil
|
323
|
+
valid = (args_array[12] != nil && args_array[12] != '') ? args_array[12] : nil
|
324
|
+
else
|
325
|
+
if @verbose
|
326
|
+
printf "WARNING: Could not find Mailing Industry info in '%s'!\n", raw_match
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
raw_match.gsub!(%r{</td>\s*<td.*?>}, ' ')
|
331
|
+
raw_match.gsub!(%r{</?font.*?>}, '')
|
332
|
+
raw_match.gsub!(%r{</?span.*?>}, '')
|
333
|
+
raw_match.gsub!(%r{</?a.*?>}, '')
|
334
|
+
raw_match.gsub!(%r{ }, ' ')
|
335
|
+
raw_match.gsub!(%r{^.*?:\s*}, '')
|
336
|
+
raw_match.gsub!(%r{\s+}, ' ')
|
337
|
+
raw_match.gsub!(%r{<!--<(.*?)/>-->}, '')
|
338
|
+
raw_match.sub!(%r{<a.*$}, '')
|
339
|
+
raw_match.sub!(%r{<br\s*/?>\s*$}, '')
|
340
|
+
|
341
|
+
if @verbose
|
342
|
+
print "-" * 79, "\n"
|
343
|
+
print "Distilled match:\n"
|
344
|
+
printf "%s\n", raw_match
|
345
|
+
end
|
346
|
+
|
347
|
+
parts = raw_match.split( /\s*<br\s*\/?>\s*/)
|
348
|
+
|
349
|
+
firm = nil
|
350
|
+
address = nil
|
351
|
+
city_state_zip = nil
|
352
|
+
|
353
|
+
if parts.size == 2
|
354
|
+
(address, city_state_zip) = parts
|
355
|
+
elsif parts.size == 3
|
356
|
+
(firm, address, city_state_zip) = parts
|
357
|
+
else
|
358
|
+
# die "Parts = " . scalar(@parts) . "!";
|
359
|
+
end
|
360
|
+
|
361
|
+
next if city_state_zip == nil
|
362
|
+
next if !(city_state_zip =~ /^(.*)\s+(\w\w)\s+(\d{5}(-\d{4})?)/)
|
363
|
+
|
364
|
+
city = $1
|
365
|
+
state = $2
|
366
|
+
zip = $3
|
367
|
+
|
368
|
+
if @verbose
|
369
|
+
print("-" * 70, "\n");
|
370
|
+
|
371
|
+
printf "Firm: %s\n", firm if firm != nil
|
372
|
+
|
373
|
+
printf "Address: %s\n", address
|
374
|
+
printf "City: %s\n", city
|
375
|
+
printf "State: %s\n", state
|
376
|
+
printf "Zip: %s\n", zip
|
377
|
+
|
378
|
+
printf "Carrier Route: %s\n", carrier_route if carrier_route != nil
|
379
|
+
printf "County: %s\n", county if county != nil
|
380
|
+
printf "Delivery Point: %s\n", delivery_point if delivery_point != nil
|
381
|
+
printf "Check Digit: %s\n", check_digit if check_digit != nil
|
382
|
+
printf "LAC Indicator: %s\n", lac_indicator if lac_indicator != nil
|
383
|
+
printf "eLOT Sequence: %s\n", elot_sequence if elot_sequence != nil
|
384
|
+
printf "eLOT Indicator: %s\n", elot_indicator if elot_indicator != nil
|
385
|
+
printf "Record Type: %s\n", record_type if record_type != nil
|
386
|
+
printf "PMB Designator: %s\n", pmb_designator if pmb_designator != nil
|
387
|
+
printf "PMB Number: %s\n", pmb_number if pmb_number != nil
|
388
|
+
printf "Default Address: %s\n", default_address if default_address != nil
|
389
|
+
printf "Early Warning: %s\n", early_warning if early_warning != nil
|
390
|
+
printf "Valid: %s\n", valid if valid != nil
|
391
|
+
|
392
|
+
print "\n";
|
393
|
+
end
|
394
|
+
|
395
|
+
match = USPS::Address.new
|
396
|
+
|
397
|
+
match.delivery_address = address
|
398
|
+
match.city = city
|
399
|
+
match.state = state
|
400
|
+
match.zip_code = zip
|
401
|
+
|
402
|
+
match.firm = firm
|
403
|
+
|
404
|
+
match.carrier_route = carrier_route
|
405
|
+
match.county = county
|
406
|
+
match.delivery_point = delivery_point
|
407
|
+
match.check_digit = check_digit
|
408
|
+
|
409
|
+
match.lac_indicator = lac_indicator
|
410
|
+
match.elot_sequence = elot_sequence
|
411
|
+
match.elot_indicator = elot_indicator
|
412
|
+
match.record_type = record_type
|
413
|
+
match.pmb_designator = pmb_designator
|
414
|
+
match.pmb_number = pmb_number
|
415
|
+
match.default_address = default_address
|
416
|
+
match.early_warning = early_warning
|
417
|
+
match.valid = valid
|
418
|
+
|
419
|
+
return match
|
420
|
+
end # method parse_match
|
421
|
+
|
422
|
+
end # class ZipLookup
|
423
|
+
|
424
|
+
end # module USPS
|
data/test/tc_address.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
#require 'rubygems'
|
5
|
+
require 'USPS/address'
|
6
|
+
#require 'test_includes'
|
7
|
+
|
8
|
+
class AddressTest < Test::Unit::TestCase
|
9
|
+
# include TestMethods
|
10
|
+
|
11
|
+
def setup
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_address
|
15
|
+
address = USPS::Address.new
|
16
|
+
|
17
|
+
address.delivery_address = '6216 Eddington'
|
18
|
+
address.city = 'Middletown'
|
19
|
+
address.zip_code = '45044'
|
20
|
+
|
21
|
+
assert_equal('6216 Eddington', address.delivery_address)
|
22
|
+
assert_equal('Middletown', address.city)
|
23
|
+
assert_equal('45044', address.zip_code)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
#
|
3
|
+
# Copyright (C) 2006 Gregor N. Purdy. All rights reserved.
|
4
|
+
# This program is free software. It is subject to the same license as Ruby.
|
5
|
+
#
|
6
|
+
# [ $Id: tc_standardize.rb 2255 2006-08-07 05:39:57Z gregor $ ]
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'test/unit'
|
10
|
+
require 'USPS/address'
|
11
|
+
require 'USPS/zip_lookup'
|
12
|
+
|
13
|
+
class StandardizeTest < Test::Unit::TestCase
|
14
|
+
def setup
|
15
|
+
@zlu = USPS::ZipLookup.new
|
16
|
+
if (ARGV.size >= 1) && (ARGV[0] == '-v') # ARGV is a global var, but doesn't use prefix '$'; GO FIGURE!
|
17
|
+
@zlu.verbose = true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_error
|
22
|
+
address = USPS::Address.new
|
23
|
+
|
24
|
+
address.delivery_address = 'bar'
|
25
|
+
address.city = 'splee'
|
26
|
+
address.state = 'OH'
|
27
|
+
|
28
|
+
result = @zlu.std_addr(address)
|
29
|
+
assert_equal([], result)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_simple_1
|
33
|
+
address = USPS::Address.new
|
34
|
+
|
35
|
+
address.delivery_address = '6216 Eddington Drive'
|
36
|
+
address.city = 'Liberty Township'
|
37
|
+
address.state = 'oh'
|
38
|
+
|
39
|
+
result = @zlu.std_addr(address)
|
40
|
+
assert_equal(1, result.size)
|
41
|
+
|
42
|
+
assert_equal('6216 EDDINGTON ST', result[0].delivery_address)
|
43
|
+
assert_equal('LIBERTY TOWNSHIP', result[0].city)
|
44
|
+
assert_equal('OH', result[0].state)
|
45
|
+
assert_equal('45044-9761', result[0].zip_code)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_apartment
|
49
|
+
address = USPS::Address.new
|
50
|
+
|
51
|
+
address.delivery_address = '3303 Pine Meadow DR SE #202'
|
52
|
+
address.city = 'Kentwood'
|
53
|
+
address.state = 'MI'
|
54
|
+
address.zip_code = '49512'
|
55
|
+
|
56
|
+
result = @zlu.std_addr(address)
|
57
|
+
assert_equal(1, result.size)
|
58
|
+
|
59
|
+
assert_equal('3303 PINE MEADOW DR SE APT 202', result[0].delivery_address)
|
60
|
+
assert_equal('KENTWOOD', result[0].city)
|
61
|
+
assert_equal('MI', result[0].state)
|
62
|
+
assert_equal('49512-8325', result[0].zip_code)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_simple_2
|
66
|
+
address = USPS::Address.new
|
67
|
+
|
68
|
+
address.delivery_address = '2701 DOUGLAS AVE'
|
69
|
+
address.city = 'DES MOINES'
|
70
|
+
address.state = 'IA'
|
71
|
+
address.zip_code = '50310'
|
72
|
+
|
73
|
+
result = @zlu.std_addr(address)
|
74
|
+
assert_equal(1, result.size)
|
75
|
+
|
76
|
+
assert_equal('2701 DOUGLAS AVE', result[0].delivery_address)
|
77
|
+
assert_equal('DES MOINES', result[0].city)
|
78
|
+
assert_equal('IA', result[0].state)
|
79
|
+
assert_equal('50310-5840', result[0].zip_code)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_multiple
|
83
|
+
address = USPS::Address.new
|
84
|
+
|
85
|
+
address.delivery_address = '1670 Broadway'
|
86
|
+
address.city = 'Denver'
|
87
|
+
address.state = 'CO'
|
88
|
+
address.zip_code = '80202'
|
89
|
+
|
90
|
+
result = @zlu.std_addr(address)
|
91
|
+
assert(result.size > 1)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: ziplookup
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2006-08-06 00:00:00 -07:00
|
8
|
+
summary: Classes for standardizing addresses via the USPS web site.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: gregor@focusresearch.com
|
12
|
+
homepage:
|
13
|
+
rubyforge_project:
|
14
|
+
description: Classes for standardizing addresses via the USPS web site.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
post_install_message:
|
30
|
+
authors:
|
31
|
+
- Gregor N. Purdy
|
32
|
+
files:
|
33
|
+
- ".project"
|
34
|
+
- bin/tryme.rb
|
35
|
+
- lib/USPS/address.rb
|
36
|
+
- lib/USPS/zip_lookup.rb
|
37
|
+
- Manifest.txt
|
38
|
+
- Rakefile
|
39
|
+
- test/tc_address.rb
|
40
|
+
- test/tc_standardize.rb
|
41
|
+
- test/ts_zip_lookup.rb
|
42
|
+
test_files: []
|
43
|
+
rdoc_options: []
|
44
|
+
extra_rdoc_files: []
|
45
|
+
executables:
|
46
|
+
- tryme.rb
|
47
|
+
extensions: []
|
48
|
+
requirements: []
|
49
|
+
dependencies: []
|