StreetAddress 1.0.5 → 2.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.
@@ -1,70 +1,9 @@
1
- =begin rdoc
2
-
3
- === Usage:
4
- StreetAddress::US.parse("1600 Pennsylvania Ave, washington, dc")
5
-
6
- === Valid Address Formats
7
-
8
- 1600 Pennsylvania Ave Washington DC 20006
9
- 1600 Pennsylvania Ave #400, Washington, DC, 20006
10
- 1600 Pennsylvania Ave Washington, DC
11
- 1600 Pennsylvania Ave #400 Washington DC
12
- 1600 Pennsylvania Ave, 20006
13
- 1600 Pennsylvania Ave #400, 20006
14
- 1600 Pennsylvania Ave 20006
15
- 1600 Pennsylvania Ave #400 20006
16
-
17
- === Valid Intersection Formats
18
-
19
- Hollywood & Vine, Los Angeles, CA
20
- Hollywood Blvd and Vine St, Los Angeles, CA
21
- Mission Street at Valencia Street, San Francisco, CA
22
- Hollywood & Vine, Los Angeles, CA, 90028
23
- Hollywood Blvd and Vine St, Los Angeles, CA, 90028
24
- Mission Street at Valencia Street, San Francisco, CA, 90028
25
-
26
- ==== License
27
-
28
- Copyright (c) 2007 Riderway (Derrek Long, Nicholas Schlueter)
29
-
30
- Permission is hereby granted, free of charge, to any person obtaining
31
- a copy of this software and associated documentation files (the
32
- "Software"), to deal in the Software without restriction, including
33
- without limitation the rights to use, copy, modify, merge, publish,
34
- distribute, sublicense, and/or sell copies of the Software, and to
35
- permit persons to whom the Software is furnished to do so, subject to
36
- the following conditions:
37
-
38
- The above copyright notice and this permission notice shall be
39
- included in all copies or substantial portions of the Software.
40
-
41
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
42
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
43
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
44
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
45
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
46
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
47
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48
-
49
- ==== Notes
50
- If parts of the address are omitted from the original string
51
- the accessor will be nil in StreetAddress::US::Address.
52
-
53
- Example:
54
- address = StreetAddress::US.parse("1600 Pennsylvania Ave, washington, dc")
55
- assert address.postal_code.nil?
56
-
57
- ==== Acknowledgements
58
-
59
- This gem is a near direct port of the perl module Geo::StreetAddress::US
60
- originally written by Schuyler D. Erle. For more information see
61
- http://search.cpan.org/~sderle/Geo-StreetAddress-US-0.99/
62
-
63
- =end
1
+ # frozen_string_literal: true
64
2
 
65
3
  module StreetAddress
66
4
  class US
67
- VERSION = '1.0.5'
5
+ VERSION = '2.0.0'
6
+
68
7
  DIRECTIONAL = {
69
8
  "north" => "N",
70
9
  "northeast" => "NE",
@@ -74,378 +13,381 @@ module StreetAddress
74
13
  "southwest" => "SW",
75
14
  "west" => "W",
76
15
  "northwest" => "NW"
77
- }
78
- DIRECTION_CODES = DIRECTIONAL.invert
16
+ }.freeze
17
+ DIRECTION_CODES = DIRECTIONAL.invert.freeze
79
18
 
