StreetAddress 1.0.6 → 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.6'
5
+ VERSION = '2.0.0'
6
+
68
7
  DIRECTIONAL = {
69
8
  "north" => "N",
70
9
  "northeast" => "NE",
@@ -74,379 +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
- "cur" => "curv",
169
- "dale" => "dl",
170
- "dam" => "dm",
171
- "div" => "dv",
172
- "divide" => "dv",
173
- "driv" => "dr",
174
- "drive" => "dr",
175
- "drives" => "drs",
176
- "drv" => "dr",
177
- "dvd" => "dv",
178
- "estate" => "est",
179
- "estates" => "ests",
180
- "exp" => "expy",
181
- "expr" => "expy",
182
- "express" => "expy",
183
- "expressway" => "expy",
184
- "expw" => "expy",
185
- "extension" => "ext",
186
- "extensions" => "exts",
187
- "extn" => "ext",
188
- "extnsn" => "ext",
189
- "falls" => "fls",
190
- "ferry" => "fry",
191
- "field" => "fld",
192
- "fields" => "flds",
193
- "flat" => "flt",
194
- "flats" => "flts",
195
- "ford" => "frd",
196
- "fords" => "frds",
197
- "forest" => "frst",
198
- "forests" => "frst",
199
- "forg" => "frg",
200
- "forge" => "frg",
201
- "forges" => "frgs",
202
- "fork" => "frk",
203
- "forks" => "frks",
204
- "fort" => "ft",
205
- "freeway" => "fwy",
206
- "freewy" => "fwy",
207
- "frry" => "fry",
208
- "frt" => "ft",
209
- "frway" => "fwy",
210
- "frwy" => "fwy",
211
- "garden" => "gdn",
212
- "gardens" => "gdns",
213
- "gardn" => "gdn",
214
- "gateway" => "gtwy",
215
- "gatewy" => "gtwy",
216
- "gatway" => "gtwy",
217
- "glen" => "gln",
218
- "glens" => "glns",
219
- "grden" => "gdn",
220
- "grdn" => "gdn",
221
- "grdns" => "gdns",
222
- "green" => "grn",
223
- "greens" => "grns",
224
- "grov" => "grv",
225
- "grove" => "grv",
226
- "groves" => "grvs",
227
- "gtway" => "gtwy",
228
- "harb" => "hbr",
229
- "harbor" => "hbr",
230
- "harbors" => "hbrs",
231
- "harbr" => "hbr",
232
- "haven" => "hvn",
233
- "havn" => "hvn",
234
- "height" => "hts",
235
- "heights" => "hts",
236
- "hgts" => "hts",
237
- "highway" => "hwy",
238
- "highwy" => "hwy",
239
- "hill" => "hl",
240
- "hills" => "hls",
241
- "hiway" => "hwy",
242
- "hiwy" => "hwy",
243
- "hllw" => "holw",
244
- "hollow" => "holw",
245
- "hollows" => "holw",
246
- "holws" => "holw",
247
- "hrbor" => "hbr",
248
- "ht" => "hts",
249
- "hway" => "hwy",
250
- "inlet" => "inlt",
251
- "island" => "is",
252
- "islands" => "iss",
253
- "isles" => "isle",
254
- "islnd" => "is",
255
- "islnds" => "iss",
256
- "jction" => "jct",
257
- "jctn" => "jct",
258
- "jctns" => "jcts",
259
- "junction" => "jct",
260
- "junctions" => "jcts",
261
- "junctn" => "jct",
262
- "juncton" => "jct",
263
- "key" => "ky",
264
- "keys" => "kys",
265
- "knol" => "knl",
266
- "knoll" => "knl",
267
- "knolls" => "knls",
268
- "la" => "ln",
269
- "lake" => "lk",
270
- "lakes" => "lks",
271
- "landing" => "lndg",
272
- "lane" => "ln",
273
- "lanes" => "ln",
274
- "ldge" => "ldg",
275
- "light" => "lgt",
276
- "lights" => "lgts",
277
- "lndng" => "lndg",
278
- "loaf" => "lf",
279
- "lock" => "lck",
280
- "locks" => "lcks",
281
- "lodg" => "ldg",
282
- "lodge" => "ldg",
283
- "loops" => "loop",
284
- "manor" => "mnr",
285
- "manors" => "mnrs",
286
- "meadow" => "mdw",
287
- "meadows" => "mdws",
288
- "medows" => "mdws",
289
- "mill" => "ml",
290
- "mills" => "mls",
291
- "mission" => "msn",
292
- "missn" => "msn",
293
- "mnt" => "mt",
294
- "mntain" => "mtn",
295
- "mntn" => "mtn",
296
- "mntns" => "mtns",
297
- "motorway" => "mtwy",
298
- "mount" => "mt",
299
- "mountain" => "mtn",
300
- "mountains" => "mtns",
301
- "mountin" => "mtn",
302
- "mssn" => "msn",
303
- "mtin" => "mtn",
304
- "neck" => "nck",
305
- "orchard" => "orch",
306
- "orchrd" => "orch",
307
- "overpass" => "opas",
308
- "ovl" => "oval",
309
- "parks" => "park",
310
- "parkway" => "pkwy",
311
- "parkways" => "pkwy",
312
- "parkwy" => "pkwy",
313
- "passage" => "psge",
314
- "paths" => "path",
315
- "pikes" => "pike",
316
- "pine" => "pne",
317
- "pines" => "pnes",
318
- "pk" => "park",
319
- "pkway" => "pkwy",
320
- "pkwys" => "pkwy",
321
- "pky" => "pkwy",
322
- "place" => "pl",
323
- "plain" => "pln",
324
- "plaines" => "plns",
325
- "plains" => "plns",
326
- "plaza" => "plz",
327
- "plza" => "plz",
328
- "point" => "pt",
329
- "points" => "pts",
330
- "port" => "prt",
331
- "ports" => "prts",
332
- "prairie" => "pr",
333
- "prarie" => "pr",
334
- "prk" => "park",
335
- "prr" => "pr",
336
- "rad" => "radl",
337
- "radial" => "radl",
338
- "radiel" => "radl",
339
- "ranch" => "rnch",
340
- "ranches" => "rnch",
341
- "rapid" => "rpd",
342
- "rapids" => "rpds",
343
- "rdge" => "rdg",
344
- "rest" => "rst",
345
- "ridge" => "rdg",
346
- "ridges" => "rdgs",
347
- "river" => "riv",
348
- "rivr" => "riv",
349
- "rnchs" => "rnch",
350
- "road" => "rd",
351
- "roads" => "rds",
352
- "route" => "rte",
353
- "rvr" => "riv",
354
- "shoal" => "shl",
355
- "shoals" => "shls",
356
- "shoar" => "shr",
357
- "shoars" => "shrs",
358
- "shore" => "shr",
359
- "shores" => "shrs",
360
- "skyway" => "skwy",
361
- "spng" => "spg",
362
- "spngs" => "spgs",
363
- "spring" => "spg",
364
- "springs" => "spgs",
365
- "sprng" => "spg",
366
- "sprngs" => "spgs",
367
- "spurs" => "spur",
368
- "sqr" => "sq",
369
- "sqre" => "sq",
370
- "sqrs" => "sqs",
371
- "squ" => "sq",
372
- "square" => "sq",
373
- "squares" => "sqs",
374
- "station" => "sta",
375
- "statn" => "sta",
376
- "stn" => "sta",
377
- "str" => "st",
378
- "strav" => "stra",
379
- "strave" => "stra",
380
- "straven" => "stra",
381
- "stravenue" => "stra",
382
- "stravn" => "stra",
383
- "stream" => "strm",
384
- "street" => "st",
385
- "streets" => "sts",
386
- "streme" => "strm",
387
- "strt" => "st",
388
- "strvn" => "stra",
389
- "strvnue" => "stra",
390
- "sumit" => "smt",
391
- "sumitt" => "smt",
392
- "summit" => "smt",
393
- "terr" => "ter",
394
- "terrace" => "ter",
395
- "throughway" => "trwy",
396
- "tpk" => "tpke",
397
- "tr" => "trl",
398
- "trace" => "trce",
399
- "traces" => "trce",
400
- "track" => "trak",
401
- "tracks" => "trak",
402
- "trafficway" => "trfy",
403
- "trail" => "trl",
404
- "trails" => "trl",
405
- "trk" => "trak",
406
- "trks" => "trak",
407
- "trls" => "trl",
408
- "trnpk" => "tpke",
409
- "trpk" => "tpke",
410
- "tunel" => "tunl",
411
- "tunls" => "tunl",
412
- "tunnel" => "tunl",
413
- "tunnels" => "tunl",
414
- "tunnl" => "tunl",
415
- "turnpike" => "tpke",
416
- "turnpk" => "tpke",
417
- "underpass" => "upas",
418
- "union" => "un",
419
- "unions" => "uns",
420
- "valley" => "vly",
421
- "valleys" => "vlys",
422
- "vally" => "vly",
423
- "vdct" => "via",
424
- "viadct" => "via",
425
- "viaduct" => "via",
426
- "view" => "vw",
427
- "views" => "vws",
428
- "vill" => "vlg",
429
- "villag" => "vlg",
430
- "village" => "vlg",
431
- "villages" => "vlgs",
432
- "ville" => "vl",
433
- "villg" => "vlg",
434
- "villiage" => "vlg",
435
- "vist" => "vis",
436
- "vista" => "vis",
437
- "vlly" => "vly",
438
- "vst" => "vis",
439
- "vsta" => "vis",
440
- "walks" => "walk",
441
- "well" => "wl",
442
- "wells" => "wls",
443
- "wy" => "way"
444
- }
445
-
446
- STREET_TYPES_LIST = {}
447
- STREET_TYPES.to_a.each do |item|
448
- STREET_TYPES_LIST[item[0]] = true
449
- 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
450
391
  end
