Skip to content
Browse files

migrated to hoe

added other distance calculations
  • Loading branch information...
1 parent 6587e86 commit de1e0403054af08fd90c527427092ce8cc65dfe9 @bkeepers bkeepers committed
View
8 CHANGELOG
@@ -0,0 +1,8 @@
+
+0.1.1
+* fixed bug in Yahoo that raised error when street address not returned
+* migrated to Hoe (http://seattlerb.rubyforge.org/hoe/)
+* added Haversine, Spherical and Vincenty distance calculations
+
+0.1 (2006-10-31)
+* Initial release
View
30 LICENSE
@@ -0,0 +1,30 @@
+Copyright 2006 Brandon Keepers, Collective Idea. All rights reserved.
+
+Original geocoding code:
+ Copyright 2006 Eric Hodel, The Robot Co-op. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the names of the authors nor the names of their contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
View
44 Manifest.txt
@@ -0,0 +1,44 @@
+CHANGELOG
+LICENSE
+Manifest.txt
+README
+Rakefile
+init.rb
+lib/graticule.rb
+lib/graticule/distance.rb
+lib/graticule/distance/haversine.rb
+lib/graticule/distance/spherical.rb
+lib/graticule/distance/vincenty.rb
+lib/graticule/geocoder.rb
+lib/graticule/geocoders/bogus.rb
+lib/graticule/geocoders/geocoder_us.rb
+lib/graticule/geocoders/google.rb
+lib/graticule/geocoders/meta_carta.rb
+lib/graticule/geocoders/rest.rb
+lib/graticule/geocoders/yahoo.rb
+lib/graticule/location.rb
+lib/graticule/version.rb
+test/fixtures/responses/geocoder_us/success.xml
+test/fixtures/responses/geocoder_us/unknown.xml
+test/fixtures/responses/google/badkey.xml
+test/fixtures/responses/google/limit.xml
+test/fixtures/responses/google/missing_address.xml
+test/fixtures/responses/google/server_error.xml
+test/fixtures/responses/google/success.xml
+test/fixtures/responses/google/unavailable.xml
+test/fixtures/responses/google/unknown_address.xml
+test/fixtures/responses/meta_carta/bad_address.xml
+test/fixtures/responses/meta_carta/multiple.xml
+test/fixtures/responses/meta_carta/success.xml
+test/fixtures/responses/yahoo/success.xml
+test/fixtures/responses/yahoo/unknown_address.xml
+test/mocks/uri.rb
+test/test_helper.rb
+test/unit/graticule/distance_test.rb
+test/unit/graticule/geocoder_test.rb
+test/unit/graticule/geocoders/geocoder_us_test.rb
+test/unit/graticule/geocoders/geocoders.rb
+test/unit/graticule/geocoders/google_test.rb
+test/unit/graticule/geocoders/meta_carta_test.rb
+test/unit/graticule/geocoders/yahoo_test.rb
+test/unit/graticule/location_test.rb
View
3 README
@@ -4,6 +4,7 @@ Graticule is a geocoding API for looking up address coordinates. It supports su
= Usage
+ require 'rubygems'
require 'graticule'
- geocoder = Graticle.service(:google).new "api_key"
+ geocoder = Graticule.service(:google).new "api_key"
location = geocoder.locate "61 East 9th Street, Holland, MI"
View
117 Rakefile
@@ -1,110 +1,15 @@
require 'rubygems'
-require 'rake'
-require 'rake/testtask'
-require 'rake/rdoctask'
-require 'rake/gempackagetask'
-require 'rake/contrib/rubyforgepublisher'
-
-
-PKG_VERSION = "0.1.0"
-PKG_NAME = "graticule"
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-
-PKG_FILES = FileList[
- "lib/**/*", "examples/**/*", "[A-Z]*", "rakefile"
-].exclude(/\.svn$/)
-
-
-desc "Default Task"
-task :default => [ :test ]
-
-# Run the unit tests
-
-Rake::TestTask.new(:test) do |t|
- t.pattern = 'test/unit/**/*_test.rb'
- t.ruby_opts << '-rubygems'
- t.verbose = true
-end
-
-Rake::TestTask.new(:test_remote) do |t|
- t.pattern = 'test/remote_tests/*_test.rb'
- t.ruby_opts << '-rubygems'
- t.verbose = true
-end
-
-# Genereate the RDoc documentation
-Rake::RDocTask.new do |rdoc|
- rdoc.rdoc_dir = 'doc'
- rdoc.title = "Graticule Geocoding Library"
- rdoc.options << '--line-numbers' << '--inline-source'
- rdoc.rdoc_files.include('README', 'CHANGELOG')
- rdoc.rdoc_files.include('lib/**/*.rb')
-end
-
-task :install => [:package] do
- `gem install pkg/#{PKG_FILE_NAME}.gem`
-end
-
-task :lines do
- lines = 0
- codelines = 0
- Dir.foreach("lib") { |file_name|
- next unless file_name =~ /.*rb/
-
- f = File.open("lib/" + file_name)
-
- while line = f.gets
- lines += 1
- next if line =~ /^\s*$/
- next if line =~ /^\s*#/
- codelines += 1
- end
- }
- puts "Lines #{lines}, LOC #{codelines}"
-end
-
-desc "Publish the gem"
-task :publish => [:rdoc, :package] do
- Rake::SshFilePublisher.new("host.collectiveidea.com", "/var/www/vhosts/source.collectiveidea.com/public/dist/pkg", "pkg", "#{PKG_FILE_NAME}.zip").upload
- Rake::SshFilePublisher.new("host.collectiveidea.com", "/var/www/vhosts/source.collectiveidea.com/public/dist/pkg", "pkg", "#{PKG_FILE_NAME}.tgz").upload
- Rake::SshFilePublisher.new("host.collectiveidea.com", "/var/www/vhosts/source.collectiveidea.com/public/dist/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
- `ssh host.collectiveidea.com "mkdir -p /var/www/vhosts/source.collectiveidea.com/public/dist/api/#{PKG_NAME}"`
- Rake::SshDirPublisher.new("host.collectiveidea.com", "/var/www/vhosts/source.collectiveidea.com/public/dist/api/#{PKG_NAME}", "doc").upload
-end
-
-desc "Delete tar.gz / zip / rdoc"
-task :cleanup => [ :rm_packages, :clobber_rdoc ]
-
-task :install => [:package] do
- `gem install pkg/#{PKG_FILE_NAME}.gem`
-end
-
-spec = Gem::Specification.new do |s|
- s.name = PKG_NAME
- s.version = PKG_VERSION
- s.summary = "Library for using various geocoding APIs."
-
- s.files = %w(README LICENSE CHANGELOG) + Dir['lib/**/*']
-
- s.has_rdoc = true
- s.extra_rdoc_files = %w( README )
- s.rdoc_options.concat ['--main', 'README']
-
- s.require_path = 'lib'
- s.autorequire = 'graticule'
- s.author = "Brandon Keepers"
- s.email = "brandon@opensoul.org"
- s.homepage = ""
-end
-
-Rake::GemPackageTask.new(spec) do |p|
- p.gem_spec = spec
+require 'hoe'
+require File.join(File.dirname(__FILE__), 'lib', 'graticule', 'version.rb')
+
+Hoe.new("graticule", Graticule::Version) do |p|
+ p.rubyforge_name = "graticule"
+ p.author = 'Brandon Keepers'
+ p.email = 'brandon@opensoul.org'
+ p.summary = "API for using all the popular geocoding services."
+ p.description = 'Graticule is a geocoding API that provides a common interface to all the popular services, including Google, Yahoo, Geocoder.us, and MetaCarta.'
+ p.url = 'http://graticule.rubyforge.org'
p.need_tar = true
p.need_zip = true
-end
-
-desc "Continuously watch unit tests"
-task :watch do
- system("clear")
- system("stakeout \"rake\" `find . -name '*.rb'`")
+ p.test_globs = ['test/**/*_test.rb']
end
View
2 init.rb
@@ -0,0 +1,2 @@
+
+require 'graticule'
View
3 lib/graticule.rb
@@ -1,4 +1,3 @@
-
$:.unshift(File.dirname(__FILE__))
require 'graticule/location'
@@ -11,3 +10,5 @@
require 'graticule/geocoders/meta_carta'
require 'graticule/distance'
require 'graticule/distance/haversine'
+require 'graticule/distance/spherical'
+require 'graticule/distance/vincenty'
View
6 lib/graticule/distance.rb
@@ -1,7 +1,11 @@
module Graticule
module Distance
+ EARTH_RADIUS = { :kilometers => 6378.135, :miles => 3963.1676 }
+ # WGS-84 numbers
+ EARTH_MAJOR_AXIS_RADIUS = { :kilometers => 6378.137, :miles => 3963.19059 }
+ EARTH_MINOR_AXIS_RADIUS = { :kilometers => 6356.7523142, :miles => 3949.90276 }
+
class DistanceFormula
- EARTH_RADIUS = { :kilometers => 6378.135, :miles => 3963.1676 }
def initialize
raise NotImplementedError
View
67 lib/graticule/distance/haversine.rb
@@ -1,29 +1,64 @@
module Graticule
module Distance
+
+ #
+ # Thanks to Chris Veness for distance formulas.
+ # * http://www.movable-type.co.uk/scripts/LatLong.html
+ #
+ # Distance Measured usign the Haversine Formula
+ # Works better at small distances than the Spherical Law of Cosines
+ # R = earth’s radius (mean radius = 6,371km)
+ # Δlat = lat2− lat1
+ # Δlong = long2− long1
+ # a = sin²(Δlat/2) + cos(lat1).cos(lat2).sin²(Δlong/2)
+ # c = 2.atan2(√a, √(1−a))
+ # d = R.c
+ #
class Haversine < DistanceFormula
def self.distance(from, to, units = :miles)
- first_longitude = deg2rad(from.longitude)
- first_latitude = deg2rad(from.latitude)
- second_longitude = deg2rad(to.longitude)
- second_latitude = deg2rad(to.latitude)
+ from_longitude = deg2rad(from.longitude)
+ from_latitude = deg2rad(from.latitude)
+ to_longitude = deg2rad(to.longitude)
+ to_latitude = deg2rad(to.latitude)
+
+ latitude_delta = to_latitude - from_latitude
+ longitude_delta = to_longitude - from_longitude
- Math.acos(
- Math.cos(first_longitude) *
- Math.cos(second_longitude) *
- Math.cos(first_latitude) *
- Math.cos(second_latitude) +
+ a = Math.sin(latitude_delta/2)**2 +
+ Math.cos(from_latitude) *
+ Math.cos(to_latitude) *
+ Math.sin(longitude_delta/2)**2
- Math.cos(first_latitude) *
- Math.sin(first_longitude) *
- Math.cos(second_latitude) *
- Math.sin(second_longitude) +
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
- Math.sin(first_latitude) *
- Math.sin(second_latitude)
- ) * EARTH_RADIUS[units.to_sym]
+ d = EARTH_RADIUS[units.to_sym] * c
end
+
+ # # What formula is this?
+ # def self.distance(from, to, units = :miles)
+ # from_longitude = deg2rad(from.longitude)
+ # from_latitude = deg2rad(from.latitude)
+ # to_longitude = deg2rad(to.longitude)
+ # to_latitude = deg2rad(to.latitude)
+ #
+ # Math.acos(
+ # Math.cos(from_longitude) *
+ # Math.cos(to_longitude) *
+ # Math.cos(from_latitude) *
+ # Math.cos(to_latitude) +
+ #
+ # Math.cos(from_latitude) *
+ # Math.sin(from_longitude) *
+ # Math.cos(to_latitude) *
+ # Math.sin(to_longitude) +
+ #
+ # Math.sin(from_latitude) *
+ # Math.sin(to_latitude)
+ # ) * EARTH_RADIUS[units.to_sym]
+ # end
+
end
end
View
30 lib/graticule/distance/spherical.rb
@@ -0,0 +1,30 @@
+module Graticule
+ module Distance
+
+ #
+ # Distance Measured usign the Spherical Law of Cosines
+ # Simplist though least accurate (earth isn't a perfect sphere)
+ # d = acos(sin(lat1).sin(lat2)+cos(lat1).cos(lat2).cos(long2−long1)).R
+ #
+ class Spherical < DistanceFormula
+
+ def self.distance(from, to, units = :miles)
+ from_longitude = deg2rad(from.longitude)
+ from_latitude = deg2rad(from.latitude)
+ to_longitude = deg2rad(to.longitude)
+ to_latitude = deg2rad(to.latitude)
+
+ Math.acos(
+ Math.sin(from_latitude) *
+ Math.sin(to_latitude) +
+
+ Math.cos(from_latitude) *
+ Math.cos(to_latitude) *
+ Math.cos(to_longitude - from_longitude)
+ ) * EARTH_RADIUS[units.to_sym]
+ end
+
+
+ end
+ end
+end
View
99 lib/graticule/distance/vincenty.rb
@@ -0,0 +1,99 @@
+module Graticule
+ module Distance
+
+ #
+ # Thanks to Chris Veness for distance formulas.
+ # * http://www.movable-type.co.uk/scripts/LatLongVincenty.html
+ #
+ # Distance Measured usign the Vincenty Formula
+ # Very accurate, using an accurate ellipsoidal model of the earth
+ # a, b = major & minor semiaxes of the ellipsoid
+ # f = flattening (a−b)/a
+ # φ1, φ2 = geodetic latitude
+ # L = difference in longitude
+ # U1 = atan((1−f).tanφ1) (U is ‘reduced latitude’)
+ # U2 = atan((1−f).tanφ2)
+ # λ = L, λ′ = 2π
+ # while abs(λ−λ′) > 10-12 { (i.e. 0.06mm)
+ # sinσ = √[ (cosU2.sinλ)² + (cosU1.sinU2 − sinU1.cosU2.cosλ)² ] (14)
+ # cosσ = sinU1.sinU2 + cosU1.cosU2.cosλ (15)
+ # σ = atan2(sinσ, cosσ) (16)
+ # sinα = cosU1.cosU2.sinλ / sinσ (17)
+ # cos²α = 1 − sin²α (trig identity; §6)
+ # cos2σm = cosσ − 2.sinU1.sinU2/cos²α (18)
+ # C = f/16.cos²α.[4+f.(4−3.cos²α)] (10)
+ # λ′ = λ
+ # λ = L + (1−C).f.sinα.{σ+C.sinσ.[cos2σm+C.cosσ.(−1+2.cos²2σm)]} (11)
+ # }
+ # u² = cos²α.(a²−b²)/b²
+ # A = 1+u²/16384.{4096+u².[−768+u².(320−175.u²)]} (3)
+ # B = u²/1024.{256+u².[−128+u².(74−47.u²)]} (4)
+ # Δσ = B.sinσ.{cos2σm+B/4.[cosσ.(−1+2.cos²2σm) − B/6.cos2σm.(−3+4.sin²σ).(−3+4.cos²2σm)]} (6)
+ # s = b.A.(σ−Δσ) (19)
+ # α1 = atan2(cosU2.sinλ, cosU1.sinU2 − sinU1.cosU2.cosλ) (20)
+ # α2 = atan2(cosU1.sinλ, −sinU1.cosU2 + cosU1.sinU2.cosλ) (21)
+ # Where:
+ #
+ # s is the distance (in the same units as a & b)
+ # α1 is the initial bearing, or forward azimuth
+ # α2 is the final bearing (in direction p1→p2)
+ #
+ class Vincenty < DistanceFormula
+
+ def self.distance(from, to, units = :miles)
+ from_longitude = deg2rad(from.longitude)
+ from_latitude = deg2rad(from.latitude)
+ to_longitude = deg2rad(to.longitude)
+ to_latitude = deg2rad(to.latitude)
+
+ earth_major_axis_radius = EARTH_MAJOR_AXIS_RADIUS[units.to_sym]
+ earth_minor_axis_radius = EARTH_MINOR_AXIS_RADIUS[units.to_sym]
+
+ f = (earth_major_axis_radius - earth_minor_axis_radius) / earth_major_axis_radius
+
+ l = to_longitude - from_longitude
+ u1 = Math.atan((1-f) * Math.tan(from_latitude))
+ u2 = Math.atan((1-f) * Math.tan(to_latitude))
+ sinU1 = Math.sin(u1)
+ cosU1 = Math.cos(u1)
+ sinU2 = Math.sin(u2)
+ cosU2 = Math.cos(u2)
+
+ lambda = l
+ lambdaP = 2*Math::PI
+ iterLimit = 20;
+ while (lambda-lambdaP).abs > 1e-12 && --iterLimit>0
+ sinLambda = Math.sin(lambda)
+ cosLambda = Math.cos(lambda)
+ sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+ (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda))
+ return 0 if sinSigma==0 # co-incident points
+ cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda
+ sigma = Math.atan2(sinSigma, cosSigma)
+ sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma
+ cosSqAlpha = 1 - sinAlpha*sinAlpha
+ cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha
+
+ cos2SigmaM = 0 if cos2SigmaM.nan? # equatorial line: cosSqAlpha=0 (§6)
+
+ c = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha))
+ lambdaP = lambda
+ lambda = l + (1-c) * f * sinAlpha *
+ (sigma + c*sinSigma*(cos2SigmaM+c*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)))
+ end
+ return NaN if (iterLimit==0) # formula failed to converge
+
+ uSq = cosSqAlpha * (earth_major_axis_radius**2 - earth_minor_axis_radius**2) / (earth_minor_axis_radius**2);
+ bigA = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+ bigB = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+ deltaSigma = bigB*sinSigma*(cos2SigmaM+bigB/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+ bigB/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+ s = earth_minor_axis_radius*bigA*(sigma-deltaSigma);
+
+ #s = s.toFixed(3) # round to 1mm precision
+ return s
+ end
+
+ end
+ end
+end
View
10 lib/graticule/geocoders/google.rb
@@ -7,9 +7,9 @@ module Graticule
# Then you create a GoogleGeocode object and start locating addresses:
#
# require 'rubygems'
- # require 'geocode'
+ # require 'graticule'
#
- # gg = Geocode.service(:google).new(:key => MAPS_API_KEY)
+ # gg = Graticule.service(:google).new(:key => MAPS_API_KEY)
# location = gg.locate '1600 Amphitheater Pkwy, Mountain View, CA'
# p location.coordinates
#
@@ -21,11 +21,11 @@ class GoogleGeocoder < RestGeocoder
0 => :unknown, # Unknown location. (Since 2.59)
1 => :country, # Country level accuracy. (Since 2.59)
2 => :state, # Region (state, province, prefecture, etc.) level accuracy. (Since 2.59)
- #3 => :county # Sub-region (county, municipality, etc.) level accuracy. (Since 2.59)
+ 3 => :state, # Sub-region (county, municipality, etc.) level accuracy. (Since 2.59)
4 => :city, # Town (city, village) level accuracy. (Since 2.59)
5 => :zip, # Post code (zip code) level accuracy. (Since 2.59)
6 => :street, # Street level accuracy. (Since 2.59)
- 7 => :intersection, # Intersection level accuracy. (Since 2.59)
+ 7 => :street, # Intersection level accuracy. (Since 2.59)
8 => :address # Address level accuracy. (Since 2.59)
}
@@ -54,7 +54,7 @@ def parse_response(xml)
:country => text(xml.elements['/kml/Response/Placemark/AddressDetails/Country/CountryNameCode']),
:latitude => latitude,
:longitude => longitude,
- :precision => PRECISION[xml.elements['/kml/Response/Placemark/AddressDetails'].attribute('Accuracy').value.to_i]
+ :precision => PRECISION[xml.elements['/kml/Response/Placemark/AddressDetails'].attribute('Accuracy').value.to_i] || :unknown
end
# Extracts and raises an error from +xml+, if any.
View
2 lib/graticule/location.rb
@@ -21,7 +21,7 @@ def ==(object)
end
def distance_to(destination, units = :miles, formula = :haversine)
- "Distance::#{formula.to_s.titleize}".constantize.distance(self, destination)
+ "Graticule::Distance::#{formula.to_s.titleize}".constantize.distance(self, destination)
end
end
View
3 lib/graticule/version.rb
@@ -0,0 +1,3 @@
+module Graticule
+ Version = '0.1.1'
+end
View
6 test/unit/graticule/distance_test.rb
@@ -6,11 +6,11 @@ class DistanceFormulaTest < Test::Unit::TestCase
EARTH_RADIUS_IN_MILES = 3963.1676
EARTH_RADIUS_IN_KILOMETERS = 6378.135
- FORMULAS = [Haversine]
+ FORMULAS = [Haversine, Spherical, Vincenty]
def test_earth_radius
- assert_equal EARTH_RADIUS_IN_MILES, DistanceFormula::EARTH_RADIUS[:miles]
- assert_equal EARTH_RADIUS_IN_KILOMETERS, DistanceFormula::EARTH_RADIUS[:kilometers]
+ assert_equal EARTH_RADIUS_IN_MILES, EARTH_RADIUS[:miles]
+ assert_equal EARTH_RADIUS_IN_KILOMETERS, EARTH_RADIUS[:kilometers]
end
def test_distance
View
18 test/unit/graticule/geocoder_test.rb
@@ -7,9 +7,25 @@ def test_cannot_instantiate
assert_raises(NotImplementedError) { Geocoder.new }
end
- def test_service
+ def test_bogus_service
assert_equal BogusGeocoder, Graticule.service(:bogus)
end
+
+ def test_yahoo_service
+ assert_equal YahooGeocoder, Graticule.service(:yahoo)
+ end
+
+ def test_google_service
+ assert_equal GoogleGeocoder, Graticule.service(:google)
+ end
+
+ def test_geocoder_us_service
+ assert_equal GeocoderUsGeocoder, Graticule.service(:geocoder_us)
+ end
+
+ def test_meta_carta_service
+ assert_equal MetaCartaGeocoder, Graticule.service(:meta_carta)
+ end
end
end
View
5 test/unit/graticule/geocoders/geocoders.rb
@@ -1,7 +1,10 @@
require File.dirname(__FILE__) + '/../../../test_helper'
+
module Graticule
- module GeocodersTestCase # < Test::Unit::TestCase
+
+ # Generic tests for all geocoders (theoretically)
+ module GeocodersTestCase
def test_success
return unless prepare_response(:success)

0 comments on commit de1e040

Please sign in to comment.
Something went wrong with that request. Please try again.