address-standardization 0.4.2.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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