451
392
 
452
393
  STATE_CODES = {
@@ -509,9 +450,9 @@ module StreetAddress
509
450
  "west virginia" => "WV",
510
451
  "wisconsin" => "WI",
511
452
  "wyoming" => "WY"
512
- }
453
+ }.freeze
513
454
 
514
- STATE_NAMES = STATE_CODES.invert
455
+ STATE_NAMES = STATE_CODES.invert.freeze
515
456
 
516
457
  STATE_FIPS = {
517
458
  "01" => "AL",
@@ -567,252 +508,309 @@ module StreetAddress
567
508
  "56" => "WY",
568
509
  "72" => "PR",
569
510
  "78" => "VI"
570
- }
571
-
572
- FIPS_STATES = STATE_FIPS.invert
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
573
527
 
574
528
  class << self
575
529
  attr_accessor(
576
530
  :street_type_regexp,
531
+ :street_type_matches,
577
532
  :number_regexp,
578
533
  :fraction_regexp,
579
534
  :state_regexp,
580
535
  :city_and_state_regexp,
581
- :direct_regexp,
536
+ :direct_regexp,
582
537
  :zip_regexp,
583
538
  :corner_regexp,
584
- :unit_regexp,
585
539
  :street_regexp,
586
540
  :place_regexp,
587
541
  :address_regexp,
588
- :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
589
550
  )
