address_standardization 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -1,3 +1,3 @@
1
+ .svn
1
2
  .DS_Store
2
- *.gemspec
3
3
  *.gem
@@ -22,36 +22,41 @@ Right now this library supports two services: MelissaData and Google Maps.
22
22
 
23
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
24
 
25
- addr = AddressStandardization::MelissaData::USAddress.standardize(
25
+ addr = AddressStandardization::MelissaData.standardize_address(
26
26
  :street => "1 Infinite Loop",
27
27
  :city => "Cupertino",
28
28
  :state => "CA"
29
29
  )
30
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::MelissaData::USAddress`. If you store the instance, you can refer to the individual fields like so:
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
32
 
33
33
  addr.street #=> "1 INFINITE LOOP"
34
34
  addr.city #=> "CUPERTINO"
35
35
  addr.state #=> "CA"
36
36
  addr.zip #=> "95014-2083"
37
+ addr.country #=> "USA"
37
38
 
38
39
  And standardizing a Canadian address:
39
40
 
40
- addr = AddressStandardization::MelissaData::CanadianAddress.standardize(
41
+ addr = AddressStandardization::MelissaData.standardize_address(
41
42
  :street => "103 Metig St",
42
43
  :city => "Sault Ste Marie",
43
- :province => "ON"
44
+ :province => "ON",
45
+ :country => "Canada"
44
46
  )
45
47
  addr.street #=> "103 METIG ST RR 4"
46
48
  addr.city #=> "SAULT STE MARIE"
47
49
  addr.province #=> "ON"
48
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.
49
54
 
50
- Note that we refer to the province as `province`, but `state` works too. The postal code may also be referred to as `zip`.
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`.
51
56
 
52
57
  Using Google Maps to validate an address is just as easy:
53
58
 
54
- addr = AddressStandardization::GoogleMaps::Address.standardize(
59
+ addr = AddressStandardization::GoogleMaps.standardize_address(
55
60
  :street => "1600 Amphitheatre Parkway",
56
61
  :city => "Mountain View",
57
62
  :state => "CA"
@@ -64,7 +69,7 @@ Using Google Maps to validate an address is just as easy:
64
69
 
65
70
  And, again, a Canadian address:
66
71
 
67
- addr = AddressStandardization::GoogleMaps::Address.standardize(
72
+ addr = AddressStandardization::GoogleMaps.standardize_address(
68
73
  :street => "1770 Stenson Blvd.",
69
74
  :city => "Peterborough",
70
75
  :province => "ON"
@@ -87,4 +92,4 @@ If you find any bugs with this plugin, feel free to:
87
92
 
88
93
  ## Author/License
89
94
 
90
- (c) 2008 Elliot Winkler. Released under the MIT license.
95
+ (c) 2008-2010 Elliot Winkler. Released under the MIT license.
data/Rakefile CHANGED
@@ -1,9 +1,12 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
 
4
+ require File.dirname(__FILE__) + "/lib/address_standardization/version.rb"
5
+
4
6
  begin
5
7
  require 'jeweler'
6
8
  Jeweler::Tasks.new do |gem|
9
+ gem.version = AddressStandardization::VERSION
7
10
  gem.name = "address_standardization"
8
11
  gem.summary = %Q{A tiny Ruby library to quickly standardize a postal address}
9
12
  gem.description = %Q{A tiny Ruby library to quickly standardize a postal address}
@@ -11,8 +14,7 @@ begin
11
14
  gem.email = "elliot.winkler@gmail.com"
12
15
  gem.homepage = "http://github.com/mcmire/address_standardization"
13
16
  gem.add_dependency "mechanize"
14
- gem.add_dependency "hpricot"
15
- gem.add_development_dependency "mcmire-contest"
17
+ gem.add_development_dependency "mcmire-context"
16
18
  gem.add_development_dependency "mcmire-matchy"
17
19
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
20
  end
@@ -41,7 +43,7 @@ rescue LoadError
41
43
  end
42
44
  end
43
45
 
44
- task :test => :check_dependencies
46
+ task :test => [:"check_dependencies:development", :check_dependencies]
45
47
 
46
48
  task :default => :test
47
49
 
@@ -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{address_standardization}
8
+ s.version = "0.4.0"
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
+
@@ -1,9 +1,28 @@
1
1
  # address_standardization: A tiny Ruby library to quickly standardize a postal address.
2
- # Copyright (C) 2008 Elliot Winkler. Released under the MIT license.
2
+ # Copyright (C) 2008-2010 Elliot Winkler. Released under the MIT license.
3
3
 
4
- require File.dirname(__FILE__)+'/address_standardization/ruby_ext'
5
- require File.dirname(__FILE__)+'/address_standardization/class_level_inheritable_attributes'
4
+ require 'mechanize'
6
5
 
7
- require File.dirname(__FILE__)+'/address_standardization/abstract_address'
8
- require File.dirname(__FILE__)+'/address_standardization/melissa_data'
9
- require File.dirname(__FILE__)+'/address_standardization/google_maps'
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
@@ -1,21 +1,20 @@
1
+ # TODO: Rename to address.rb
2
+
1
3
  module AddressStandardization
2
4
  class StandardizationError < StandardError; end
3
5
 
4
- class AbstractAddress
5
-
6
- extend ClassLevelInheritableAttributes
7
- cattr_inheritable :valid_keys
8
-
9
- def self.standardize
10
- raise NotImplementedError, "You must override .standardize in a subclass"
6
+ class Address
7
+ class << self
8
+ attr_accessor :valid_keys
11
9
  end
10
+ self.valid_keys = %w(street city state province zip postalcode country)
12
11
 
13
12
  attr_reader :address_info
14
13
 
15
14
  def initialize(address_info)
16
15
  raise NotImplementedError, "You must define valid_keys" unless self.class.valid_keys
17
16
  raise ArgumentError, "No address given!" if address_info.empty?
18
- address_info = address_info.inject({}) {|h,(k,v)| h[k.to_s] = v; h } # stringify keys
17
+ address_info = address_info.stringify_keys
19
18
  validate_keys(address_info)
20
19
  standardize_values!(address_info)
21
20
  @address_info = address_info
@@ -43,7 +42,7 @@ module AddressStandardization
43
42
  end
44
43
 
45
44
  def ==(other)
46
- other.kind_of?(AbstractAddress) && @address_info == other.address_info
45
+ other.kind_of?(self.class) && @address_info == other.address_info
47
46
  end
48
47
 
49
48
  private
@@ -1,59 +1,54 @@
1
- require 'rubygems'
2
- require 'hpricot'
3
-
4
1
  module AddressStandardization
5
- module GoogleMaps
2
+ # See <http://code.google.com/apis/maps/documentation/geocoding/>
3
+ class GoogleMaps < AbstractService
6
4
  class << self
7
5
  attr_accessor :api_key
8
- end
9
6
 
10
- class Address < AbstractAddress
11
- self.valid_keys = %w(street city state province postalcode zip country full_address precision)
12
-
13
- class << self
14
- # much of this code was borrowed from GeoKit, thanks...
15
- def standardize(address_info)
16
- raise "API key not specified.\nCall AddressStandardization::GoogleMaps.api_key = '...' before you call .standardize()." unless GoogleMaps.api_key
17
-
18
- address_str = "%s, %s, %s %s" % [
19
- address_info[:street],
20
- address_info[:city],
21
- (address_info[:state] || address_info[:province]),
22
- address_info[:zip]
23
- ]
24
- url = "http://maps.google.com/maps/geo?q=#{address_str.url_escape}&output=xml&key=#{GoogleMaps.api_key}&oe=utf-8"
25
- # puts url
26
- uri = URI.parse(url)
27
- res = Net::HTTP.get_response(uri)
28
- unless res.is_a?(Net::HTTPSuccess)
29
- File.open("test.xml", "w") {|f| f.write("(no response or response was unsuccessful)") }
30
- return nil
31
- end
32
- xml = res.body
33
- #File.open("test.xml", "w") {|f| f.write(xml) }
34
- xml = Hpricot::XML(xml)
35
-
36
- if xml.at("//kml/Response/Status/code").inner_text == "200"
37
- addr = {}
38
-
39
- addr[:street] = get_inner_text(xml, '//ThoroughfareName')
40
- addr[:city] = get_inner_text(xml, '//LocalityName')
41
- addr[:province] = addr[:state] = get_inner_text(xml, '//AdministrativeAreaName')
42
- addr[:zip] = addr[:postalcode] = get_inner_text(xml, '//PostalCodeNumber')
43
- addr[:country] = get_inner_text(xml, '//CountryName')
44
-
45
- new(addr)
46
- else
47
- #File.open("test.xml", "w") {|f| f.write("(no response or response was unsuccessful)") }
48
- nil
49
- end
50
- end
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 = {}
51
36
 
52
- private
53
- def get_inner_text(xml, xpath)
54
- lambda {|x| x && x.inner_text.upcase }.call(xml.at(xpath))
55
- end
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)
56
46
  end
57
- end # Address
58
- end # GoogleMaps
59
- end # AddressStandardization
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
@@ -1,69 +1,69 @@
1
- require 'rubygems'
2
- require 'mechanize'
1
+ # encoding: utf-8
3
2
 
4
3
  module AddressStandardization
5
- class MelissaData
6
- class BaseAddress < AbstractAddress
7
- cattr_inheritable :start_url
8
-
9
- def initialize(address_info)
10
- raise NotImplementedError, "You must define start_url" unless self.class.start_url
11
- super(address_info)
12
- end
13
-
14
- class << self
15
- protected
16
- def standardize(address_info, action, attrs_to_fields)
17
- is_canada = (action =~ /Canadian/)
18
- addr = new(address_info)
19
- fields = nil
20
- WWW::Mechanize.new do |ua|
21
- form_page = ua.get(start_url)
22
- form = form_page.form_with(:action => action) do |form|
23
- attrs_to_fields.each do |attr, field|
24
- form[field] = addr.send(attr)
25
- end
26
- end
27
- results_page = form.submit(form.buttons.first)
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
+ WWW::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 "--------------------------------------------------"
28
32
 
29
- table = results_page.search("table.Tableresultborder")[1]
30
- return unless table
31
- status_row = table.at("span.Titresultableok")
32
- return unless status_row && status_row.inner_text =~ /Address Verified/
33
- main_td = table.search("tr:eq(#{is_canada ? 2 : 3})/td:eq(2)")
34
- street_part, city_state_zip_part = main_td.inner_html.split("<br>")[0..1]
35
- street = street_part.strip_html.strip_whitespace
36
- city, state, zip = city_state_zip_part.strip_html.split("\240\240")
37
- #pp :main_td => main_td.to_s,
38
- # :street_part => street_part,
39
- # :city_state_zip_part => city_state_zip_part
40
- fields = [ street.upcase, city.upcase, state.upcase, zip.upcase ]
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"
41
48
  end
42
- fields
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
43
54
  end
55
+ Address.new(attrs)
44
56
  end
45
- end
46
-
47
- class USAddress < BaseAddress
48
- self.start_url = 'http://www.melissadata.com/lookups/AddressVerify.asp'
49
- self.valid_keys = %w(street city state zip)
50
57
 
51
- def self.standardize(address_info)
52
- if fields = super(address_info, "AddressVerify.asp", :street => 'Address', :city => 'city', :state => 'state', :zip => 'zip')
53
- street, city, state, zip = fields
54
- new(:street => street, :city => city, :state => state, :zip => zip)
55
- end
58
+ def action(is_canada)
59
+ is_canada ? "CanadianAddressVerify.asp" : "AddressVerify.asp"
56
60
  end
57
- end
58
-
59
- class CanadianAddress < BaseAddress
60
- self.start_url = 'http://www.melissadata.com/lookups/CanadianAddressVerify.asp'
61
- self.valid_keys = %w(street city province postalcode)
62
61
 
63
- def self.standardize(address_info)
64
- if fields = super(address_info, "CanadianAddressVerify.asp", :street => 'Street', :city => 'city', :province => 'Province', :postalcode => 'Postcode')
65
- street, city, province, postalcode = fields
66
- new(:street => street, :city => city, :province => province, :postalcode => postalcode)
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
67
  end
68
68
  end
69
69
  end
@@ -14,4 +14,10 @@ class String
14
14
  '%' + $1.unpack('H2' * $1.size).join('%').upcase
15
15
  end.tr(' ', '+')
16
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
17
23
  end
@@ -0,0 +1,3 @@
1
+ module AddressStandardization
2
+ VERSION = "0.4.0"
3
+ end
@@ -3,46 +3,120 @@ require 'test_helper'
3
3
  AddressStandardization::GoogleMaps.api_key = "ABQIAAAALHg3jKnK9wN9K3_ArJA6TxSTZ2OgdK08l2h0_gdsozNQ-6zpaxQvIY84J7Mh1fAHQrYGI4W27qKZaw"
4
4
 
5
5
  class GoogleMapsTest < Test::Unit::TestCase
6
- test "A valid US address" do
7
- addr = AddressStandardization::GoogleMaps::Address.standardize(
8
- :street => "1600 Amphitheatre Parkway",
9
- :city => "Mountain View",
10
- :state => "CA"
11
- )
12
- addr.should == AddressStandardization::GoogleMaps::Address.new(
13
- "street" => "1600 AMPHITHEATRE PKWY",
14
- "city" => "MOUNTAIN VIEW",
15
- "state" => "CA",
16
- "province" => "CA",
17
- "postalcode" => "94043",
18
- "zip" => "94043",
19
- "country" => "USA"
20
- )
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"
21
56
  end
22
57
 
23
- test "A valid Canadian address" do
24
- addr = AddressStandardization::GoogleMaps::Address.standardize(
25
- :street => "1770 Stenson Boulevard",
26
- :city => "Peterborough",
27
- :province => "ON"
28
- )
29
- addr.should == AddressStandardization::GoogleMaps::Address.new(
30
- "street" => "1770 STENSON BLVD",
31
- "city" => "PETERBOROUGH",
32
- "state" => "ON",
33
- "province" => "ON",
34
- "postalcode" => "K9K",
35
- "zip" => "K9K",
36
- "country" => "CANADA"
37
- )
58
+ context "With test mode implicitly false" do
59
+ setup do
60
+ AddressStandardization.test_mode = nil
61
+ end
62
+ uses "production mode tests"
38
63
  end
39
64
 
40
- test "An invalid address" do
41
- addr = AddressStandardization::GoogleMaps::Address.standardize(
42
- :street => "123 Imaginary Lane",
43
- :city => "Some Town",
44
- :state => "AK"
45
- )
46
- addr.should == nil
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
47
121
  end
48
122
  end
@@ -1,49 +1,148 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class MelissaDataTest < Test::Unit::TestCase
4
- test "Valid US address" do
5
- addr = AddressStandardization::MelissaData::USAddress.standardize(
6
- :street => "1 Infinite Loop",
7
- :city => "Cupertino",
8
- :state => "CA"
9
- )
10
- addr.should == AddressStandardization::MelissaData::USAddress.new(
11
- "street" => "1 INFINITE LOOP",
12
- "city" => "CUPERTINO",
13
- "state" => "CA",
14
- "zip" => "95014-2083"
15
- )
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
16
75
  end
17
76
 
18
- test "Invalid US address" do
19
- addr = AddressStandardization::MelissaData::USAddress.standardize(
20
- :street => "123 Imaginary Lane",
21
- :city => "Some Town",
22
- :state => "AK"
23
- )
24
- addr.should == nil
77
+ context "With test mode explicitly false" do
78
+ setup do
79
+ AddressStandardization.test_mode = false
80
+ end
81
+ uses "production mode tests"
25
82
  end
26
83
 
27
- test "Valid Canadian address" do
28
- addr = AddressStandardization::MelissaData::CanadianAddress.standardize(
29
- :street => "3025 Clayhill Rd",
30
- :city => "Mississauga",
31
- :province => "ON"
32
- )
33
- addr.should == AddressStandardization::MelissaData::CanadianAddress.new(
34
- "street" => "3025 CLAYHILL RD",
35
- "province" => "ON",
36
- "city" => "MISSISSAUGA",
37
- "postalcode" => "L5B 4L2"
38
- )
84
+ context "With test mode implicitly false" do
85
+ setup do
86
+ AddressStandardization.test_mode = nil
87
+ end
88
+ uses "production mode tests"
39
89
  end
40
90
 
41
- test "Invalid Canadian address" do
42
- addr = AddressStandardization::MelissaData::CanadianAddress.standardize(
43
- :street => "123 Imaginary Lane",
44
- :city => "Some Town",
45
- :province => "BC"
46
- )
47
- addr.should == nil
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
48
147
  end
49
148
  end
data/test/test_helper.rb CHANGED
@@ -1,12 +1,48 @@
1
- # http://sneaq.net/textmate-wtf
2
- #$LOAD_PATH.reject! { |e| e.include? 'TextMate' }
3
-
4
- #dir = File.dirname(__FILE__)
5
- #lib = dir + "/../lib"
6
- #$LOAD_PATH.unshift(lib)
7
-
8
1
  require 'rubygems'
2
+ gem 'mcmire-context'
9
3
  require 'context'
4
+ gem 'mcmire-matchy'
10
5
  require 'matchy'
11
6
 
12
- require 'address_standardization'
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 CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: address_standardization
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elliot Winkler
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-01 00:00:00 -06:00
12
+ date: 2010-02-01 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -23,17 +23,7 @@ dependencies:
23
23
  version: "0"
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
- name: hpricot
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: "0"
34
- version:
35
- - !ruby/object:Gem::Dependency
36
- name: mcmire-contest
26
+ name: mcmire-context
37
27
  type: :development
38
28
  version_requirement:
39
29
  version_requirements: !ruby/object:Gem::Requirement
@@ -59,20 +49,22 @@ executables: []
59
49
  extensions: []
60
50
 
61
51
  extra_rdoc_files:
62
- - README.rdoc
52
+ - README.md
63
53
  - TODO
64
54
  files:
65
55
  - .gitignore
66
- - README.rdoc
56
+ - README.md
67
57
  - Rakefile
68
58
  - TODO
69
- - VERSION
59
+ - address_standardization.gemspec
70
60
  - lib/address_standardization.rb
71
- - lib/address_standardization/abstract_address.rb
61
+ - lib/address_standardization/abstract_service.rb
62
+ - lib/address_standardization/address.rb
72
63
  - lib/address_standardization/class_level_inheritable_attributes.rb
73
64
  - lib/address_standardization/google_maps.rb
74
65
  - lib/address_standardization/melissa_data.rb
75
66
  - lib/address_standardization/ruby_ext.rb
67
+ - lib/address_standardization/version.rb
76
68
  - test/google_maps_test.rb
77
69
  - test/melissa_data_test.rb
78
70
  - test/test_helper.rb
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.3.0