mysmallidea-address_standardization 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .svn
2
+ .DS_Store
3
+ *.gem
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # address_standardization
2
+
3
+ ## Summary
4
+
5
+ A tiny Ruby library to quickly standardize a postal address, either through MelissaData or Google Maps.
6
+
7
+ ## Installation
8
+
9
+ If you are using Rails, put this in your environment.rb:
10
+
11
+ config.gem 'address_standardization'
12
+
13
+ Then run `rake gems:install` to install the gem.
14
+
15
+ Otherwise, just run
16
+
17
+ gem install address_standardization
18
+
19
+ ## Usage
20
+
21
+ Right now this library supports two services: MelissaData and Google Maps.
22
+
23
+ MelissaData provides two services itself: [US address lookup](http://www.melissadata.com/lookups/AddressVerify.asp) and [Canadian address lookup](http://www.melissadata.com/lookups/CanadianAddressVerify.asp). They both work the same way, however. First, here's how to standardize a US address:
24
+
25
+ addr = AddressStandardization::MelissaData.standardize_address(
26
+ :street => "1 Infinite Loop",
27
+ :city => "Cupertino",
28
+ :state => "CA"
29
+ )
30
+
31
+ This submits the address to MelissaData. If the address can't be found, you'll get back `nil`. But if the address can be found (as in this case), you'll get an instance of `AddressStandardization::Address`. If you store the instance, you can refer to the individual fields like so:
32
+
33
+ addr.street #=> "1 INFINITE LOOP"
34
+ addr.city #=> "CUPERTINO"
35
+ addr.state #=> "CA"
36
+ addr.zip #=> "95014-2083"
37
+ addr.country #=> "USA"
38
+
39
+ And standardizing a Canadian address:
40
+
41
+ addr = AddressStandardization::MelissaData.standardize_address(
42
+ :street => "103 Metig St",
43
+ :city => "Sault Ste Marie",
44
+ :province => "ON",
45
+ :country => "Canada"
46
+ )
47
+ addr.street #=> "103 METIG ST RR 4"
48
+ addr.city #=> "SAULT STE MARIE"
49
+ addr.province #=> "ON"
50
+ addr.postalcode #=> "P6A 5K9"
51
+ addr.country #=> "CANADA"
52
+
53
+ Note that when standardizing a Canadian address, the `:country` must be "Canada" (or "CANADA", or anything like that). Otherwise it will be treated as a US address.
54
+
55
+ Also note that I'm referring to the address's province as `province`, but you can also use `state` if you like. Same goes for the postal code -- you can also refer to it as `zip`.
56
+
57
+ Using Google Maps to validate an address is just as easy:
58
+
59
+ addr = AddressStandardization::GoogleMaps.standardize_address(
60
+ :street => "1600 Amphitheatre Parkway",
61
+ :city => "Mountain View",
62
+ :state => "CA"
63
+ )
64
+ addr.street #=> "1600 AMPHITHEATRE PKWY"
65
+ addr.city #=> "MOUNTAIN VIEW"
66
+ addr.state #=> "CA"
67
+ addr.zip #=> "94043"
68
+ addr.country #=> "USA"
69
+
70
+ And, again, a Canadian address:
71
+
72
+ addr = AddressStandardization::GoogleMaps.standardize_address(
73
+ :street => "1770 Stenson Blvd.",
74
+ :city => "Peterborough",
75
+ :province => "ON"
76
+ )
77
+ addr.street #=> "1770 STENSON BLVD"
78
+ addr.city #=> "PETERBOROUGH"
79
+ addr.province #=> "ON"
80
+ addr.postalcode #=> "K9K"
81
+ addr.country #=> "CANADA"
82
+
83
+ Sharp eyes will notice that the Google Maps API doesn't return the full postal code for Canadian addresses. If you know why this is please let me know (my email address is below).
84
+
85
+ ## Support
86
+
87
+ If you find any bugs with this plugin, feel free to:
88
+
89
+ * file a bug report in the [Issues area on Github](http://github.com/mcmire/address_standardization/issues)
90
+ * fork the [project on Github](http://github.com/mcmire/address_standardization) and send me a pull request
91
+ * email me (*firstname* dot *lastname* at gmail dot com)
92
+
93
+ ## Author/License
94
+
95
+ (c) 2008-2010 Elliot Winkler. Released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require File.dirname(__FILE__) + "/lib/address_standardization/version.rb"
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.version = AddressStandardization::VERSION
10
+ gem.name = "address_standardization"
11
+ gem.summary = %Q{A tiny Ruby library to quickly standardize a postal address}
12
+ gem.description = %Q{A tiny Ruby library to quickly standardize a postal address}
13
+ gem.authors = ["Elliot Winkler"]
14
+ gem.email = "elliot.winkler@gmail.com"
15
+ gem.homepage = "http://github.com/mcmire/address_standardization"
16
+ gem.add_dependency "mechanize"
17
+ gem.add_development_dependency "mcmire-context"
18
+ gem.add_development_dependency "mcmire-matchy"
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
24
+ end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/*_test.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => [:"check_dependencies:development", :check_dependencies]
47
+
48
+ task :default => :test
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "address_standardization #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
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,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mysmallidea-address_standardization}
8
+ s.version = "0.4.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Elliot Winkler"]
12
+ s.date = %q{2010-02-01}
13
+ s.description = %q{A tiny Ruby library to quickly standardize a postal address}
14
+ s.email = %q{elliot.winkler@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md",
17
+ "TODO"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "README.md",
22
+ "Rakefile",
23
+ "TODO",
24
+ "address_standardization.gemspec",
25
+ "lib/address_standardization.rb",
26
+ "lib/address_standardization/abstract_service.rb",
27
+ "lib/address_standardization/address.rb",
28
+ "lib/address_standardization/class_level_inheritable_attributes.rb",
29
+ "lib/address_standardization/google_maps.rb",
30
+ "lib/address_standardization/melissa_data.rb",
31
+ "lib/address_standardization/ruby_ext.rb",
32
+ "lib/address_standardization/version.rb",
33
+ "test/google_maps_test.rb",
34
+ "test/melissa_data_test.rb",
35
+ "test/test_helper.rb"
36
+ ]
37
+ s.homepage = %q{http://github.com/mcmire/address_standardization}
38
+ s.rdoc_options = ["--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.5}
41
+ s.summary = %q{A tiny Ruby library to quickly standardize a postal address}
42
+ s.test_files = [
43
+ "test/google_maps_test.rb",
44
+ "test/melissa_data_test.rb",
45
+ "test/test_helper.rb"
46
+ ]
47
+
48
+ if s.respond_to? :specification_version then
49
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
50
+ s.specification_version = 3
51
+
52
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
53
+ s.add_runtime_dependency(%q<mechanize>, [">= 0"])
54
+ s.add_development_dependency(%q<mcmire-context>, [">= 0"])
55
+ s.add_development_dependency(%q<mcmire-matchy>, [">= 0"])
56
+ else
57
+ s.add_dependency(%q<mechanize>, [">= 0"])
58
+ s.add_dependency(%q<mcmire-context>, [">= 0"])
59
+ s.add_dependency(%q<mcmire-matchy>, [">= 0"])
60
+ end
61
+ else
62
+ s.add_dependency(%q<mechanize>, [">= 0"])
63
+ s.add_dependency(%q<mcmire-context>, [">= 0"])
64
+ s.add_dependency(%q<mcmire-matchy>, [">= 0"])
65
+ end
66
+ end
67
+
@@ -0,0 +1,28 @@
1
+ # address_standardization: A tiny Ruby library to quickly standardize a postal address.
2
+ # Copyright (C) 2008-2010 Elliot Winkler. Released under the MIT license.
3
+
4
+ require 'mechanize'
5
+
6
+ require 'address_standardization/ruby_ext'
7
+ require 'address_standardization/class_level_inheritable_attributes'
8
+
9
+ require 'address_standardization/address'
10
+ require 'address_standardization/abstract_service'
11
+ require 'address_standardization/melissa_data'
12
+ require 'address_standardization/google_maps'
13
+
14
+ module AddressStandardization
15
+ class << self
16
+ attr_accessor :test_mode
17
+ alias_method :test_mode?, :test_mode
18
+
19
+ attr_accessor :debug_mode
20
+ alias_method :debug_mode?, :debug_mode
21
+
22
+ def debug(*args)
23
+ puts(*args) if debug_mode?
24
+ end
25
+ end
26
+ self.test_mode = false
27
+ self.debug_mode = $DEBUG || ENV["DEBUG"] || false
28
+ end
@@ -0,0 +1,31 @@
1
+ module AddressStandardization
2
+ class AbstractService
3
+ extend ClassLevelInheritableAttributes
4
+ cattr_inheritable :canned_response
5
+ self.canned_response = :success
6
+
7
+ class << self
8
+ def standardize_address(address_info)
9
+ if AddressStandardization.test_mode?
10
+ get_canned_response(address_info)
11
+ else
12
+ get_live_response(address_info)
13
+ end
14
+ end
15
+
16
+ def with_canned_response(response, &block)
17
+ old_response = self.canned_response
18
+ self.canned_response = response
19
+ ret = yield
20
+ self.canned_response = old_response
21
+ ret
22
+ end
23
+
24
+ protected
25
+ def get_canned_response(address_info)
26
+ response = (self.canned_response ||= :success)
27
+ response == :success ? Address.new(address_info) : nil
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ # TODO: Rename to address.rb
2
+
3
+ module AddressStandardization
4
+ class StandardizationError < StandardError; end
5
+
6
+ class Address
7
+ class << self
8
+ attr_accessor :valid_keys
9
+ end
10
+ self.valid_keys = %w(street city state province zip postalcode country)
11
+
12
+ attr_reader :address_info
13
+
14
+ def initialize(address_info)
15
+ raise NotImplementedError, "You must define valid_keys" unless self.class.valid_keys
16
+ raise ArgumentError, "No address given!" if address_info.empty?
17
+ address_info = address_info.stringify_keys
18
+ validate_keys(address_info)
19
+ standardize_values!(address_info)
20
+ @address_info = address_info
21
+ end
22
+
23
+ def validate_keys(hash)
24
+ # assume keys are already stringified
25
+ invalid_keys = hash.keys - self.class.valid_keys
26
+ unless invalid_keys.empty?
27
+ raise ArgumentError, "Invalid keys: #{invalid_keys.join(', ')}. Valid keys are: #{self.class.valid_keys.join(', ')}"
28
+ end
29
+ end
30
+
31
+ def method_missing(name, *args)
32
+ name = name.to_s
33
+ if self.class.valid_keys.include?(name)
34
+ if args.empty?
35
+ @address_info[name]
36
+ else
37
+ @address_info[name] = standardize_value(args.first)
38
+ end
39
+ else
40
+ super(name.to_sym, *args)
41
+ end
42
+ end
43
+
44
+ def ==(other)
45
+ other.kind_of?(self.class) && @address_info == other.address_info
46
+ end
47
+
48
+ private
49
+ def standardize_values!(hash)
50
+ hash.each {|k,v| hash[k] = standardize_value(v) }
51
+ end
52
+
53
+ def standardize_value(value)
54
+ value ? value.strip_whitespace : ""
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,20 @@
1
+ # from <http://railstips.org/2008/6/13/a-class-instance-variable-update>
2
+ module ClassLevelInheritableAttributes
3
+ def cattr_inheritable(*args)
4
+ @cattr_inheritable_attrs ||= [:cattr_inheritable_attrs]
5
+ @cattr_inheritable_attrs += args
6
+ args.each do |arg|
7
+ class_eval %(
8
+ class << self; attr_accessor :#{arg} end
9
+ )
10
+ end
11
+ @cattr_inheritable_attrs
12
+ end
13
+
14
+ def inherited(subclass)
15
+ @cattr_inheritable_attrs.each do |inheritable_attribute|
16
+ instance_var = "@#{inheritable_attribute}"
17
+ subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ module AddressStandardization
2
+ # See <http://code.google.com/apis/maps/documentation/geocoding/>
3
+ class GoogleMaps < AbstractService
4
+ class << self
5
+ attr_accessor :api_key
6
+
7
+ protected
8
+ # much of this code was borrowed from GeoKit, thanks...
9
+ def get_live_response(address_info)
10
+ raise "API key not specified.\nCall AddressStandardization::GoogleMaps.api_key = '...' before you call .standardize()." unless GoogleMaps.api_key
11
+
12
+ address_info = address_info.stringify_keys
13
+
14
+ address_str = [
15
+ address_info["street"],
16
+ address_info["city"],
17
+ (address_info["state"] || address_info["province"]),
18
+ address_info["zip"]
19
+ ].join(" ")
20
+ url = "http://maps.google.com/maps/geo?q=#{address_str.url_escape}&output=xml&key=#{GoogleMaps.api_key}&oe=utf-8"
21
+ AddressStandardization.debug "[GoogleMaps] Hitting URL: #{url}"
22
+ uri = URI.parse(url)
23
+ res = Net::HTTP.get_response(uri)
24
+ return unless res.is_a?(Net::HTTPSuccess)
25
+
26
+ content = res.body
27
+ AddressStandardization.debug "[GoogleMaps] Response body:"
28
+ AddressStandardization.debug "--------------------------------------------------"
29
+ AddressStandardization.debug content
30
+ AddressStandardization.debug "--------------------------------------------------"
31
+ xml = Nokogiri::XML(content)
32
+ xml.remove_namespaces! # good or bad? I say good.
33
+ return unless xml.at("/kml/Response/Status/code").inner_text == "200"
34
+
35
+ addr = {}
36
+
37
+ addr[:street] = get_inner_text(xml, '//ThoroughfareName').to_s
38
+ addr[:city] = get_inner_text(xml, '//LocalityName').to_s
39
+ addr[:province] = addr[:state] = get_inner_text(xml, '//AdministrativeAreaName').to_s
40
+ addr[:zip] = addr[:postalcode] = get_inner_text(xml, '//PostalCodeNumber').to_s
41
+ addr[:country] = get_inner_text(xml, '//CountryName').to_s
42
+
43
+ return if addr[:street] =~ /^\s*$/ or addr[:city] =~ /^\s*$/
44
+
45
+ Address.new(addr)
46
+ end
47
+
48
+ private
49
+ def get_inner_text(xml, xpath)
50
+ lambda {|x| x && x.inner_text.upcase }.call(xml.at(xpath))
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,71 @@
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
+ addr = Address.new(address_info)
12
+
13
+ url = "http://www.melissadata.com/lookups/#{action(is_canada)}"
14
+ params = []
15
+ attrs_to_fields(is_canada).each do |attr, field|
16
+ key, val = field, address_info[attr]
17
+ params << "#{key}=#{val.url_escape}" if val
18
+ end
19
+ url << "?" + params.join("&")
20
+ url << "&FindAddress=Submit"
21
+
22
+ #puts "URL: <#{url}>"
23
+
24
+ attrs = {:country => (is_canada ? "CANADA" : "USA")}
25
+ Mechanize.new do |ua|
26
+ AddressStandardization.debug "[MelissaData] Hitting URL: #{url}"
27
+ results_page = ua.get(url)
28
+ AddressStandardization.debug "[MelissaData] Response body:"
29
+ AddressStandardization.debug "--------------------------------------------------"
30
+ AddressStandardization.debug results_page.body
31
+ AddressStandardization.debug "--------------------------------------------------"
32
+
33
+ table = results_page.search("table.Tableresultborder")[1]
34
+ return unless table
35
+ status_row = table.at("span.Titresultableok")
36
+ return unless status_row && status_row.inner_text =~ /Address Verified/
37
+ main_td = table.search("tr:eq(#{is_canada ? 2 : 3})/td:eq(2)")
38
+ main_td_s = main_td.inner_html
39
+ main_td_s.encode!("utf-8") if main_td_s.respond_to?(:encode!)
40
+ street_part, city_state_zip_part = main_td_s.split("<br>")[0..1]
41
+ street = street_part.strip_html.strip_whitespace
42
+ if main_td_s.respond_to?(:encode!)
43
+ # ruby 1.9
44
+ separator = city_state_zip_part.include?("&#160;&#160;") ? "&#160;&#160;" : "  "
45
+ else
46
+ # ruby 1.8
47
+ separator = "\240\240"
48
+ end
49
+ city, state, zip = city_state_zip_part.strip_html.split(separator)
50
+ attrs[:street] = street.upcase
51
+ attrs[:city] = city.upcase
52
+ attrs[:province] = attrs[:state] = state.upcase
53
+ attrs[:postalcode] = attrs[:zip] = zip.upcase
54
+ end
55
+ Address.new(attrs)
56
+ end
57
+
58
+ def action(is_canada)
59
+ is_canada ? "CanadianAddressVerify.asp" : "AddressVerify.asp"
60
+ end
61
+
62
+ def attrs_to_fields(is_canada)
63
+ if is_canada
64
+ {"street" => 'Street', "city" => 'city', "province" => 'Province', "postalcode" => 'Postcode'}
65
+ else
66
+ {"street" => 'Address', "city" => 'city', "state" => 'state', "zip" => 'zip'}
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ class String
2
+ def strip_html
3
+ gsub(/<\/?([^>]+)>/, '')
4
+ end
5
+ def strip_newlines
6
+ gsub(/[\r\n]+/, '')
7
+ end
8
+ def strip_whitespace
9
+ strip_newlines.squeeze(" ").strip
10
+ end
11
+
12
+ def url_escape
13
+ gsub(/([^ a-zA-Z0-9_.-]+)/n) do
14
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
15
+ end.tr(' ', '+')
16
+ end
17
+ end
18
+
19
+ class Hash
20
+ def stringify_keys
21
+ self.inject({}) {|h,(k,v)| h[k.to_s] = v; h }
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module AddressStandardization
2
+ VERSION = "0.4.1"
3
+ end
@@ -0,0 +1,122 @@
1
+ require 'test_helper'
2
+
3
+ AddressStandardization::GoogleMaps.api_key = "ABQIAAAALHg3jKnK9wN9K3_ArJA6TxSTZ2OgdK08l2h0_gdsozNQ-6zpaxQvIY84J7Mh1fAHQrYGI4W27qKZaw"
4
+
5
+ class GoogleMapsTest < Test::Unit::TestCase
6
+ shared "production mode tests" do
7
+ test "A valid US address" do
8
+ addr = AddressStandardization::GoogleMaps.standardize_address(
9
+ "street" => "1600 Amphitheatre Parkway",
10
+ :city => "Mountain View",
11
+ :state => "CA"
12
+ )
13
+ addr.should == AddressStandardization::Address.new(
14
+ "street" => "1600 AMPHITHEATRE PKWY",
15
+ "city" => "MOUNTAIN VIEW",
16
+ "state" => "CA",
17
+ "province" => "CA",
18
+ "postalcode" => "94043",
19
+ "zip" => "94043",
20
+ "country" => "USA"
21
+ )
22
+ end
23
+
24
+ test "A valid Canadian address" do
25
+ addr = AddressStandardization::GoogleMaps.standardize_address(
26
+ :street => "1770 Stenson Boulevard",
27
+ :city => "Peterborough",
28
+ "province" => "ON"
29
+ )
30
+ addr.should == AddressStandardization::Address.new(
31
+ "street" => "1770 STENSON BLVD",
32
+ "city" => "PETERBOROUGH",
33
+ "state" => "ON",
34
+ "province" => "ON",
35
+ "postalcode" => "K9K",
36
+ "zip" => "K9K",
37
+ "country" => "CANADA"
38
+ )
39
+ end
40
+
41
+ test "An invalid address" do
42
+ addr = AddressStandardization::GoogleMaps.standardize_address(
43
+ :street => "123 Imaginary Lane",
44
+ :city => "Some Town",
45
+ :state => "AK"
46
+ )
47
+ addr.should == nil
48
+ end
49
+ end
50
+
51
+ context "With test mode explicitly false" do
52
+ setup do
53
+ AddressStandardization.test_mode = false
54
+ end
55
+ uses "production mode tests"
56
+ end
57
+
58
+ context "With test mode implicitly false" do
59
+ setup do
60
+ AddressStandardization.test_mode = nil
61
+ end
62
+ uses "production mode tests"
63
+ end
64
+
65
+ context "With test mode true" do
66
+ setup do
67
+ AddressStandardization.test_mode = true
68
+ end
69
+
70
+ test "Valid address (before and after)" do
71
+ AddressStandardization::GoogleMaps.canned_response = :success
72
+ addr = AddressStandardization::GoogleMaps.standardize_address(
73
+ :street => "123 Imaginary Lane",
74
+ :city => "Some Town",
75
+ :state => "AK"
76
+ )
77
+ addr.should == AddressStandardization::Address.new(
78
+ :street => "123 Imaginary Lane",
79
+ :city => "Some Town",
80
+ :state => "AK"
81
+ )
82
+ AddressStandardization::GoogleMaps.canned_response = nil
83
+ end
84
+
85
+ test "Valid address (block)" do
86
+ AddressStandardization::GoogleMaps.with_canned_response(:success) do
87
+ addr = AddressStandardization::GoogleMaps.standardize_address(
88
+ :street => "123 Imaginary Lane",
89
+ :city => "Some Town",
90
+ :state => "AK"
91
+ )
92
+ addr.should == AddressStandardization::Address.new(
93
+ :street => "123 Imaginary Lane",
94
+ :city => "Some Town",
95
+ :state => "AK"
96
+ )
97
+ end
98
+ end
99
+
100
+ test "Invalid address (before and after)" do
101
+ AddressStandardization::GoogleMaps.canned_response = :failure
102
+ addr = AddressStandardization::GoogleMaps.standardize_address(
103
+ :street => "123 Imaginary Lane",
104
+ :city => "Some Town",
105
+ :state => "AK"
106
+ )
107
+ addr.should == nil
108
+ AddressStandardization::GoogleMaps.canned_response = nil
109
+ end
110
+
111
+ test "Invalid address (block)" do
112
+ AddressStandardization::GoogleMaps.with_canned_response(:failure) do
113
+ addr = AddressStandardization::GoogleMaps.standardize_address(
114
+ :street => "123 Imaginary Lane",
115
+ :city => "Some Town",
116
+ :state => "AK"
117
+ )
118
+ addr.should == nil
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,148 @@
1
+ require 'test_helper'
2
+
3
+ class MelissaDataTest < Test::Unit::TestCase
4
+ shared "production mode tests" do
5
+ test "Valid US address (implicit country)" do
6
+ addr = AddressStandardization::MelissaData.standardize_address(
7
+ :street => "1 Infinite Loop",
8
+ "city" => "Cupertino",
9
+ :state => "CA"
10
+ )
11
+ addr.should == AddressStandardization::Address.new(
12
+ "street" => "1 INFINITE LOOP",
13
+ "city" => "CUPERTINO",
14
+ "state" => "CA",
15
+ "province" => "CA",
16
+ "zip" => "95014-2083",
17
+ "postalcode" => "95014-2083",
18
+ "country" => "USA"
19
+ )
20
+ end
21
+
22
+ test "Valid US address (explicit country)" do
23
+ addr = AddressStandardization::MelissaData.standardize_address(
24
+ :street => "1 Infinite Loop",
25
+ :city => "Cupertino",
26
+ "state" => "CA",
27
+ "country" => "USA"
28
+ )
29
+ addr.should == AddressStandardization::Address.new(
30
+ "street" => "1 INFINITE LOOP",
31
+ "city" => "CUPERTINO",
32
+ "state" => "CA",
33
+ "province" => "CA",
34
+ "zip" => "95014-2083",
35
+ "postalcode" => "95014-2083",
36
+ "country" => "USA"
37
+ )
38
+ end
39
+
40
+ test "Invalid US address" do
41
+ addr = AddressStandardization::MelissaData.standardize_address(
42
+ :street => "123 Imaginary Lane",
43
+ :city => "Some Town",
44
+ :state => "AK"
45
+ )
46
+ addr.should == nil
47
+ end
48
+
49
+ test "Valid Canadian address" do
50
+ addr = AddressStandardization::MelissaData.standardize_address(
51
+ "street" => "3025 Clayhill Rd",
52
+ :city => "Mississauga",
53
+ "province" => "ON",
54
+ :country => "CANADA"
55
+ )
56
+ addr.should == AddressStandardization::Address.new(
57
+ "street" => "3025 CLAYHILL RD",
58
+ "state" => "ON",
59
+ "province" => "ON",
60
+ "city" => "MISSISSAUGA",
61
+ "zip" => "L5B 4L2",
62
+ "postalcode" => "L5B 4L2",
63
+ "country" => "CANADA"
64
+ )
65
+ end
66
+
67
+ test "Invalid Canadian address" do
68
+ addr = AddressStandardization::MelissaData.standardize_address(
69
+ :street => "123 Imaginary Lane",
70
+ :city => "Some Town",
71
+ :province => "BC"
72
+ )
73
+ addr.should == nil
74
+ end
75
+ end
76
+
77
+ context "With test mode explicitly false" do
78
+ setup do
79
+ AddressStandardization.test_mode = false
80
+ end
81
+ uses "production mode tests"
82
+ end
83
+
84
+ context "With test mode implicitly false" do
85
+ setup do
86
+ AddressStandardization.test_mode = nil
87
+ end
88
+ uses "production mode tests"
89
+ end
90
+
91
+ context "With test mode true" do
92
+ setup do
93
+ AddressStandardization.test_mode = true
94
+ end
95
+
96
+ test "Valid address (before and after)" do
97
+ AddressStandardization::MelissaData.canned_response = :success
98
+ addr = AddressStandardization::MelissaData.standardize_address(
99
+ :street => "123 Imaginary Lane",
100
+ :city => "Some Town",
101
+ :province => "BC"
102
+ )
103
+ addr.should == AddressStandardization::Address.new(
104
+ :street => "123 Imaginary Lane",
105
+ :city => "Some Town",
106
+ :province => "BC"
107
+ )
108
+ AddressStandardization::MelissaData.canned_response = nil
109
+ end
110
+
111
+ test "Valid address (block)" do
112
+ AddressStandardization::MelissaData.with_canned_response(:success) do
113
+ addr = AddressStandardization::MelissaData.standardize_address(
114
+ :street => "123 Imaginary Lane",
115
+ :city => "Some Town",
116
+ :province => "BC"
117
+ )
118
+ addr.should == AddressStandardization::Address.new(
119
+ :street => "123 Imaginary Lane",
120
+ :city => "Some Town",
121
+ :province => "BC"
122
+ )
123
+ end
124
+ end
125
+
126
+ test "Invalid address (before and after)" do
127
+ AddressStandardization::MelissaData.canned_response = :failure
128
+ addr = AddressStandardization::MelissaData.standardize_address(
129
+ :street => "123 Imaginary Lane",
130
+ :city => "Some Town",
131
+ :province => "BC"
132
+ )
133
+ addr.should == nil
134
+ AddressStandardization::MelissaData.canned_response = nil
135
+ end
136
+
137
+ test "Invalid address (block)" do
138
+ AddressStandardization::MelissaData.with_canned_response(:failure) do
139
+ addr = AddressStandardization::MelissaData.standardize_address(
140
+ :street => "123 Imaginary Lane",
141
+ :city => "Some Town",
142
+ :province => "BC"
143
+ )
144
+ addr.should == nil
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ gem 'mcmire-context'
3
+ require 'context'
4
+ gem 'mcmire-matchy'
5
+ require 'matchy'
6
+
7
+ require 'address_standardization'
8
+
9
+ module Context
10
+ class SharedBehavior < Module
11
+ # Fix this so that sharing a module twice within two sub-contexts
12
+ # doesn't fail with something like "'test: blah blah' is already defined"
13
+ def included(arg) # :nodoc:
14
+ arg.instance_eval(&@_behavior)
15
+ end
16
+ end
17
+ end
18
+
19
+ class << Test::Unit::TestCase
20
+ # Modify this so that the constant is set within the current context (and sub-contexts)
21
+ def shared(name, &block)
22
+ case name.class.name
23
+ when "String"
24
+ name = name.to_module_name
25
+ when "Symbol"
26
+ name = name.to_s.to_module_name
27
+ else
28
+ raise ArgumentError, "Provide a String or Symbol as the name of the shared behavior group"
29
+ end
30
+ const_set(name, Context::SharedBehavior.create_from_behavior(block))
31
+ end
32
+ %w(shared_behavior share_as share_behavior_as shared_examples_for).each {|m| alias_method m, :shared}
33
+
34
+ # Modify this so that we look in the current context (and any super-contexts) for the module
35
+ def use(shared_name)
36
+ case shared_name.class.name
37
+ when "Context::SharedBehavior", "Module"
38
+ include shared_name
39
+ when "String"
40
+ include const_get(shared_name.to_module_name)
41
+ when "Symbol"
42
+ include const_get(shared_name.to_s.to_module_name)
43
+ else
44
+ raise ArgumentError, "Provide a String or Symbol as the name of the shared behavior group or the module name"
45
+ end
46
+ end
47
+ %w(uses it_should_behave_like behaves_like uses_examples_from).each {|m| alias_method m, :use}
48
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mysmallidea-address_standardization
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 4
8
+ - 1
9
+ version: 0.4.1
10
+ platform: ruby
11
+ authors:
12
+ - Elliot Winkler
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-02-01 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: mechanize
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: mcmire-context
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :development
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: mcmire-matchy
46
+ prerelease: false
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :development
55
+ version_requirements: *id003
56
+ description: A tiny Ruby library to quickly standardize a postal address
57
+ email: elliot.winkler@gmail.com
58
+ executables: []
59
+
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - README.md
64
+ - TODO
65
+ files:
66
+ - .gitignore
67
+ - README.md
68
+ - Rakefile
69
+ - TODO
70
+ - address_standardization.gemspec
71
+ - lib/address_standardization.rb
72
+ - lib/address_standardization/abstract_service.rb
73
+ - lib/address_standardization/address.rb
74
+ - lib/address_standardization/class_level_inheritable_attributes.rb
75
+ - lib/address_standardization/google_maps.rb
76
+ - lib/address_standardization/melissa_data.rb
77
+ - lib/address_standardization/ruby_ext.rb
78
+ - lib/address_standardization/version.rb
79
+ - test/google_maps_test.rb
80
+ - test/melissa_data_test.rb
81
+ - test/test_helper.rb
82
+ has_rdoc: true
83
+ homepage: http://github.com/mcmire/address_standardization
84
+ licenses: []
85
+
86
+ post_install_message:
87
+ rdoc_options:
88
+ - --charset=UTF-8
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ requirements: []
106
+
107
+ rubyforge_project:
108
+ rubygems_version: 1.3.6
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: A tiny Ruby library to quickly standardize a postal address
112
+ test_files:
113
+ - test/google_maps_test.rb
114
+ - test/melissa_data_test.rb
115
+ - test/test_helper.rb