80
19
  STREET_TYPES = {
81
- "allee" => "aly",
82
- "alley" => "aly",
83
- "ally" => "aly",
84
- "anex" => "anx",
85
- "annex" => "anx",
86
- "annx" => "anx",
87
- "arcade" => "arc",
88
- "av" => "ave",
89
- "aven" => "ave",
90
- "avenu" => "ave",
91
- "avenue" => "ave",
92
- "avn" => "ave",
93
- "avnue" => "ave",
94
- "bayoo" => "byu",
95
- "bayou" => "byu",
96
- "beach" => "bch",
97
- "bend" => "bnd",
98
- "bluf" => "blf",
99
- "bluff" => "blf",
100
- "bluffs" => "blfs",
101
- "bot" => "btm",
102
- "bottm" => "btm",
103
- "bottom" => "btm",
104
- "boul" => "blvd",
105
- "boulevard" => "blvd",
106
- "boulv" => "blvd",
107
- "branch" => "br",
108
- "brdge" => "brg",
109
- "bridge" => "brg",
110
- "brnch" => "br",
111
- "brook" => "brk",
112
- "brooks" => "brks",
113
- "burg" => "bg",
114
- "burgs" => "bgs",
115
- "bypa" => "byp",
116
- "bypas" => "byp",
117
- "bypass" => "byp",
118
- "byps" => "byp",
119
- "camp" => "cp",
120
- "canyn" => "cyn",
121
- "canyon" => "cyn",
122
- "cape" => "cpe",
123
- "causeway" => "cswy",
124
- "causway" => "cswy",
125
- "cen" => "ctr",
126
- "cent" => "ctr",
127
- "center" => "ctr",
128
- "centers" => "ctrs",
129
- "centr" => "ctr",
130
- "centre" => "ctr",
131
- "circ" => "cir",
132
- "circl" => "cir",
133
- "circle" => "cir",
134
- "circles" => "cirs",
135
- "ck" => "crk",
136
- "cliff" => "clf",
137
- "cliffs" => "clfs",
138
- "club" => "clb",
139
- "cmp" => "cp",
140
- "cnter" => "ctr",
141
- "cntr" => "ctr",
142
- "cnyn" => "cyn",
143
- "common" => "cmn",
144
- "corner" => "cor",
145
- "corners" => "cors",
146
- "course" => "crse",
147
- "court" => "ct",
148
- "courts" => "cts",
149
- "cove" => "cv",
150
- "coves" => "cvs",
151
- "cr" => "crk",
152
- "crcl" => "cir",
153
- "crcle" => "cir",
154
- "crecent" => "cres",
155
- "creek" => "crk",
156
- "crescent" => "cres",
157
- "cresent" => "cres",
158
- "crest" => "crst",
159
- "crossing" => "xing",
160
- "crossroad" => "xrd",
161
- "crscnt" => "cres",
162
- "crsent" => "cres",
163
- "crsnt" => "cres",
164
- "crssing" => "xing",
165
- "crssng" => "xing",
166
- "crt" => "ct",
167
- "curve" => "curv",
168
- "dale" => "dl",
169
- "dam" => "dm",
170
- "div" => "dv",
171
- "divide" => "dv",
172
- "driv" => "dr",
173
- "drive" => "dr",
174
- "drives" => "drs",
175
- "drv" => "dr",
176
- "dvd" => "dv",
177
- "estate" => "est",
178
- "estates" => "ests",
179
- "exp" => "expy",
180
- "expr" => "expy",
181
- "express" => "expy",
182
- "expressway" => "expy",
183
- "expw" => "expy",
184
- "extension" => "ext",
185
- "extensions" => "exts",
186
- "extn" => "ext",
187
- "extnsn" => "ext",
188
- "falls" => "fls",
189
- "ferry" => "fry",
190
- "field" => "fld",
191
- "fields" => "flds",
192
- "flat" => "flt",
193
- "flats" => "flts",
194
- "ford" => "frd",
195
- "fords" => "frds",
196
- "forest" => "frst",
197
- "forests" => "frst",
198
- "forg" => "frg",
199
- "forge" => "frg",
200
- "forges" => "frgs",
201
- "fork" => "frk",
202
- "forks" => "frks",
203
- "fort" => "ft",
204
- "freeway" => "fwy",
205
- "freewy" => "fwy",
206
- "frry" => "fry",
207
- "frt" => "ft",
208
- "frway" => "fwy",
209
- "frwy" => "fwy",
210
- "garden" => "gdn",
211
- "gardens" => "gdns",
212
- "gardn" => "gdn",
213
- "gateway" => "gtwy",
214
- "gatewy" => "gtwy",
215
- "gatway" => "gtwy",
216
- "glen" => "gln",
217
- "glens" => "glns",
218
- "grden" => "gdn",
219
- "grdn" => "gdn",
220
- "grdns" => "gdns",
221
- "green" => "grn",
222
- "greens" => "grns",
223
- "grov" => "grv",
224
- "grove" => "grv",
225
- "groves" => "grvs",
226
- "gtway" => "gtwy",
227
- "harb" => "hbr",
228
- "harbor" => "hbr",
229
- "harbors" => "hbrs",
230
- "harbr" => "hbr",
231
- "haven" => "hvn",
232
- "havn" => "hvn",
233
- "height" => "hts",
234
- "heights" => "hts",
235
- "hgts" => "hts",
236
- "highway" => "hwy",
237
- "highwy" => "hwy",
238
- "hill" => "hl",
239
- "hills" => "hls",
240
- "hiway" => "hwy",
241
- "hiwy" => "hwy",
242
- "hllw" => "holw",
243
- "hollow" => "holw",
244
- "hollows" => "holw",
245
- "holws" => "holw",
246
- "hrbor" => "hbr",
247
- "ht" => "hts",
248
- "hway" => "hwy",
249
- "inlet" => "inlt",
250
- "island" => "is",
251
- "islands" => "iss",
252
- "isles" => "isle",
253
- "islnd" => "is",
254
- "islnds" => "iss",
255
- "jction" => "jct",
256
- "jctn" => "jct",
257
- "jctns" => "jcts",
258
- "junction" => "jct",
259
- "junctions" => "jcts",
260
- "junctn" => "jct",
261
- "juncton" => "jct",
262
- "key" => "ky",
263
- "keys" => "kys",
264
- "knol" => "knl",
265
- "knoll" => "knl",
266
- "knolls" => "knls",
267
- "la" => "ln",
268
- "lake" => "lk",
269
- "lakes" => "lks",
270
- "landing" => "lndg",
271
- "lane" => "ln",
272
- "lanes" => "ln",
273
- "ldge" => "ldg",
274
- "light" => "lgt",
275
- "lights" => "lgts",
276
- "lndng" => "lndg",
277
- "loaf" => "lf",
278
- "lock" => "lck",
279
- "locks" => "lcks",
280
- "lodg" => "ldg",
281
- "lodge" => "ldg",
282
- "loops" => "loop",
283
- "manor" => "mnr",
284
- "manors" => "mnrs",
285
- "meadow" => "mdw",
286
- "meadows" => "mdws",
287
- "medows" => "mdws",
288
- "mill" => "ml",
289
- "mills" => "mls",
290
- "mission" => "msn",
291
- "missn" => "msn",
292
- "mnt" => "mt",
293
- "mntain" => "mtn",
294
- "mntn" => "mtn",
295
- "mntns" => "mtns",
296
- "motorway" => "mtwy",
297
- "mount" => "mt",
298
- "mountain" => "mtn",
299
- "mountains" => "mtns",
300
- "mountin" => "mtn",
301
- "mssn" => "msn",
302
- "mtin" => "mtn",
303
- "neck" => "nck",
304
- "orchard" => "orch",
305
- "orchrd" => "orch",
306
- "overpass" => "opas",
307
- "ovl" => "oval",
308
- "parks" => "park",
309
- "parkway" => "pkwy",
310
- "parkways" => "pkwy",
311
- "parkwy" => "pkwy",
312
- "passage" => "psge",
313
- "paths" => "path",
314
- "pikes" => "pike",
315
- "pine" => "pne",
316
- "pines" => "pnes",
317
- "pk" => "park",
318
- "pkway" => "pkwy",
319
- "pkwys" => "pkwy",
320
- "pky" => "pkwy",
321
- "place" => "pl",
322
- "plain" => "pln",
323
- "plaines" => "plns",
324
- "plains" => "plns",
325
- "plaza" => "plz",
326
- "plza" => "plz",
327
- "point" => "pt",
328
- "points" => "pts",
329
- "port" => "prt",
330
- "ports" => "prts",
331
- "prairie" => "pr",
332
- "prarie" => "pr",
333
- "prk" => "park",
334
- "prr" => "pr",
335
- "rad" => "radl",
336
- "radial" => "radl",
337
- "radiel" => "radl",
338
- "ranch" => "rnch",
339
- "ranches" => "rnch",
340
- "rapid" => "rpd",
341
- "rapids" => "rpds",
342
- "rdge" => "rdg",
343
- "rest" => "rst",
344
- "ridge" => "rdg",
345
- "ridges" => "rdgs",
346
- "river" => "riv",
347
- "rivr" => "riv",
348
- "rnchs" => "rnch",
349
- "road" => "rd",
350
- "roads" => "rds",
351
- "route" => "rte",
352
- "rvr" => "riv",
353
- "shoal" => "shl",
354
- "shoals" => "shls",
355
- "shoar" => "shr",
356
- "shoars" => "shrs",
357
- "shore" => "shr",
358
- "shores" => "shrs",
359
- "skyway" => "skwy",
360
- "spng" => "spg",
361
- "spngs" => "spgs",
362
- "spring" => "spg",
363
- "springs" => "spgs",
364
- "sprng" => "spg",
365
- "sprngs" => "spgs",
366
- "spurs" => "spur",
367
- "sqr" => "sq",
368
- "sqre" => "sq",
369
- "sqrs" => "sqs",
370
- "squ" => "sq",
371
- "square" => "sq",
372
- "squares" => "sqs",
373
- "station" => "sta",
374
- "statn" => "sta",
375
- "stn" => "sta",
376
- "str" => "st",
377
- "strav" => "stra",
378
- "strave" => "stra",
379
- "straven" => "stra",
380
- "stravenue" => "stra",
381
- "stravn" => "stra",
382
- "stream" => "strm",
383
- "street" => "st",
384
- "streets" => "sts",
385
- "streme" => "strm",
386
- "strt" => "st",
387
- "strvn" => "stra",
388
- "strvnue" => "stra",
389
- "sumit" => "smt",
390
- "sumitt" => "smt",
391
- "summit" => "smt",
392
- "terr" => "ter",
393
- "terrace" => "ter",
394
- "throughway" => "trwy",
395
- "tpk" => "tpke",
396
- "tr" => "trl",
397
- "trace" => "trce",
398
- "traces" => "trce",
399
- "track" => "trak",
400
- "tracks" => "trak",
401
- "trafficway" => "trfy",
402
- "trail" => "trl",
403
- "trails" => "trl",
404
- "trk" => "trak",
405
- "trks" => "trak",
406
- "trls" => "trl",
407
- "trnpk" => "tpke",
408
- "trpk" => "tpke",
409
- "tunel" => "tunl",
410
- "tunls" => "tunl",
411
- "tunnel" => "tunl",
412
- "tunnels" => "tunl",
413
- "tunnl" => "tunl",
414
- "turnpike" => "tpke",
415
- "turnpk" => "tpke",
416
- "underpass" => "upas",
417
- "union" => "un",
418
- "unions" => "uns",
419
- "valley" => "vly",
420
- "valleys" => "vlys",
421
- "vally" => "vly",
422
- "vdct" => "via",
423
- "viadct" => "via",
424
- "viaduct" => "via",
425
- "view" => "vw",
426
- "views" => "vws",
427
- "vill" => "vlg",
428
- "villag" => "vlg",
429
- "village" => "vlg",
430
- "villages" => "vlgs",
431
- "ville" => "vl",
432
- "villg" => "vlg",
433
- "villiage" => "vlg",
434
- "vist" => "vis",
435
- "vista" => "vis",
436
- "vlly" => "vly",
437
- "vst" => "vis",
438
- "vsta" => "vis",
439
- "walks" => "walk",
440
- "well" => "wl",
441
- "wells" => "wls",
442
- "wy" => "way"
443
- }
444
-
445
- STREET_TYPES_LIST = {}
446
- STREET_TYPES.to_a.each do |item|
447
- STREET_TYPES_LIST[item[0]] = true
448
- STREET_TYPES_LIST[item[1]] = true
20
+ 'allee' => "aly",
21
+ 'alley' => "aly",
22
+ 'ally' => "aly",
23
+ 'anex' => "anx",
24
+ 'annex' => "anx",
25
+ 'annx' => "anx",
26
+ 'arcade'=> "arc",
27
+ 'av' => "ave",
28
+ 'aven' => "ave",
29
+ 'avenu' => "ave",
30
+ 'avenue'=> "ave",
31
+ 'avn' => "ave",
32
+ 'avnue' => "ave",
33
+ 'bayoo' => "byu",
34
+ 'bayou' => "byu",
35
+ 'beach' => "bch",
36
+ 'bend' => "bnd",
37
+ 'bluf' => "blf",
38
+ 'bluff' => "blf",
39
+ 'bluffs'=> "blfs",
40
+ 'bot' => "btm",
41
+ 'bottm' => "btm",
42
+ 'bottom'=> "btm",
43
+ 'boul' => "blvd",
44
+ 'boulevard' => "blvd",
45
+ 'boulv' => "blvd",
46
+ 'branch'=> "br",
47
+ 'brdge' => "brg",
48
+ 'bridge'=> "brg",
49
+ 'brnch' => "br",
50
+ 'brook' => "brk",
51
+ 'brooks'=> "brks",
52
+ 'burg' => "bg",
53
+ 'burgs' => "bgs",
54
+ 'bypa' => "byp",
55
+ 'bypas' => "byp",
56
+ 'bypass'=> "byp",
57
+ 'byps' => "byp",
58
+ 'camp' => "cp",
59
+ 'canyn' => "cyn",
60
+ 'canyon'=> "cyn",
61
+ 'cape' => "cpe",
62
+ 'causeway' => "cswy",
63
+ 'causway' => "cswy",
64
+ 'cen' => "ctr",
65
+ 'cent' => "ctr",
66
+ 'center'=> "ctr",
67
+ 'centers' => "ctrs",
68
+ 'centr' => "ctr",
69
+ 'centre'=> "ctr",
70
+ 'circ' => "cir",
71
+ 'circl' => "cir",
72
+ 'circle'=> "cir",
73
+ 'circles' => "cirs",
74
+ 'ck' => "crk",
75
+ 'cliff' => "clf",
76
+ 'cliffs'=> "clfs",
77
+ 'club' => "clb",
78
+ 'cmp' => "cp",
79
+ 'cnter' => "ctr",
80
+ 'cntr' => "ctr",
81
+ 'cnyn' => "cyn",
82
+ 'common'=> "cmn",
83
+ 'corner'=> "cor",
84
+ 'corners' => "cors",
85
+ 'course'=> "crse",
86
+ 'court' => "ct",
87
+ 'courts'=> "cts",
88
+ 'cove' => "cv",
89
+ 'coves' => "cvs",
90
+ 'cr' => "crk",
91
+ 'crcl' => "cir",
92
+ 'crcle' => "cir",
93
+ 'crecent' => "cres",
94
+ 'creek' => "crk",
95
+ 'crescent' => "cres",
96
+ 'cresent' => "cres",
97
+ 'crest' => "crst",
98
+ 'crossing' => "xing",
99
+ 'crossroad' => "xrd",
100
+ 'crscnt'=> "cres",
101
+ 'crsent'=> "cres",
102
+ 'crsnt' => "cres",
103
+ 'crssing' => "xing",
104
+ 'crssng'=> "xing",
105
+ 'crt' => "ct",
106
+ 'curve' => "curv",
107
+ 'dale' => "dl",
108
+ 'dam' => "dm",
109
+ 'div' => "dv",
110
+ 'divide'=> "dv",
111
+ 'driv' => "dr",
112
+ 'drive' => "dr",
113
+ 'drives'=> "drs",
114
+ 'drv' => "dr",
115
+ 'dvd' => "dv",
116
+ 'estate'=> "est",
117
+ 'estates' => "ests",
118
+ 'exp' => "expy",
119
+ 'expr' => "expy",
120
+ 'express' => "expy",
121
+ 'expressway' => "expy",
122
+ 'expw' => "expy",
123
+ 'extension' => "ext",
124
+ 'extensions' => "exts",
125
+ 'extn' => "ext",
126
+ 'extnsn'=> "ext",
127
+ 'falls' => "fls",
128
+ 'ferry' => "fry",
129
+ 'field' => "fld",
130
+ 'fields'=> "flds",
131
+ 'flat' => "flt",
132
+ 'flats' => "flts",
133
+ 'ford' => "frd",
134
+ 'fords' => "frds",
135
+ 'forest'=> "frst",
136
+ 'forests' => "frst",
137
+ 'forg' => "frg",
138
+ 'forge' => "frg",
139
+ 'forges'=> "frgs",
140
+ 'fork' => "frk",
141
+ 'forks' => "frks",
142
+ 'fort' => "ft",
143
+ 'freeway' => "fwy",
144
+ 'freewy'=> "fwy",
145
+ 'frry' => "fry",
146
+ 'frt' => "ft",
147
+ 'frway' => "fwy",
148
+ 'frwy' => "fwy",
149
+ 'garden'=> "gdn",
150
+ 'gardens' => "gdns",
151
+ 'gardn' => "gdn",
152
+ 'gateway' => "gtwy",
153
+ 'gatewy'=> "gtwy",
154
+ 'gatway'=> "gtwy",
155
+ 'glen' => "gln",
156
+ 'glens' => "glns",
157
+ 'grden' => "gdn",
158
+ 'grdn' => "gdn",
159
+ 'grdns' => "gdns",
160
+ 'green' => "grn",
161
+ 'greens'=> "grns",
162
+ 'grov' => "grv",
163
+ 'grove' => "grv",
164
+ 'groves'=> "grvs",
165
+ 'gtway' => "gtwy",
166
+ 'harb' => "hbr",
167
+ 'harbor'=> "hbr",
168
+ 'harbors' => "hbrs",
169
+ 'harbr' => "hbr",
170
+ 'haven' => "hvn",
171
+ 'havn' => "hvn",
172
+ 'height'=> "hts",
173
+ 'heights' => "hts",
174
+ 'hgts' => "hts",
175
+ 'highway' => "hwy",
176
+ 'highwy'=> "hwy",
177
+ 'hill' => "hl",
178
+ 'hills' => "hls",
179
+ 'hiway' => "hwy",
180
+ 'hiwy' => "hwy",
181
+ 'hllw' => "holw",
182
+ 'hollow'=> "holw",
183
+ 'hollows' => "holw",
184
+ 'holws' => "holw",
185
+ 'hrbor' => "hbr",
186
+ 'ht' => "hts",
187
+ 'hway' => "hwy",
188
+ 'inlet' => "inlt",
189
+ 'island'=> "is",
190
+ 'islands' => "iss",
191
+ 'isles' => "isle",
192
+ 'islnd' => "is",
193
+ 'islnds'=> "iss",
194
+ 'jction'=> "jct",
195
+ 'jctn' => "jct",
196
+ 'jctns' => "jcts",
197
+ 'junction' => "jct",
198
+ 'junctions' => "jcts",
199
+ 'junctn'=> "jct",
200
+ 'juncton' => "jct",
201
+ 'key' => "ky",
202
+ 'keys' => "kys",
203
+ 'knol' => "knl",
204
+ 'knoll' => "knl",
205
+ 'knolls'=> "knls",
206
+ 'la' => "ln",
207
+ 'lake' => "lk",
208
+ 'lakes' => "lks",
209
+ 'landing' => "lndg",
210
+ 'lane' => "ln",
211
+ 'lanes' => "ln",
212
+ 'ldge' => "ldg",
213
+ 'light' => "lgt",
214
+ 'lights'=> "lgts",
215
+ 'lndng' => "lndg",
216
+ 'loaf' => "lf",
217
+ 'lock' => "lck",
218
+ 'locks' => "lcks",
219
+ 'lodg' => "ldg",
220
+ 'lodge' => "ldg",
221
+ 'loops' => "loop",
222
+ 'manor' => "mnr",
223
+ 'manors'=> "mnrs",
224
+ 'meadow'=> "mdw",
225
+ 'meadows' => "mdws",
226
+ 'medows'=> "mdws",
227
+ 'mill' => "ml",
228
+ 'mills' => "mls",
229
+ 'mission' => "msn",
230
+ 'missn' => "msn",
231
+ 'mnt' => "mt",
232
+ 'mntain'=> "mtn",
233
+ 'mntn' => "mtn",
234
+ 'mntns' => "mtns",
235
+ 'motorway' => "mtwy",
236
+ 'mount' => "mt",
237
+ 'mountain' => "mtn",
238
+ 'mountains' => "mtns",
239
+ 'mountin' => "mtn",
240
+ 'mssn' => "msn",
241
+ 'mtin' => "mtn",
242
+ 'neck' => "nck",
243
+ 'orchard' => "orch",
244
+ 'orchrd'=> "orch",
245
+ 'overpass' => "opas",
246
+ 'ovl' => "oval",
247
+ 'parks' => "park",
248
+ 'parkway' => "pkwy",
249
+ 'parkways' => "pkwy",
250
+ 'parkwy'=> "pkwy",
251
+ 'passage' => "psge",
252
+ 'paths' => "path",
253
+ 'pikes' => "pike",
254
+ 'pine' => "pne",
255
+ 'pines' => "pnes",
256
+ 'pk' => "park",
257
+ 'pkway' => "pkwy",
258
+ 'pkwys' => "pkwy",
259
+ 'pky' => "pkwy",
260
+ 'place' => "pl",
261
+ 'plain' => "pln",
262
+ 'plaines' => "plns",
263
+ 'plains'=> "plns",
264
+ 'plaza' => "plz",
265
+ 'plza' => "plz",
266
+ 'point' => "pt",
267
+ 'points'=> "pts",
268
+ 'port' => "prt",
269
+ 'ports' => "prts",
270
+ 'prairie' => "pr",
271
+ 'prarie'=> "pr",
272
+ 'prk' => "park",
273
+ 'prr' => "pr",
274
+ 'rad' => "radl",
275
+ 'radial'=> "radl",
276
+ 'radiel'=> "radl",
277
+ 'ranch' => "rnch",
278
+ 'ranches' => "rnch",
279
+ 'rapid' => "rpd",
280
+ 'rapids'=> "rpds",
281
+ 'rdge' => "rdg",
282
+ 'rest' => "rst",
283
+ 'ridge' => "rdg",
284
+ 'ridges'=> "rdgs",
285
+ 'river' => "riv",
286
+ 'rivr' => "riv",
287
+ 'rnchs' => "rnch",
288
+ 'road' => "rd",
289
+ 'roads' => "rds",
290
+ 'route' => "rte",
291
+ 'rvr' => "riv",
292
+ 'shoal' => "shl",
293
+ 'shoals'=> "shls",
294
+ 'shoar' => "shr",
295
+ 'shoars'=> "shrs",
296
+ 'shore' => "shr",
297
+ 'shores'=> "shrs",
298
+ 'skyway'=> "skwy",
299
+ 'spng' => "spg",
300
+ 'spngs' => "spgs",
301
+ 'spring'=> "spg",
302
+ 'springs' => "spgs",
303
+ 'sprng' => "spg",
304
+ 'sprngs'=> "spgs",
305
+ 'spurs' => "spur",
306
+ 'sqr' => "sq",
307
+ 'sqre' => "sq",
308
+ 'sqrs' => "sqs",
309
+ 'squ' => "sq",
310
+ 'square'=> "sq",
311
+ 'squares' => "sqs",
312
+ 'station' => "sta",
313
+ 'statn' => "sta",
314
+ 'stn' => "sta",
315
+ 'str' => "st",
316
+ 'strav' => "stra",
317
+ 'strave'=> "stra",
318
+ 'straven' => "stra",
319
+ 'stravenue' => "stra",
320
+ 'stravn'=> "stra",
321
+ 'stream'=> "strm",
322
+ 'street'=> "st",
323
+ 'streets' => "sts",
324
+ 'streme'=> "strm",
325
+ 'strt' => "st",
326
+ 'strvn' => "stra",
327
+ 'strvnue' => "stra",
328
+ 'sumit' => "smt",
329
+ 'sumitt'=> "smt",
330
+ 'summit'=> "smt",
331
+ 'terr' => "ter",
332
+ 'terrace' => "ter",
333
+ 'throughway' => "trwy",
334
+ 'tpk' => "tpke",
335
+ 'tr' => "trl",
336
+ 'trace' => "trce",
337
+ 'traces'=> "trce",
338
+ 'track' => "trak",
339
+ 'tracks'=> "trak",
340
+ 'trafficway' => "trfy",
341
+ 'trail' => "trl",
342
+ 'trails'=> "trl",
343
+ 'trk' => "trak",
344
+ 'trks' => "trak",
345
+ 'trls' => "trl",
346
+ 'trnpk' => "tpke",
347
+ 'trpk' => "tpke",
348
+ 'tunel' => "tunl",
349
+ 'tunls' => "tunl",
350
+ 'tunnel'=> "tunl",
351
+ 'tunnels' => "tunl",
352
+ 'tunnl' => "tunl",
353
+ 'turnpike' => "tpke",
354
+ 'turnpk'=> "tpke",
355
+ 'underpass' => "upas",
356
+ 'union' => "un",
357
+ 'unions'=> "uns",
358
+ 'valley'=> "vly",
359
+ 'valleys' => "vlys",
360
+ 'vally' => "vly",
361
+ 'vdct' => "via",
362
+ 'viadct'=> "via",
363
+ 'viaduct' => "via",
364
+ 'view' => "vw",
365
+ 'views' => "vws",
366
+ 'vill' => "vlg",
367
+ 'villag'=> "vlg",
368
+ 'village' => "vlg",
369
+ 'villages' => "vlgs",
370
+ 'ville' => "vl",
371
+ 'villg' => "vlg",
372
+ 'villiage' => "vlg",
373
+ 'vist' => "vis",
374
+ 'vista' => "vis",
375
+ 'vlly' => "vly",
376
+ 'vst' => "vis",
377
+ 'vsta' => "vis",
378
+ 'walks' => "walk",
379
+ 'well' => "wl",
380
+ 'wells' => "wls",
381
+ 'wy' => "way",
382
+ }.freeze
383
+
384
+ STREET_TYPES_LIST = begin
385
+ list = {}
386
+ STREET_TYPES.each_pair do |key, val|
387
+ list[key] = true
388
+ list[val] = true
389
+ end
390
+ list.freeze
449
391
  end