590
551
  end
591
552
 
592
- self.street_type_regexp = STREET_TYPES_LIST.keys.join("|")
593
- self.number_regexp = '\d+-?\d*'
594
- self.fraction_regexp = '\d+\/\d+'
595
- self.state_regexp = STATE_CODES.to_a.join("|").gsub(/ /, "\\s")
596
- self.city_and_state_regexp = '
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 = /
597
587
  (?:
598
- ([^\d,]+?)\W+
599
- (' + state_regexp + ')
600
- )'
601
-
602
- self.direct_regexp = DIRECTIONAL.keys.join("|") +
603
- "|" +
604
- DIRECTIONAL.values.sort{ |a,b|
605
- b.length <=> a.length
606
- }.map{ |x|
607
- f = x.gsub(/(\w)/, '\1.')
608
- [Regexp::quote(f), Regexp::quote(x)]
609
- }.join("|")
610
- self.zip_regexp = '(\d{5})(?:-?(\d{4})?)'
611
- self.corner_regexp = '(?:\band\b|\bat\b|&|\@)'
612
- self.unit_regexp = '(?:(su?i?te|p\W*[om]\W*b(?:ox)?|dept|apt|apartment|ro*m|fl|unit|box)\W+|(\#)\W*)([\w-]+)'
613
- self.street_regexp =
614
- '(?:
615
- (?:(' + direct_regexp + ')\W+
616
- (' + 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)
617
597
  |
618
- (?:(' + direct_regexp + ')\W+)?
619
- (?:
620
- ([^,]+)
621
- (?:[^\w,]+(' + street_type_regexp + ')\b)
622
- (?:[^\w,]+(' + direct_regexp + ')\b)?
623
- |
624
- ([^,]*\d)
625
- (' + direct_regexp + ')\b
626
- |
627
- ([^,]+?)
628
- (?:[^\w,]+(' + street_type_regexp + ')\b)?
629
- (?:[^\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*
630
649
  )
631
- )'
632
- self.place_regexp =
633
- '(?:' + city_and_state_regexp + '\W*)?
634
- (?:' + zip_regexp + ')?'
635
-
636
- self.address_regexp =
637
- '\A[^\w\#]*
638
- (' + number_regexp + ')\W*
639
- (?:' + fraction_regexp + '\W*)?' +
640
- street_regexp + '\W+
641
- (?:' + unit_regexp + '\W+)?' +
642
- place_regexp +
643
- '\W*\Z'
644
-
645
- self.informal_address_regexp =
646
- '\A\s*
647
- (?:' + unit_regexp + '(?:\W+|\Z))?
648
- (' + number_regexp + ')\W*
649
- (?:' + fraction_regexp + '\W*)?' +
650
- street_regexp + '(?:[^\#\w]+|\Z)
651
- (?:' + unit_regexp + '(?:\W+|\Z))?' +
652
- '(?:' + place_regexp + ')?'
653
-
654
- =begin rdoc
655
-
656
- parses either an address or intersection and returns an instance of
657
- StreetAddress::US::Address or nil if the location cannot be parsed
658
-
659
- pass the arguement, :informal => true, to make parsing more lenient
660
-
661
- ====example
662
- StreetAddress::US.parse('1600 Pennsylvania Ave Washington, DC 20006')
663
- or:
664
- StreetAddress::US.parse('Hollywood & Vine, Los Angeles, CA')
665
- or
666
- StreetAddress::US.parse("1600 Pennsylvania Ave", :informal => true)
667
-
668
- =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
+
669
707
  class << self
670
- def parse(location, args = {})
671
- if Regexp.new(corner_regexp, Regexp::IGNORECASE).match(location)
672
- parse_intersection(location)
673
- elsif args[:informal]
674
- parse_address(location) || parse_informal_address(location)
708
+ def parse(location, args={})
709
+ if( corner_regexp.match(location) )
710
+ return parse_intersection(location, args)
675
711
  else
676
- parse_address(location);
712
+ return parse_address(location, args) || parse_informal_address(location, args)
677
713
  end
678
714
  end
679
- =begin rdoc
680
-
681
- parses only an intersection and returns an instance of
682
- StreetAddress::US::Address or nil if the intersection cannot be parsed
683
-
684
- ====example
685
- address = StreetAddress::US.parse('Hollywood & Vine, Los Angeles, CA')
686
- assert address.intersection?
687
-
688
- =end
689
- def parse_intersection(inter)
690
- regex = Regexp.new(
691
- '\A\W*' + street_regexp + '\W*?
692
- \s+' + corner_regexp + '\s+' +
693
- street_regexp + '\W+' +
694
- place_regexp + '\W*\Z', Regexp::IGNORECASE + Regexp::EXTENDED
695
- )
696
715
 
697
- return unless match = regex.match(inter)
698
-
699
- normalize_address(
700
- StreetAddress::US::Address.new(
701
- :street => match[4] || match[9],
702
- :street_type => match[5],
703
- :suffix => match[6],
704
- :prefix => match[3],
705
- :street2 => match[15] || match[20],
706
- :street_type2 => match[16],
707
- :suffix2 => match[17],
708
- :prefix2 => match[14],
709
- :city => match[23],
710
- :state => match[24],
711
- :postal_code => match[25]
712
- )
713
- )
714
- end
716
+ def parse_address(address, args={})
717
+ return unless match = address_regexp.match(address)
715
718
 
716
- =begin rdoc
717
-
718
- parses only an address and returns an instance of
719
- StreetAddress::US::Address or nil if the address cannot be parsed
720
-
721
- ====example
722
- address = StreetAddress::US.parse('1600 Pennsylvania Ave Washington, DC 20006')
723
- assert !address.intersection?
724
-
725
- =end
726
- def parse_address(addr)
727
- regex = Regexp.new(address_regexp, Regexp::IGNORECASE + Regexp::EXTENDED)
728
-
729
- return unless match = regex.match(addr)
730
-
731
- normalize_address(
732
- StreetAddress::US::Address.new(
733
- :number => match[1],
734
- :street => match[5] || match[10] || match[2],
735
- :street_type => match[6] || match[3],
736
- :unit => match[15],
737
- :unit_prefix => match[13] || match[14],
738
- :suffix => match[7] || match[12],
739
- :prefix => match[4],
740
- :city => match[16],
741
- :state => match[17],
742
- :postal_code => match[18],
743
- :postal_code_ext => match[19]
744
- )
745
- )
719
+ to_address( match_to_hash(match), args )
746
720
  end
747
721
 
748
- def parse_informal_address(addr)
749
- regex = Regexp.new(informal_address_regexp, Regexp::IGNORECASE + Regexp::EXTENDED)
750
-
751
- return unless match = regex.match(addr)
752
-
753
- normalize_address(
754
- StreetAddress::US::Address.new(
755
- :number => match[4],
756
- :street => match[8] || match[13] || match[5],
757
- :street_type => match[9] || match[6],
758
- :unit => match[18] || match[3],
759
- :unit_prefix => match[16] || match[17] || match[1] || match[2],
760
- :suffix => match[10] || match[15],
761
- :prefix => match[7],
762
- :city => match[19],
763
- :state => match[20],
764
- :postal_code => match[21],
765
- :postal_code_ext => match[22]
766
- )
767
- )
768
- end
722
+ def parse_informal_address(address, args={})
723
+ return unless match = informal_address_regexp.match(address)
769
724
 
770
- private
771
- def normalize_address(addr)
772
- addr.state = normalize_state(addr.state) unless addr.state.nil?
773
- addr.street_type = normalize_street_type(addr.street_type) unless addr.street_type.nil?
774
- addr.prefix = normalize_directional(addr.prefix) unless addr.prefix.nil?
775
- addr.suffix = normalize_directional(addr.suffix) unless addr.suffix.nil?
776
- addr.street.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street.nil?
777
- addr.street_type2 = normalize_street_type(addr.street_type2) unless addr.street_type2.nil?
778
- addr.prefix2 = normalize_directional(addr.prefix2) unless addr.prefix2.nil?
779
- addr.suffix2 = normalize_directional(addr.suffix2) unless addr.suffix2.nil?
780
- addr.street2.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street2.nil?
781
- addr.city.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.city.nil?
782
- addr.unit_prefix.capitalize! unless addr.unit_prefix.nil?
783
- return addr
725
+ to_address( match_to_hash(match), args )
784
726
  end
785
727
 
786
- def normalize_state(state)
787
- if state.length < 3
788
- state.upcase
789
- else
790
- STATE_CODES[state.downcase]
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
+ )
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
791
756
  end
792
- end
793
757
 
794
- def normalize_street_type(s_type)
795
- s_type.downcase!
796
- s_type = STREET_TYPES[s_type] || s_type if STREET_TYPES_LIST[s_type]
797
- s_type.capitalize
758
+ to_address( hash, args )
798
759
  end
799
760
 
800
- def normalize_directional(dir)
801
- if dir.length < 3
802
- dir.upcase
803
- else
804
- DIRECTIONAL[dir.downcase]
761
+ private
762
+ def match_to_hash(match)
763
+ hash = {}
764
+ match.names.each { |name| hash[name] = match[name] if match[name] }
765
+ return hash
805
766
  end
806
- end
807
- end
808
767
 
809
- =begin rdoc
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
810
781
 
811
- This is class returned by StreetAddress::US::parse, StreetAddress::US::parse_address
812
- and StreetAddress::US::parse_intersection. If an instance represents an intersection
813
- the attribute street2 will be populated.
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 )
811
+ end
812
+ end
814
813
 
815
- =end
816
814
  class Address
817
815
  attr_accessor(
818
816
  :number,
@@ -829,84 +827,101 @@ module StreetAddress
829
827
  :street2,
830
828
  :street_type2,
831
829
  :suffix2,
832
- :prefix2
830
+ :prefix2,
831
+ :redundant_street_type
833
832
  )
834
833
 
835
834
  def initialize(args)
836
- args.keys.each do |attrib|
837
- self.send("#{attrib}=", args[attrib])
835
+ args.each do |attr, val|
836
+ public_send("#{attr}=", val)
838
837
  end
839
- return
840
838
  end
841
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
+
842
847
  def state_fips
843
848
  StreetAddress::US::FIPS_STATES[state]
844
849
  end
845
850
 
851
+
846
852
  def state_name
847
- name = StreetAddress::US::STATE_NAMES[state] and name.capitalize
853
+ name = StreetAddress::US::STATE_NAMES[state]
854
+ name&.split&.map(&:capitalize)&.join(" ")
848
855
  end
849
856
 
857
+
850
858
  def intersection?
851
859
  !street2.nil?
852
860
  end
853
861
 
854
- def line1(s = "")
862
+
863
+ def line1
864
+ parts = []
855
865
  if intersection?
856
- s += prefix + " " unless prefix.nil?
857
- s += street
858
- s += " " + street_type unless street_type.nil?
859
- s += " " + suffix unless suffix.nil?
860
- s += " and"
861
- s += " " + prefix2 unless prefix2.nil?
862
- s += " " + street2
863
- s += " " + street_type2 unless street_type2.nil?
864
- s += " " + suffix2 unless suffix2.nil?
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
865
875
  else
866
- return if intersection?
867
- s += number
868
- s += " " + prefix unless prefix.nil?
869
- s += " " + street unless street.nil?
870
- s += " " + street_type unless street_type.nil?
871
- if( !unit_prefix.nil? && !unit.nil? )
872
- s += " " + unit_prefix
873
- s += " " + unit
874
- elsif( unit_prefix.nil? && !unit.nil? )
875
- s += " #" + unit
876
- end
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
+
877
888
 
878
- return s
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
879
897
  end
898
+ s.strip
880
899
  end
881
900
 
901
+
882
902
  def to_s(format = :default)
883
- s = ""
884
903
  case format
885
904
  when :line1
886
- s += line1(s)
905
+ line1
906
+ when :line2
907
+ line2
887
908
  else
888
- if intersection?
889
- s += prefix + " " unless prefix.nil?
890
- s += street
891
- s += " " + street_type unless street_type.nil?
892
- s += " " + suffix unless suffix.nil?
893
- s += " and"
894
- s += " " + prefix2 unless prefix2.nil?
895
- s += " " + street2
896
- s += " " + street_type2 unless street_type2.nil?
897
- s += " " + suffix2 unless suffix2.nil?
898
- s += ", " + city unless city.nil?
899
- s += ", " + state unless state.nil?
900
- s += " " + postal_code unless postal_code.nil?
901
- else
902
- s += line1(s)
903
- s += ", " + city unless city.nil?
904
- s += ", " + state unless state.nil?
905
- s += " " + postal_code unless postal_code.nil?
906
- s += "-" + postal_code_ext unless postal_code_ext.nil?
907
- end
909
+ [line1, line2].reject(&:empty?).join(", ")
910
+ 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
908
919
  end
909
- return s
920
+ end
921
+
922
+ def ==(other)
923
+ return false if other.nil?
924
+ to_s == other.to_s
910
925
  end
911
926
  end
912
927
  end