address-standardization 0.4.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ *.gem
3
+ tags
4
+ *.vim*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use --create 1.9.3@address_standardization
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,45 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ address-standardization (0.4.1)
5
+ httparty (~> 0.8.1)
6
+ logging
7
+ mechanize (= 2.0.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ diff-lcs (1.1.3)
13
+ httparty (0.8.1)
14
+ multi_json
15
+ multi_xml
16
+ little-plugger (1.1.3)
17
+ logging (1.6.1)
18
+ little-plugger (>= 1.1.2)
19
+ mechanize (2.0)
20
+ net-http-digest_auth (~> 1.1, >= 1.1.1)
21
+ net-http-persistent (~> 1.8)
22
+ nokogiri (~> 1.4)
23
+ webrobots (~> 0.0, >= 0.0.9)
24
+ multi_json (1.0.4)
25
+ multi_xml (0.4.1)
26
+ net-http-digest_auth (1.2)
27
+ net-http-persistent (1.9)
28
+ nokogiri (1.5.0)
29
+ rspec (2.7.0)
30
+ rspec-core (~> 2.7.0)
31
+ rspec-expectations (~> 2.7.0)
32
+ rspec-mocks (~> 2.7.0)
33
+ rspec-core (2.7.1)
34
+ rspec-expectations (2.7.0)
35
+ diff-lcs (~> 1.1.2)
36
+ rspec-mocks (2.7.0)
37
+ webrobots (0.0.12)
38
+ nokogiri (>= 1.4.4)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ address-standardization!
45
+ rspec (~> 2.7.0)
@@ -0,0 +1,158 @@
1
+ # address_standardization
2
+
3
+ ## What is it?
4
+
5
+ Despite the long name, this is a tiny Ruby library to quickly standardize a
6
+ postal address, either via the Google Geocoding API or MelissaData. So if you
7
+ have an online store and you need to verify a shipping address, then you might
8
+ consider using this library.
9
+
10
+ ## What is it *not*?
11
+
12
+ This is not a library to *geocode* addresses or locations. In other words, you
13
+ won't get world coordinates back for a location. If you need this information,
14
+ then there are other libraries that will do this for you, such as
15
+ [graticule](http://github.com/collectiveidea/graticule) or
16
+ [GeoKit](http://github.com/andre/geokit-gem).
17
+
18
+ ## How do I install it?
19
+
20
+ gem install address_standardization
21
+
22
+ ## How do I use it?
23
+
24
+ Right now this library supports two services: the Google Geocoding API and
25
+ MelissaData. All services work the same way: you call a `standardize_address`
26
+ method with a hash of address data. If the address exists (according to the
27
+ service), you'll get back a full hash of address data. Otherwise, you'll get
28
+ back `nil`.
29
+
30
+ Both Google and MelissaData have benefits and drawbacks. First, in terms of
31
+ postal addresses there are three kinds of addresses you are probably concerned
32
+ with: US addresses, Canadian addresses, and then international addresses.
33
+
34
+ ### Google
35
+
36
+ Google is pretty great in that in knows about all sorts of addresses. So if
37
+ you're trying to verify an address in France, then Google probably knows about
38
+ it. However, it is not very useful in terms of standardizing US addresses,
39
+ because it doesn't know what to do with P.O. boxes.
40
+
41
+ That said, here is how you'd standardize a US address:
42
+
43
+ addr = AddressStandardization::GoogleMaps.standardize_address(
44
+ :street => "1600 Amphitheatre Parkway",
45
+ :city => "Mountain View",
46
+ :state => "CA"
47
+ )
48
+ addr.street #=> "1600 AMPHITHEATRE PKWY"
49
+ addr.city #=> "MOUNTAIN VIEW"
50
+ addr.county #=> "SANTA CLARA"
51
+ addr.state #=> "CA"
52
+ addr.zip #=> "94043"
53
+ addr.country #=> "UNITED STATES"
54
+
55
+ By default, the `standardize_address` method will leave it up to the service to
56
+ decide which country to scope the address query to. Google may be able to figure
57
+ it out based on the address you provide, but you can always narrow it down by
58
+ providing a `:country` key. So for instance, to standardize a Canadian address:
59
+
60
+ addr = AddressStandardization::GoogleMaps.standardize_address(
61
+ :street => "55 East Cordova St. Apt 415",
62
+ :city => "Vancouver",
63
+ :province => "BC",
64
+ :country => "Canada"
65
+ )
66
+ addr.street #=> "55 E CORDOVA ST"
67
+ addr.city #=> "VANCOUVER"
68
+ addr.district #=> "GREATER VANCOUVER REGIONAL DISTRICT"
69
+ addr.province #=> "ON"
70
+ addr.postal_code #=> "V6A 1K3"
71
+ addr.country #=> "CANADA"
72
+
73
+ Note that `province` is an alias for `state`, `district` is an alias for
74
+ `county` and `postal_code` is an alias for `zip`.
75
+
76
+ And to standardize a French address:
77
+
78
+ addr = AddressStandardization::GoogleMaps.standardize_address(
79
+ :street => "Parvis de Notre Dame",
80
+ :city => "Paris",
81
+ :country => "France"
82
+ )
83
+ addr.street #=> "PLACE DU PARVIS NOTRE-DAME"
84
+ addr.city #=> "PARIS"
85
+ addr.region #=> "ÎLE-DE-FRANCE"
86
+ addr.postal_code #=> "75004"
87
+ addr.country #=> "FRANCE"
88
+
89
+ Finally, note what happens if we attempt to standardize a bogus address:
90
+
91
+ addr = AddressStandardization::GoogleMaps.standardize_address(
92
+ :street => "abcdef"
93
+ )
94
+ addr #=> nil
95
+
96
+ ### MelissaData
97
+
98
+ If you want to simply verify US addresses, then MelissaData is a better bet, as
99
+ it not only understands P.O. boxes, but it is also able to tell whether a unit
100
+ or apartment number is left out of the address (although currently this
101
+ information is not used; there is an
102
+ [open ticket](https://github.com/mcmire/address_standardization/issues/12) to
103
+ do this).
104
+
105
+ Standardizing a US address works much the same way as with Google:
106
+
107
+ addr = AddressStandardization::MelissaData.standardize_address(
108
+ :street => "1600 Amphitheatre Parkway",
109
+ :city => "Mountain View",
110
+ :state => "CA"
111
+ )
112
+ addr.street #=> "1600 AMPHITHEATRE PKWY"
113
+ addr.city #=> "MOUNTAIN VIEW"
114
+ addr.county #=> "SANTA CLARA"
115
+ addr.state #=> "CA"
116
+ addr.zip #=> "94043-1351"
117
+ addr.country #=> "UNITED STATES"
118
+
119
+ As mentioned, you can also do P.O. boxes:
120
+
121
+ addr = AddressStandardization::MelissaData.standardize_address(
122
+ :street => 'P.O. Box 400160',
123
+ :city => 'Charlottesville',
124
+ :state => 'VA'
125
+ )
126
+ addr.street #=> "PO BOX 400160"
127
+ addr.city #=> "CHARLOTTESVILLE"
128
+ addr.county #=> "ALBEMARLE"
129
+ addr.state #=> "VA"
130
+ addr.zip #=> "22904-4160"
131
+ addr.country #=> "UNITED STATES"
132
+
133
+ ## How do I hack on it?
134
+
135
+ * Run `bundle install` to ensure you have all dependencies.
136
+ * Add your changes.
137
+ * Write some tests! Tests are written in RSpec, so `rspec FILE` to run a
138
+ single test, and `rake test` to run all of the tests.
139
+ * If you're feeling generous, fork the project and submit a pull request. I'll
140
+ take a look at it when I can.
141
+
142
+ ## Contributors
143
+
144
+ * Evan Whalen ([evanwhalen](http://github.com/evanwhalen))
145
+ * Ryan Heneise ([mysmallidea](http://github.com/mysmallidea))
146
+
147
+ ## Support
148
+
149
+ If you need help, you can get in touch with me here on Github, or via:
150
+
151
+ * **Twitter**: [@mcmire](http://twitter.com/mcmire)
152
+ * **Email**: <elliot.winkler@gmail.com>
153
+
154
+ ## Copyright/License
155
+
156
+ (c) 2008-2011 Elliot Winkler. All code in this project is free to use, modify,
157
+ and redistribute for any purpose, commercial or personal. An attribution, while
158
+ not required, would be appreciated.
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ desc "Run all of the tests"
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :spec
data/TODO ADDED
@@ -0,0 +1 @@
1
+ * MelissaData will let us know if there are other residents in the building that the address points to and thus whether to supply the suite number -- tap into this
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/address_standardization/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Elliot Winkler']
6
+ gem.email = ['elliot.winkler@gmail.com']
7
+ gem.description = "A tiny Ruby library to quickly standardize a postal address"
8
+ gem.summary = "A tiny Ruby library to quickly standardize a postal address"
9
+ gem.homepage = 'http://github.com/mcmire/address_standardization'
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "address-standardization"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = AddressStandardization::VERSION
17
+
18
+ # 2.0.1 contains a bug, hardcode to 2.0.0 for now until 2.1 comes out
19
+ gem.add_runtime_dependency('mechanize', '2.0.0')
20
+ gem.add_runtime_dependency('httparty', '~> 0.8.1')
21
+ gem.add_runtime_dependency('logging', '~> 1.6.1')
22
+
23
+ gem.add_development_dependency('rspec', '~> 2.7.0')
24
+ end
@@ -0,0 +1,34 @@
1
+ # address_standardization: A tiny Ruby library to quickly standardize a postal address.
2
+ # Copyright (C) 2008-2011 Elliot Winkler. Released under the MIT license.
3
+
4
+ require 'logging'
5
+
6
+ # TODO: Force users to require MelissaData or GoogleMaps manually
7
+ # so that no dependency is required without being unused
8
+ require 'mechanize'
9
+ require 'httparty'
10
+
11
+ here = File.expand_path('..', __FILE__)
12
+
13
+ require "#{here}/address_standardization/ruby_ext"
14
+ require "#{here}/address_standardization/helpers"
15
+
16
+ require "#{here}/address_standardization/address"
17
+ require "#{here}/address_standardization/abstract_service"
18
+ require "#{here}/address_standardization/melissa_data"
19
+ require "#{here}/address_standardization/google_maps"
20
+
21
+ module AddressStandardization
22
+ class << self
23
+ attr_accessor :test_mode
24
+ alias_method :test_mode?, :test_mode
25
+
26
+ def logger
27
+ @_logger ||= Logging.logger(STDOUT)
28
+ end
29
+ end
30
+
31
+ self.test_mode = false
32
+
33
+ logger.level = ($DEBUG || ENV['DEBUG']) ? :debug : :info
34
+ end
@@ -0,0 +1,39 @@
1
+ module AddressStandardization
2
+ class AbstractService
3
+ class << self
4
+ attr_accessor :canned_response
5
+
6
+ def standardize_address(address_info)
7
+ if AddressStandardization.test_mode?
8
+ get_canned_response(address_info)
9
+ else
10
+ get_live_response(address_info)
11
+ end
12
+ end
13
+
14
+ def with_canned_response(response, &block)
15
+ old_response = self.canned_response
16
+ self.canned_response = response
17
+ ret = yield
18
+ self.canned_response = old_response
19
+ ret
20
+ end
21
+
22
+ protected
23
+ def get_canned_response(address_info)
24
+ response = (self.canned_response ||= :success)
25
+ response == :success ? Address.new(address_info) : nil
26
+ end
27
+
28
+ def get_live_response
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def logger
33
+ AddressStandardization.logger
34
+ end
35
+ end
36
+
37
+ self.canned_response = :success
38
+ end
39
+ end
@@ -0,0 +1,56 @@
1
+ module AddressStandardization
2
+ class Address
3
+ ATTRIBUTE_DEFS = [
4
+ :street,
5
+ :city,
6
+ [:region, :state, :province],
7
+ [:postal_code, :postcode, :zip],
8
+ [:district, :county],
9
+ :country
10
+ ]
11
+ ATTRIBUTE_NAMES = ATTRIBUTE_DEFS.flatten
12
+ UNIQUE_ATTRIBUTE_NAMES = ATTRIBUTE_DEFS.map { |attr|
13
+ Array === attr ? attr[0] : attr
14
+ }
15
+
16
+ ATTRIBUTE_DEFS.each do |duck|
17
+ duck = [duck] unless Array === duck
18
+ name, aliases = duck[0], duck[1..-1]
19
+
20
+ attr_reader name
21
+ define_method(:"#{name}=") do |value|
22
+ instance_variable_set("@#{name}", value.to_s.upcase)
23
+ end
24
+
25
+ aliases.each do |a|
26
+ alias_method a, name
27
+ alias_method "#{a}=", "#{name}="
28
+ end
29
+ end
30
+
31
+ def initialize(attrs={})
32
+ attrs.symbolize_keys!
33
+ _assert_valid_attrs!(attrs)
34
+ attrs.each do |attr, value|
35
+ __send__("#{attr}=", Helpers.strip_whitespace(value))
36
+ end
37
+ end
38
+
39
+ def attributes
40
+ UNIQUE_ATTRIBUTE_NAMES.inject({}) {|h,k| h[k] = __send__(k); h }
41
+ end
42
+
43
+ def ==(other)
44
+ self.class === other && attributes == other.attributes
45
+ end
46
+
47
+ def _assert_valid_attrs!(attrs)
48
+ # assume attrs's keys are already symbols
49
+ valid_keys = ATTRIBUTE_NAMES
50
+ invalid_keys = attrs.keys - valid_keys
51
+ if invalid_keys.any?
52
+ raise ArgumentError, "You gave invalid attributes: #{invalid_keys.join(', ')}. Valid attributes are: #{valid_keys.join(', ')}"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,78 @@
1
+ module AddressStandardization
2
+ # See http://code.google.com/apis/maps/documentation/geocoding/
3
+ # for documentation on Google's Geocoding API.
4
+ class GoogleMaps < AbstractService
5
+ class << self
6
+ def api_key; end
7
+ def api_key=(value)
8
+ warn "The Google Maps API no longer requires a key, so you are free to remove `AddressStandardization::GoogleMaps.api_key = ...` from your code as it is now a no-op."
9
+ end
10
+
11
+ protected
12
+ def get_live_response(address_info)
13
+ # Much of this code was borrowed from GeoKit, specifically:
14
+ # https://github.com/andre/geokit-gem/blob/master/lib/geokit/geocoders.rb#L530
15
+
16
+ address_info = address_info.stringify_keys
17
+
18
+ address_str = [
19
+ address_info["street"],
20
+ address_info["city"],
21
+ (address_info["state"] || address_info["province"]),
22
+ address_info["zip"]
23
+ ].compact.join(" ")
24
+ address_country = address_info["country"] || "US"
25
+
26
+ resp = HTTParty.get("http://maps.google.com/maps/api/geocode/json",
27
+ :query => {
28
+ :sensor => 'false',
29
+ :address => address_str,
30
+ :bias => address_country.downcase
31
+ }
32
+ )
33
+ data = resp.parsed_response
34
+ logger.debug <<EOT
35
+ [GoogleMaps] Response body:
36
+ --------------------------------------------------------------------------------
37
+ #{resp.body}
38
+ --------------------------------------------------------------------------------
39
+ EOT
40
+ logger.debug <<EOT
41
+ [GoogleMaps] Parsed response:
42
+ --------------------------------------------------------------------------------
43
+ #{data}
44
+ --------------------------------------------------------------------------------
45
+ EOT
46
+
47
+ if data['results'].any? && data['status'] == "OK"
48
+ result = data['results'].first
49
+ addr = Address.new
50
+ street = ["", ""]
51
+ result['address_components'].each do |comp|
52
+ case
53
+ when comp['types'].include?("street_number")
54
+ street[0] = comp['short_name']
55
+ when comp['types'].include?("route")
56
+ street[1] = comp['long_name']
57
+ when comp['types'].include?("locality")
58
+ addr.city = comp['long_name']
59
+ when comp['types'].include?("administrative_area_level_1")
60
+ addr.region = comp['short_name']
61
+ when comp['types'].include?("postal_code")
62
+ addr.postal_code = comp['long_name']
63
+ when comp['types'].include?("country")
64
+ # addr[:country_code] = comp['short_name']
65
+ addr.country = comp['long_name']
66
+ when comp['types'].include?("administrative_area_level_2")
67
+ addr.district = comp['long_name']
68
+ end
69
+ end
70
+ addr.street = street.join(" ").strip
71
+ return addr
72
+ else
73
+ return nil
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ module AddressStandardization
2
+ module Helpers
3
+ extend self
4
+
5
+ def strip_html(str)
6
+ str.gsub(/<\/?([^>]+)>/, '')
7
+ end
8
+ def strip_newlines(str)
9
+ str.gsub(/[\r\n]+/, '')
10
+ end
11
+ def strip_whitespace(str)
12
+ strip_newlines(str).squeeze(" ").strip
13
+ end
14
+
15
+ def url_escape(str)
16
+ str.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
17
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
18
+ end.tr(' ', '+')
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+
3
+ module AddressStandardization
4
+ class MelissaData < AbstractService
5
+ class << self
6
+ protected
7
+ def get_live_response(address_info)
8
+ address_info = address_info.stringify_keys
9
+
10
+ is_canada = (address_info["country"].to_s.upcase == "CANADA")
11
+
12
+ if is_canada
13
+ raise "The MelissaData adapter doesn't work for Canadian addresses currently. This is a known issue and will be fixed in a future release."
14
+ end
15
+
16
+ url = "http://www.melissadata.com/lookups/#{action(is_canada)}"
17
+ params = []
18
+ attrs_to_fields(is_canada).each do |attr, field|
19
+ key, val = field, address_info[attr]
20
+ params << "#{key}=#{Helpers.url_escape(val)}" if val
21
+ end
22
+ url << "?" + params.join("&")
23
+ url << "&FindAddress=Submit"
24
+
25
+ addr = Address.new(
26
+ :country => (is_canada ? "Canada" : "United States")
27
+ )
28
+ ua = Mechanize.new
29
+ logger.debug "[MelissaData] Hitting URL: #{url}"
30
+ results_page = ua.get(url)
31
+ logger.debug "[MelissaData] Response body:"
32
+ logger.debug "--------------------------------------------------"
33
+ logger.debug results_page.body
34
+ logger.debug "--------------------------------------------------"
35
+
36
+ table = results_page.at("table.Tableresultborder")
37
+ unless table
38
+ logger.debug "Couldn't find table"
39
+ return nil
40
+ end
41
+ status_row = table.at("div.Titresultableok")
42
+ unless status_row
43
+ logger.debug "Couldn't find status_row"
44
+ return nil
45
+ end
46
+ unless status_row.inner_text =~ /Address Verified/
47
+ logger.debug "Address not verified"
48
+ return nil
49
+ end
50
+ main_td = table.search("tr:eq(#{is_canada ? 2 : 3})/td:eq(2)")
51
+ main_td_s = main_td.inner_html
52
+ main_td_s.encode!("utf-8") if main_td_s.respond_to?(:encode!)
53
+ street_part, city_state_zip_part = main_td_s.split("<br>")[0..1]
54
+ street = Helpers.strip_whitespace(Helpers.strip_html(street_part))
55
+ if main_td_s.respond_to?(:encode!)
56
+ # ruby 1.9
57
+ separator = city_state_zip_part.include?("&#160;&#160;") ? "&#160;&#160;" : "  "
58
+ else
59
+ # ruby 1.8
60
+ separator = "\240\240"
61
+ end
62
+ county_row = table.search('tr.tdresul01')[4]
63
+ county = county_row.inner_text.match(/County ([A-Za-z ]+)/)[1].strip
64
+ city, state, zip = Helpers.strip_html(city_state_zip_part).split(separator)
65
+ addr.street = street
66
+ addr.city = city
67
+ addr.province = state
68
+ addr.postal_code = zip
69
+ addr.district = county
70
+
71
+ return addr
72
+ end
73
+
74
+ def action(is_canada)
75
+ is_canada ? "CanadianAddressVerify.asp" : "AddressVerify.asp"
76
+ end
77
+
78
+ def attrs_to_fields(is_canada)
79
+ if is_canada
80
+ {'street' => 'Street', 'city' => 'city', 'province' => 'Province', 'postalcode' => 'Postcode'}
81
+ else
82
+ {'street' => 'Address', 'city' => 'city', 'state' => 'state', 'zip' => 'zip'}
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,29 @@
1
+ class Hash
2
+ # Return a new hash with all keys converted to strings.
3
+ def stringify_keys
4
+ dup.stringify_keys!
5
+ end
6
+
7
+ # Destructively convert all keys to strings.
8
+ def stringify_keys!
9
+ keys.each do |key|
10
+ self[key.to_s] = delete(key)
11
+ end
12
+ self
13
+ end
14
+
15
+ # Return a new hash with all keys converted to symbols, as long as
16
+ # they respond to +to_sym+.
17
+ def symbolize_keys
18
+ dup.symbolize_keys!
19
+ end
20
+
21
+ # Destructively convert all keys to symbols, as long as they respond
22
+ # to +to_sym+.
23
+ def symbolize_keys!
24
+ keys.each do |key|
25
+ self[(key.to_sym rescue key) || key] = delete(key)
26
+ end
27
+ self
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module AddressStandardization
2
+ VERSION = "0.4.2.rc1"
3
+ end
@@ -0,0 +1,142 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe AddressStandardization::Address do
4
+ describe '.new' do
5
+ it "raises an ArgumentError if the given hash contains unknown attributes" do
6
+ lambda { described_class.new(:foo => 'bar') }.
7
+ should raise_error(ArgumentError)
8
+ end
9
+
10
+ it "doesn't raise an ArgumentError if given nothing" do
11
+ lambda { described_class.new }.should_not raise_error
12
+ end
13
+
14
+ it "doesn't raise an ArgumentError if given an empty hash" do
15
+ lambda { described_class.new({}) }.should_not raise_error
16
+ end
17
+
18
+ it "doesn't raise an ArgumentError if given a valid hash" do
19
+ lambda { described_class.new(:street => '111 Main St.') }.should_not raise_error
20
+ end
21
+
22
+ it "sets the given attributes" do
23
+ addr = described_class.new(
24
+ :street => '111 Main St.',
25
+ :city => 'Boulder',
26
+ :state => 'CO'
27
+ )
28
+ addr.street.should == '111 Main St.'
29
+ addr.city.should == 'Boulder'
30
+ addr.state.should == 'CO'
31
+ end
32
+ end
33
+
34
+ it "has a street attribute" do
35
+ subject.street = 'foo'
36
+ subject.street.should == 'foo'
37
+ end
38
+
39
+ it "has a city attribute" do
40
+ subject.city = 'foo'
41
+ subject.city.should == 'foo'
42
+ end
43
+
44
+ it "has a region attribute (aliased to state and province)" do
45
+ subject.region = 'foo'
46
+ subject.region.should == 'foo'
47
+ subject.state.should == 'foo'
48
+ subject.province.should == 'foo'
49
+
50
+ subject.state = 'bar'
51
+ subject.region.should == 'bar'
52
+ subject.state.should == 'bar'
53
+ subject.province.should == 'bar'
54
+
55
+ subject.province = 'baz'
56
+ subject.region.should == 'baz'
57
+ subject.state.should == 'baz'
58
+ subject.province.should == 'baz'
59
+ end
60
+
61
+ it "has a postal_code attribute (aliased to postcode and zip)" do
62
+ subject.postal_code = 11111
63
+ subject.postal_code.should == 11111
64
+ subject.postcode.should == 11111
65
+ subject.zip.should == 11111
66
+
67
+ subject.postal_code = 22222
68
+ subject.postal_code.should == 22222
69
+ subject.postcode.should == 22222
70
+ subject.zip.should == 22222
71
+
72
+ subject.postal_code = 33333
73
+ subject.postal_code.should == 33333
74
+ subject.postcode.should == 33333
75
+ subject.zip.should == 33333
76
+ end
77
+
78
+ it "has a district attribute (aliased to county)" do
79
+ subject.district = 'foo'
80
+ subject.district.should == 'foo'
81
+ subject.county.should == 'foo'
82
+
83
+ subject.county = 'bar'
84
+ subject.district.should == 'bar'
85
+ subject.county.should == 'bar'
86
+ end
87
+
88
+ it "has a country attribute" do
89
+ subject.country = 'foo'
90
+ subject.country.should == 'foo'
91
+ end
92
+
93
+ describe '#attributes' do
94
+ it "returns all of the attributes as a hash" do
95
+ subject.street = '111 Main St.'
96
+ subject.region = 'Georgia'
97
+ subject.country = 'United States'
98
+ subject.attributes.should == {
99
+ :street => '111 Main St.',
100
+ :city => nil,
101
+ :region => 'Georgia',
102
+ :postal_code => nil,
103
+ :district => nil,
104
+ :country => 'United States'
105
+ }
106
+ end
107
+ end
108
+
109
+ describe '#==' do
110
+ it "returns false if the given object is not an described_class" do
111
+ subject.should_not == :foo
112
+ end
113
+
114
+ it "returns false if the attributes of the given described_class don't match this described_class's attributes" do
115
+ addr1 = described_class.new(
116
+ :street => '111 Pearl St.',
117
+ :city => 'Boulder',
118
+ :state => 'CO'
119
+ )
120
+ addr2 = described_class.new(
121
+ :street => '111 Pearl Pkwy.',
122
+ :city => 'Boulder',
123
+ :state => 'CO'
124
+ )
125
+ addr1.should_not == addr2
126
+ end
127
+
128
+ it "returns true otherwise" do
129
+ addr1 = described_class.new(
130
+ :street => '111 Pearl St.',
131
+ :city => 'Boulder',
132
+ :state => 'CO'
133
+ )
134
+ addr2 = described_class.new(
135
+ :street => '111 Pearl St.',
136
+ :city => 'Boulder',
137
+ :state => 'CO'
138
+ )
139
+ addr1.should == addr2
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,114 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe AddressStandardization::GoogleMaps do
4
+ context 'in production mode' do
5
+ before do
6
+ AddressStandardization.test_mode = nil
7
+ end
8
+
9
+ it "returns the correct data for a valid US address" do
10
+ addr = AddressStandardization::GoogleMaps.standardize_address(
11
+ # test that it works regardless of symbols or strings
12
+ "street" => "1600 Amphitheatre Parkway",
13
+ :city => "Mountain View",
14
+ :state => "CA"
15
+ )
16
+ addr.attributes.should == {
17
+ :street => "1600 AMPHITHEATRE PKWY",
18
+ :city => "MOUNTAIN VIEW",
19
+ :district => "SANTA CLARA",
20
+ :region => "CA",
21
+ :postal_code => "94043",
22
+ :country => "UNITED STATES"
23
+ }
24
+ end
25
+
26
+ it "returns the correct data for a valid Canadian address" do
27
+ addr = AddressStandardization::GoogleMaps.standardize_address(
28
+ # test that it works regardless of symbols or strings
29
+ :street => "55 East Cordova St. Apt 415",
30
+ :city => "Vancouver",
31
+ "province" => "BC"
32
+ )
33
+ addr.attributes.should == {
34
+ :street => "55 E CORDOVA ST",
35
+ :city => "VANCOUVER",
36
+ :district => "GREATER VANCOUVER REGIONAL DISTRICT",
37
+ :region => "BC",
38
+ :postal_code => "V6A 1K3",
39
+ :country => "CANADA"
40
+ }
41
+ end
42
+
43
+ it "returns nothing for an invalid address" do
44
+ addr = AddressStandardization::GoogleMaps.standardize_address(
45
+ :street => "123 Imaginary Lane",
46
+ :city => "Some Town",
47
+ :state => "AK"
48
+ )
49
+ addr.should be_nil
50
+ end
51
+ end
52
+
53
+ context 'in test mode' do
54
+ before do
55
+ AddressStandardization.test_mode = true
56
+ end
57
+
58
+ it "returns the correct data for a successful response" do
59
+ AddressStandardization::GoogleMaps.canned_response = :success
60
+ addr = AddressStandardization::GoogleMaps.standardize_address(
61
+ :street => "123 Imaginary Lane",
62
+ :city => "Some Town",
63
+ :state => "AK"
64
+ )
65
+ addr.attributes.should == {
66
+ :street => "123 IMAGINARY LANE",
67
+ :city => "SOME TOWN",
68
+ :district => nil,
69
+ :region => "AK",
70
+ :postal_code => nil,
71
+ :country => nil
72
+ }
73
+ AddressStandardization::GoogleMaps.canned_response = nil
74
+
75
+ # block form
76
+ AddressStandardization::GoogleMaps.with_canned_response(:success) do
77
+ addr = AddressStandardization::GoogleMaps.standardize_address(
78
+ :street => "123 Imaginary Lane",
79
+ :city => "Some Town",
80
+ :state => "AK"
81
+ )
82
+ addr.attributes.should == {
83
+ :street => "123 IMAGINARY LANE",
84
+ :city => "SOME TOWN",
85
+ :district => nil,
86
+ :region => "AK",
87
+ :postal_code => nil,
88
+ :country => nil
89
+ }
90
+ end
91
+ end
92
+
93
+ it "returns nothing for an unsuccessful response" do
94
+ AddressStandardization::GoogleMaps.canned_response = :failure
95
+ addr = AddressStandardization::GoogleMaps.standardize_address(
96
+ :street => "123 Imaginary Lane",
97
+ :city => "Some Town",
98
+ :state => "AK"
99
+ )
100
+ addr.should be_nil
101
+ AddressStandardization::GoogleMaps.canned_response = nil
102
+
103
+ # block form
104
+ AddressStandardization::GoogleMaps.with_canned_response(:failure) do
105
+ addr = AddressStandardization::GoogleMaps.standardize_address(
106
+ :street => "123 Imaginary Lane",
107
+ :city => "Some Town",
108
+ :state => "AK"
109
+ )
110
+ addr.should be_nil
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,114 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe AddressStandardization::MelissaData do
4
+ context 'in production mode' do
5
+ before do
6
+ AddressStandardization.test_mode = nil
7
+ end
8
+
9
+ it "returns the correct data for a valid US address" do
10
+ addr = AddressStandardization::MelissaData.standardize_address(
11
+ # test that it works regardless of symbols or strings
12
+ "street" => "1600 Amphitheatre Parkway",
13
+ :city => "Mountain View",
14
+ :state => "CA"
15
+ )
16
+ addr.attributes.should == {
17
+ :street => "1600 AMPHITHEATRE PKWY",
18
+ :city => "MOUNTAIN VIEW",
19
+ :district => "SANTA CLARA",
20
+ :region => "CA",
21
+ :postal_code => "94043-1351",
22
+ :country => "UNITED STATES"
23
+ }
24
+ end
25
+
26
+ it "returns the correct data for a valid Canadian address" do
27
+ addr = AddressStandardization::MelissaData.standardize_address(
28
+ # test that it works regardless of symbols or strings
29
+ :street => "55 East Cordova St. Apt 415",
30
+ :city => "Vancouver",
31
+ "province" => "BC"
32
+ )
33
+ addr.attributes.should == {
34
+ :street => "55 E CORDOVA ST",
35
+ :city => "VANCOUVER",
36
+ :district => "GREATER VANCOUVER REGIONAL DISTRICT",
37
+ :region => "BC",
38
+ :postal_code => "V6A 1K3",
39
+ :country => "CANADA"
40
+ }
41
+ end
42
+
43
+ it "returns nothing for an invalid address" do
44
+ addr = AddressStandardization::MelissaData.standardize_address(
45
+ :street => "123 Imaginary Lane",
46
+ :city => "Some Town",
47
+ :state => "AK"
48
+ )
49
+ addr.should be_nil
50
+ end
51
+ end
52
+
53
+ context 'in test mode' do
54
+ before do
55
+ AddressStandardization.test_mode = true
56
+ end
57
+
58
+ it "returns the correct data for a successful response" do
59
+ AddressStandardization::MelissaData.canned_response = :success
60
+ addr = AddressStandardization::MelissaData.standardize_address(
61
+ :street => "123 Imaginary Lane",
62
+ :city => "Some Town",
63
+ :state => "AK"
64
+ )
65
+ addr.attributes.should == {
66
+ :street => "123 IMAGINARY LANE",
67
+ :city => "SOME TOWN",
68
+ :district => nil,
69
+ :region => "AK",
70
+ :postal_code => nil,
71
+ :country => nil
72
+ }
73
+ AddressStandardization::MelissaData.canned_response = nil
74
+
75
+ # block form
76
+ AddressStandardization::MelissaData.with_canned_response(:success) do
77
+ addr = AddressStandardization::MelissaData.standardize_address(
78
+ :street => "123 Imaginary Lane",
79
+ :city => "Some Town",
80
+ :state => "AK"
81
+ )
82
+ addr.attributes.should == {
83
+ :street => "123 IMAGINARY LANE",
84
+ :city => "SOME TOWN",
85
+ :district => nil,
86
+ :region => "AK",
87
+ :postal_code => nil,
88
+ :country => nil
89
+ }
90
+ end
91
+ end
92
+
93
+ it "returns nothing for an unsuccessful response" do
94
+ AddressStandardization::MelissaData.canned_response = :failure
95
+ addr = AddressStandardization::MelissaData.standardize_address(
96
+ :street => "123 Imaginary Lane",
97
+ :city => "Some Town",
98
+ :state => "AK"
99
+ )
100
+ addr.should be_nil
101
+ AddressStandardization::MelissaData.canned_response = nil
102
+
103
+ # block form
104
+ AddressStandardization::MelissaData.with_canned_response(:failure) do
105
+ addr = AddressStandardization::MelissaData.standardize_address(
106
+ :street => "123 Imaginary Lane",
107
+ :city => "Some Town",
108
+ :state => "AK"
109
+ )
110
+ addr.should be_nil
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require 'address_standardization'
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: address-standardization
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.2.rc1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Elliot Winkler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mechanize
16
+ requirement: &70277233513360 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: 2.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70277233513360
25
+ - !ruby/object:Gem::Dependency
26
+ name: httparty
27
+ requirement: &70277233512180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70277233512180
36
+ - !ruby/object:Gem::Dependency
37
+ name: logging
38
+ requirement: &70277233510980 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.6.1
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70277233510980
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70277233510300 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.7.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70277233510300
58
+ description: A tiny Ruby library to quickly standardize a postal address
59
+ email:
60
+ - elliot.winkler@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
67
+ - .rvmrc
68
+ - Gemfile
69
+ - Gemfile.lock
70
+ - README.md
71
+ - Rakefile
72
+ - TODO
73
+ - address_standardization.gemspec
74
+ - lib/address_standardization.rb
75
+ - lib/address_standardization/abstract_service.rb
76
+ - lib/address_standardization/address.rb
77
+ - lib/address_standardization/google_maps.rb
78
+ - lib/address_standardization/helpers.rb
79
+ - lib/address_standardization/melissa_data.rb
80
+ - lib/address_standardization/ruby_ext.rb
81
+ - lib/address_standardization/version.rb
82
+ - spec/address_spec.rb
83
+ - spec/google_maps_spec.rb
84
+ - spec/melissa_data_spec.rb
85
+ - spec/spec_helper.rb
86
+ homepage: http://github.com/mcmire/address_standardization
87
+ licenses: []
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>'
102
+ - !ruby/object:Gem::Version
103
+ version: 1.3.1
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 1.8.10
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: A tiny Ruby library to quickly standardize a postal address
110
+ test_files:
111
+ - spec/address_spec.rb
112
+ - spec/google_maps_spec.rb
113
+ - spec/melissa_data_spec.rb
114
+ - spec/spec_helper.rb