450
392
 
451
393
  STATE_CODES = {
@@ -508,10 +450,10 @@ module StreetAddress
508
450
  "west virginia" => "WV",
509
451
  "wisconsin" => "WI",
510
452
  "wyoming" => "WY"
511
- }
453
+ }.freeze
454
+
455
+ STATE_NAMES = STATE_CODES.invert.freeze
512
456
 
513
- STATE_NAMES = STATE_CODES.invert
514
-
515
457
  STATE_FIPS = {
516
458
  "01" => "AL",
517
459
  "02" => "AK",
@@ -566,334 +508,421 @@ module StreetAddress
566
508
  "56" => "WY",
567
509
  "72" => "PR",
568
510
  "78" => "VI"
569
- }
511
+ }.freeze
512
+
513
+ FIPS_STATES = STATE_FIPS.invert.freeze
514
+
515
+ NORMALIZE_MAP = {
516
+ 'prefix' => DIRECTIONAL,
517
+ 'prefix1' => DIRECTIONAL,
518
+ 'prefix2' => DIRECTIONAL,
519
+ 'suffix' => DIRECTIONAL,
520
+ 'suffix1' => DIRECTIONAL,
521
+ 'suffix2' => DIRECTIONAL,
522
+ 'street_type' => STREET_TYPES,
523
+ 'street_type1' => STREET_TYPES,
524
+ 'street_type2' => STREET_TYPES,
525
+ 'state' => STATE_CODES,
526
+ }.freeze
570
527
 
571
- FIPS_STATES = STATE_FIPS.invert
572
-
573
528
  class << self
574
529
  attr_accessor(
575
530
  :street_type_regexp,
531
+ :street_type_matches,
576
532
  :number_regexp,
577
533
  :fraction_regexp,
578
534
  :state_regexp,
579
535
  :city_and_state_regexp,
580
- :direct_regexp,
536
+ :direct_regexp,
581
537
  :zip_regexp,
582
538
  :corner_regexp,
583
- :unit_regexp,
584
539
  :street_regexp,
585
540
  :place_regexp,
586
541
  :address_regexp,
587
- :informal_address_regexp
542
+ :informal_address_regexp,
543
+ :dircode_regexp,
544
+ :unit_prefix_numbered_regexp,
545
+ :unit_prefix_unnumbered_regexp,
546
+ :unit_regexp,
547
+ :sep_regexp,
548
+ :sep_avoid_unit_regexp,
549
+ :intersection_regexp
588
550
  )
589
551
  end
590
-
591
- self.street_type_regexp = STREET_TYPES_LIST.keys.join("|")
592
- self.number_regexp = '\d+-?\d*'
593
- self.fraction_regexp = '\d+\/\d+'
594
- self.state_regexp = STATE_CODES.to_a.join("|").gsub(/ /, "\\s")
595
- self.city_and_state_regexp = '
552
+
553
+ self.street_type_matches = {}
554
+ STREET_TYPES.each_pair { |type,abbrv|
555
+ self.street_type_matches[abbrv] = /\b (?: #{abbrv}|#{Regexp.quote(type)} ) \b/ix
556
+ }
557
+
558
+ self.street_type_regexp = Regexp.new(STREET_TYPES_LIST.keys.join("|"), Regexp::IGNORECASE)
559
+ self.fraction_regexp = /\d+\/\d+/
560
+ self.state_regexp = Regexp.new(
561
+ '\b' + STATE_CODES.flatten.map{ |code| Regexp.quote(code) }.join("|") + '\b',
562
+ Regexp::IGNORECASE
563
+ )
564
+ self.direct_regexp = Regexp.new(
565
+ (DIRECTIONAL.keys +
566
+ DIRECTIONAL.values.sort { |a,b|
567
+ b.length <=> a.length
568
+ }.map { |c|
569
+ f = c.gsub(/(\w)/, '\1.')
570
+ [Regexp::quote(f), Regexp::quote(c)]
571
+ }
572
+ ).join("|"),
573
+ Regexp::IGNORECASE
574
+ )
575
+ self.dircode_regexp = Regexp.new(DIRECTION_CODES.keys.join("|"), Regexp::IGNORECASE)
576
+ self.zip_regexp = /(?:(?<postal_code>\d{5})(?:-?(?<postal_code_ext>\d{4}))?)/
577
+ self.corner_regexp = /(?:\band\b|\bat\b|&|\@)/i
578
+
579
+ # we don't include letters in the number regex because we want to
580
+ # treat "42S" as "42 S" (42 South). For example,
581
+ # Utah and Wisconsin have a more elaborate system of block numbering
582
+ # http://en.wikipedia.org/wiki/House_number#Block_numbers
583
+ self.number_regexp = /(?<number>\d+-?\d*)(?=\D)/ix
584
+
585
+ # note that expressions like [^,]+ may scan more than you expect
586
+ self.street_regexp = /
596
587
  (?:
597
- ([^\d,]+?)\W+
598
- (' + state_regexp + ')
599
- )'
600
-
601
- self.direct_regexp = DIRECTIONAL.keys.join("|") +
602
- "|" +
603
- DIRECTIONAL.values.sort{ |a,b|
604
- b.length <=> a.length
605
- }.map{ |x|
606
- f = x.gsub(/(\w)/, '\1.')
607
- [Regexp::quote(f), Regexp::quote(x)]
608
- }.join("|")
609
- self.zip_regexp = '(\d{5})(?:-?(\d{4})?)'
610
- self.corner_regexp = '(?:\band\b|\bat\b|&|\@)'
611
- self.unit_regexp = '(?:(su?i?te|p\W*[om]\W*b(?:ox)?|dept|apt|apartment|ro*m|fl|unit|box)\W+|\#\W*)([\w-]+)'
612
- self.street_regexp =
613
- '(?:
614
- (?:(' + direct_regexp + ')\W+
615
- (' + street_type_regexp + ')\b)
588
+ # special case for addresses like 100 South Street
589
+ (?:(?<street> #{direct_regexp})\W+
590
+ (?<street_type> #{street_type_regexp})\b
591
+ )
592
+ |
593
+ (?:(?<prefix> #{direct_regexp})\W+)?
594
+ (?:
595
+ (?<street> [^,]*\d)
596
+ (?:[^\w,]* (?<suffix> #{direct_regexp})\b)
616
597
  |
617
- (?:(' + direct_regexp + ')\W+)?
618
- (?:
619
- ([^,]+)
620
- (?:[^\w,]+(' + street_type_regexp + ')\b)
621
- (?:[^\w,]+(' + direct_regexp + ')\b)?
622
- |
623
- ([^,]*\d)
624
- (' + direct_regexp + ')\b
625
- |
626
- ([^,]+?)
627
- (?:[^\w,]+(' + street_type_regexp + ')\b)?
628
- (?:[^\w,]+(' + direct_regexp + ')\b)?
598
+ (?<street> [^,]+)
599
+ (?:[^\w,]+(?<street_type> #{street_type_regexp})\b)
600
+ (?:[^\w,]+(?<suffix> #{direct_regexp})\b)?
601
+ |
602
+ (?<street> [^,]+?)
603
+ (?:[^\w,]+(?<street_type> #{street_type_regexp})\b)?
604
+ (?:[^\w,]+(?<suffix> #{direct_regexp})\b)?
605
+ )
606
+ )
607
+ /ix;
608
+
609
+ # http://pe.usps.com/text/pub28/pub28c2_003.htm
610
+ # TODO add support for those that don't require a number
611
+ # TODO map to standard names/abbreviations
612
+ self.unit_prefix_numbered_regexp = /
613
+ (?<unit_prefix>
614
+ su?i?te
615
+ |p\W*[om]\W*b(?:ox)?
616
+ |(?:ap|dep)(?:ar)?t(?:me?nt)?
617
+ |ro*m
618
+ |flo*r?
619
+ |uni?t
620
+ |bu?i?ldi?n?g
621
+ |ha?nga?r
622
+ |lo?t
623
+ |pier
624
+ |slip
625
+ |spa?ce?
626
+ |stop
627
+ |tra?i?le?r
628
+ |box)(?![a-z])
629
+ /ix;
630
+
631
+ self.unit_prefix_unnumbered_regexp = /
632
+ (?<unit_prefix>
633
+ ba?se?me?n?t
634
+ |fro?nt
635
+ |lo?bby
636
+ |lowe?r
637
+ |off?i?ce?
638
+ |pe?n?t?ho?u?s?e?
639
+ |rear
640
+ |side
641
+ |uppe?r
642
+ )\b
643
+ /ix;
644
+
645
+ self.unit_regexp = /
646
+ (?:
647
+ (?: (?:#{unit_prefix_numbered_regexp} \W*)
648
+ | (?<unit_prefix> \#)\W*
629
649
  )
630
- )'
631
- self.place_regexp =
632
- '(?:' + city_and_state_regexp + '\W*)?
633
- (?:' + zip_regexp + ')?'
634
-
635
- self.address_regexp =
636
- '\A\W*
637
- (' + number_regexp + ')\W*
638
- (?:' + fraction_regexp + '\W*)?' +
639
- street_regexp + '\W+
640
- (?:' + unit_regexp + '\W+)?' +
641
- place_regexp +
642
- '\W*\Z'
643
-
644
- self.informal_address_regexp =
645
- '\A\s*
646
- (' + number_regexp + ')\W*
647
- (?:' + fraction_regexp + '\W*)?' +
648
- street_regexp + '(?:\W+|\Z)
649
- (?:' + unit_regexp + '(?:\W+|\Z))?' +
650
- '(?:' + place_regexp + ')?'
651
-
652
- =begin rdoc
653
-
654
- parses either an address or intersection and returns an instance of
655
- StreetAddress::US::Address or nil if the location cannot be parsed
656
-
657
- pass the arguement, :informal => true, to make parsing more lenient
658
-
659
- ====example
660
- StreetAddress::US.parse('1600 Pennsylvania Ave Washington, DC 20006')
661
- or:
662
- StreetAddress::US.parse('Hollywood & Vine, Los Angeles, CA')
663
- or
664
- StreetAddress::US.parse("1600 Pennsylvania Ave", :informal => true)
665
-
666
- =end
650
+ (?<unit> [\w-]+)
651
+ )
652
+ |
653
+ #{unit_prefix_unnumbered_regexp}
654
+ /ix;
655
+
656
+ self.city_and_state_regexp = /
657
+ (?:
658
+ (?<city> [^\d,]+?)\W+
659
+ (?<state> #{state_regexp})
660
+ )
661
+ /ix;
662
+
663
+ self.place_regexp = /
664
+ (?:#{city_and_state_regexp}\W*)? (?:#{zip_regexp})?
665
+ /ix;
666
+
667
+ self.address_regexp = /
668
+ \A
669
+ [^\w\x23]* # skip non-word chars except # (eg unit)
670
+ #{number_regexp} \W*
671
+ (?:#{fraction_regexp}\W*)?
672
+ #{street_regexp}\W+
673
+ (?:#{unit_regexp}\W+)?
674
+ #{place_regexp}
675
+ \W* # require on non-word chars at end
676
+ \z # right up to end of string
677
+ /ix;
678
+
679
+ self.sep_regexp = /(?:\W+|\Z)/;
680
+ self.sep_avoid_unit_regexp = /(?:[^\#\w]+|\Z)/;
681
+
682
+ self.informal_address_regexp = /
683
+ \A
684
+ \s* # skip leading whitespace
685
+ (?:#{unit_regexp} #{sep_regexp})?
686
+ (?:#{number_regexp})? \W*
687
+ (?:#{fraction_regexp} \W*)?
688
+ #{street_regexp} #{sep_avoid_unit_regexp}
689
+ (?:#{unit_regexp} #{sep_regexp})?
690
+ (?:#{place_regexp})?
691
+ # don't require match to reach end of string
692
+ /ix;
693
+
694
+ self.intersection_regexp = /\A\W*
695
+ #{street_regexp}\W*?
696
+
697
+ \s+#{corner_regexp}\s+
698
+
699
+ # (?{ exists $_{$_} and $_{$_.1} = delete $_{$_} for (qw{prefix street type suffix})})
700
+ #{street_regexp}\W+
701
+ # (?{ exists $_{$_} and $_{$_.2} = delete $_{$_} for (qw{prefix street type suffix})})
702
+
703
+ #{place_regexp}
704
+ \W*\z
705
+ /ix;
706
+
667
707
  class << self
668
- def parse(location, args = {})
669
- if Regexp.new(corner_regexp, Regexp::IGNORECASE).match(location)
670
- parse_intersection(location)
671
- elsif args[:informal]
672
- parse_address(location) || parse_informal_address(location)
673
- else
674
- parse_address(location);
708
+ def parse(location, args={})
709
+ if( corner_regexp.match(location) )
710
+ return parse_intersection(location, args)
711
+ else
712
+ return parse_address(location, args) || parse_informal_address(location, args)
675
713
  end
676
714
  end
677
- =begin rdoc
678
-
679
- parses only an intersection and returnsan instance of
680
- StreetAddress::US::Address or nil if the intersection cannot be parsed
681
-
682
- ====example
683
- address = StreetAddress::US.parse('Hollywood & Vine, Los Angeles, CA')
684
- assert address.intersection?
685
-
686
- =end
687
- def parse_intersection(inter)
688
- regex = Regexp.new(
689
- '\A\W*' + street_regexp + '\W*?
690
- \s+' + corner_regexp + '\s+' +
691
- street_regexp + '\W+' +
692
- place_regexp + '\W*\Z', Regexp::IGNORECASE + Regexp::EXTENDED
693
- )
694
-
695
- return unless match = regex.match(inter)
696
-
697
- normalize_address(
698
- StreetAddress::US::Address.new(
699
- :street => match[4] || match[9],
700
- :street_type => match[5],
701
- :suffix => match[6],
702
- :prefix => match[3],
703
- :street2 => match[15] || match[20],
704
- :street_type2 => match[16],
705
- :suffix2 => match[17],
706
- :prefix2 => match[14],
707
- :city => match[23],
708
- :state => match[24],
709
- :postal_code => match[25]
710
- )
711
- )
715
+
716
+ def parse_address(address, args={})
717
+ return unless match = address_regexp.match(address)
718
+
719
+ to_address( match_to_hash(match), args )
712
720
  end
713
-
714
- =begin rdoc
715
-
716
- parses only an address and returnsan instance of
717
- StreetAddress::US::Address or nil if the address cannot be parsed
718
-
719
- ====example
720
- address = StreetAddress::US.parse('1600 Pennsylvania Ave Washington, DC 20006')
721
- assert !address.intersection?
722
-
723
- =end
724
- def parse_address(addr)
725
- regex = Regexp.new(address_regexp, Regexp::IGNORECASE + Regexp::EXTENDED)
726
-
727
- return unless match = regex.match(addr)
728
-
729
- normalize_address(
730
- StreetAddress::US::Address.new(
731
- :number => match[1],
732
- :street => match[5] || match[10] || match[2],
733
- :street_type => match[6] || match[3],
734
- :unit => match[14],
735
- :unit_prefix => match[13],
736
- :suffix => match[7] || match[12],
737
- :prefix => match[4],
738
- :city => match[15],
739
- :state => match[16],
740
- :postal_code => match[17],
741
- :postal_code_ext => match[18]
742
- )
743
- )
721
+
722
+ def parse_informal_address(address, args={})
723
+ return unless match = informal_address_regexp.match(address)
724
+
725
+ to_address( match_to_hash(match), args )
744
726
  end
745
727
 
746
- def parse_informal_address(addr)
747
- regex = Regexp.new(informal_address_regexp, Regexp::IGNORECASE + Regexp::EXTENDED)
748
-
749
- return unless match = regex.match(addr)
750
-
751
- normalize_address(
752
- StreetAddress::US::Address.new(
753
- :number => match[1],
754
- :street => match[5] || match[10] || match[2],
755
- :street_type => match[6] || match[3],
756
- :unit => match[14],
757
- :unit_prefix => match[13],
758
- :suffix => match[7] || match[12],
759
- :prefix => match[4],
760
- :city => match[15],
761
- :state => match[16],
762
- :postal_code => match[17],
763
- :postal_code_ext => match[18]
764
- )
728
+ def parse_intersection(intersection, args)
729
+ return unless match = intersection_regexp.match(intersection)
730
+
731
+ hash = match_to_hash(match)
732
+
733
+ streets = intersection_regexp.named_captures["street"].map { |pos|
734
+ match[pos.to_i]
735
+ }.select { |v| v }
736
+ hash["street"] = streets[0] if streets[0]
737
+ hash["street2"] = streets[1] if streets[1]
738
+
739
+ street_types = intersection_regexp.named_captures["street_type"].map { |pos|
740
+ match[pos.to_i]
741
+ }.select { |v| v }
742
+ hash["street_type"] = street_types[0] if street_types[0]
743
+ hash["street_type2"] = street_types[1] if street_types[1]
744
+
745
+ if(
746
+ hash["street_type"] &&
747
+ (
748
+ !hash["street_type2"] ||
749
+ (hash["street_type"] == hash["street_type2"])
750
+ )
765
751
  )
752
+ type = hash["street_type"].clone
753
+ if( type.gsub!(/s\W*$/i, '') && /\A#{street_type_regexp}\z/i =~ type )
754
+ hash["street_type"] = hash["street_type2"] = type
755
+ end
756
+ end
757
+
758
+ to_address( hash, args )
766
759
  end
767
-
760
+
768
761
  private
769
- def normalize_address(addr)
770
- addr.state = normalize_state(addr.state) unless addr.state.nil?
771
- addr.street_type = normalize_street_type(addr.street_type) unless addr.street_type.nil?
772
- addr.prefix = normalize_directional(addr.prefix) unless addr.prefix.nil?
773
- addr.suffix = normalize_directional(addr.suffix) unless addr.suffix.nil?
774
- addr.street.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street.nil?
775
- addr.street_type2 = normalize_street_type(addr.street_type2) unless addr.street_type2.nil?
776
- addr.prefix2 = normalize_directional(addr.prefix2) unless addr.prefix2.nil?
777
- addr.suffix2 = normalize_directional(addr.suffix2) unless addr.suffix2.nil?
778
- addr.street2.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street2.nil?
779
- addr.city.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.city.nil?
780
- addr.unit_prefix.capitalize! unless addr.unit_prefix.nil?
781
- return addr
782
- end
783
-
784
- def normalize_state(state)
785
- if state.length < 3
786
- state.upcase
787
- else
788
- STATE_CODES[state.downcase]
762
+ def match_to_hash(match)
763
+ hash = {}
764
+ match.names.each { |name| hash[name] = match[name] if match[name] }
765
+ return hash
789
766
  end
790
- end
791
-
792
- def normalize_street_type(s_type)
793
- s_type.downcase!
794
- s_type = STREET_TYPES[s_type] || s_type if STREET_TYPES_LIST[s_type]
795
- s_type.capitalize
796
- end
797
-
798
- def normalize_directional(dir)
799
- if dir.length < 3
800
- dir.upcase
801
- else
802
- DIRECTIONAL[dir.downcase]
767
+
768
+ def to_address(input, args)
769
+ # strip off some punctuation and whitespace
770
+ input.values.each { |string|
771
+ string.strip!
772
+ string.gsub!(/[^\w\s\-\#\&]/, '')
773
+ }
774
+
775
+ input['redundant_street_type'] = false
776
+ if( input['street'] && !input['street_type'] )
777
+ match = street_regexp.match(input['street'])
778
+ input['street_type'] = match['street_type']
779
+ input['redundant_street_type'] = true
780
+ end
781
+
782
+ NORMALIZE_MAP.each_pair { |key, map|
783
+ next unless input[key]
784
+ mapping = map[input[key].downcase]
785
+ input[key] = mapping if mapping
786
+ }
787
+
788
+ if( args[:avoid_redundant_street_type] )
789
+ ['', '1', '2'].each { |suffix|
790
+ street = input['street' + suffix];
791
+ type = input['street_type' + suffix];
792
+ next if !street || !type
793
+
794
+ type_regexp = street_type_matches[type.downcase] # || fail "No STREET_TYPE_MATCH for #{type}"
795
+ input.delete('street_type' + suffix) if type_regexp.match(street)
796
+ }
797
+ end
798
+
799
+ # attempt to expand directional prefixes on place names
800
+ if( input['city'] )
801
+ input['city'].gsub!(/^(#{dircode_regexp})\s+(?=\S)/) { |match|
802
+ DIRECTION_CODES[match[0].upcase] + ' '
803
+ }
804
+ end
805
+
806
+ %w(street street_type street2 street_type2 city unit_prefix).each do |k|
807
+ input[k] = input[k].split.map(&:capitalize).join(' ') if input[k]
808
+ end
809
+
810
+ return StreetAddress::US::Address.new( input )
803
811
  end
804
- end
805
812
  end
806
813
 
807
- =begin rdoc
808
-
809
- This is class returned by StreetAddress::US::parse, StreetAddress::US::parse_address
810
- and StreetAddress::US::parse_intersection. If an instance represents an intersection
811
- the attribute street2 will be populated.
812
-
813
- =end
814
814
  class Address
815
815
  attr_accessor(
816
- :number,
817
- :street,
818
- :street_type,
819
- :unit,
820
- :unit_prefix,
821
- :suffix,
822
- :prefix,
823
- :city,
824
- :state,
825
- :postal_code,
826
- :postal_code_ext,
827
- :street2,
828
- :street_type2,
829
- :suffix2,
830
- :prefix2
816
+ :number,
817
+ :street,
818
+ :street_type,
819
+ :unit,
820
+ :unit_prefix,
821
+ :suffix,
822
+ :prefix,
823
+ :city,
824
+ :state,
825
+ :postal_code,
826
+ :postal_code_ext,
827
+ :street2,
828
+ :street_type2,
829
+ :suffix2,
830
+ :prefix2,
831
+ :redundant_street_type
831
832
  )
832
833
 
833
834
  def initialize(args)
834
- args.keys.each do |attrib|
835
- self.send("#{attrib}=", args[attrib])
835
+ args.each do |attr, val|
836
+ public_send("#{attr}=", val)
836
837
  end
837
- return
838
838
  end
839
839
 
840
+
841
+ def full_postal_code
842
+ return nil unless self.postal_code
843
+ self.postal_code_ext ? "#{postal_code}-#{postal_code_ext}" : self.postal_code
844
+ end
845
+
846
+
840
847
  def state_fips
841
848
  StreetAddress::US::FIPS_STATES[state]
842
849
  end
843
850
 
851
+
844
852
  def state_name
845
- name = StreetAddress::US::STATE_NAMES[state] and name.capitalize
853
+ name = StreetAddress::US::STATE_NAMES[state]
854
+ name&.split&.map(&:capitalize)&.join(" ")
846
855
  end
847
856
 
857
+
848
858
  def intersection?
849
859
  !street2.nil?
850
860
  end
851
861
 
852
- def line1(s = "")
853
- return if intersection?
854
- s += number
855
- s += " " + prefix unless prefix.nil?
856
- s += " " + street unless street.nil?
857
- s += " " + street_type unless street_type.nil?
858
- if( !unit_prefix.nil? && !unit.nil? )
859
- s += " " + unit_prefix
860
- s += " " + unit
861
- elsif( unit_prefix.nil? && !unit.nil? )
862
- s += " #" + unit
862
+
863
+ def line1
864
+ parts = []
865
+ if intersection?
866
+ parts << prefix if prefix
867
+ parts << street
868
+ parts << street_type if street_type
869
+ parts << suffix if suffix
870
+ parts << "and"
871
+ parts << prefix2 if prefix2
872
+ parts << street2
873
+ parts << street_type2 if street_type2
874
+ parts << suffix2 if suffix2
875
+ else
876
+ parts << number
877
+ parts << prefix if prefix
878
+ parts << street if street
879
+ parts << street_type if street_type && !redundant_street_type
880
+ parts << suffix if suffix
881
+ parts << unit_prefix if unit_prefix
882
+ # follow USPS guidelines: http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf pg28
883
+ parts << (unit_prefix ? unit : "# #{unit}") if unit
884
+ end
885
+ parts.join(" ").strip
886
+ end
887
+
888
+
889
+ def line2
890
+ parts = []
891
+ parts << city if city
892
+ parts << state if state
893
+ s = parts.join(", ")
894
+ if postal_code
895
+ s = "#{s} #{postal_code}"
896
+ s = "#{s}-#{postal_code_ext}" if postal_code_ext
863
897
  end
864
- s += " " + suffix unless suffix.nil?
865
- return s
898
+ s.strip
866
899
  end
867
900
 
901
+
868
902
  def to_s(format = :default)
869
- s = ""
870
903
  case format
871
904
  when :line1
872
- s += line1(s)
905
+ line1
906
+ when :line2
907
+ line2
873
908
  else
874
- if intersection?
875
- s += prefix + " " unless prefix.nil?
876
- s += street
877
- s += " " + street_type unless street_type.nil?
878
- s += " " + suffix unless suffix.nil?
879
- s += " and"
880
- s += " " + prefix2 unless prefix2.nil?
881
- s += " " + street2
882
- s += " " + street_type2 unless street_type2.nil?
883
- s += " " + suffix2 unless suffix2.nil?
884
- s += ", " + city unless city.nil?
885
- s += ", " + state unless state.nil?
886
- s += " " + postal_code unless postal_code.nil?
887
- else
888
- s += line1(s)
889
- s += ", " + city unless city.nil?
890
- s += ", " + state unless state.nil?
891
- s += " " + postal_code unless postal_code.nil?
892
- s += "-" + postal_code_ext unless postal_code_ext.nil?
893
- end
909
+ [line1, line2].reject(&:empty?).join(", ")
894
910
  end
895
- return s
896
- end
911
+ end
912
+
913
+
914
+ def to_h
915
+ self.instance_variables.each_with_object({}) do |var_name, hash|
916
+ var_value = self.instance_variable_get(var_name)
917
+ hash_name = var_name[1..-1].to_sym
918
+ hash[hash_name] = var_value
919
+ end
920
+ end
921
+
922
+ def ==(other)
923
+ return false if other.nil?
924
+ to_s == other.to_s
925
+ end
897
926
  end
898
927
  end
899
928
  end