reverse_geocoder 0.0.1

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
+ === 0.0.1 2009-12-06
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,26 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/reverse_geocoder.rb
6
+ lib/reverse_geocoder/exceptions.rb
7
+ lib/reverse_geocoder/geocoder.rb
8
+ lib/reverse_geocoder/geonames.rb
9
+ lib/reverse_geocoder/google.rb
10
+ lib/reverse_geocoder/ibegin.rb
11
+ lib/reverse_geocoder/multi.rb
12
+ lib/reverse_geocoder/numerex.rb
13
+ lib/reverse_geocoder/version.rb
14
+ script/console
15
+ script/destroy
16
+ script/generate
17
+ spec/geocoder_spec.rb
18
+ spec/geonames_spec.rb
19
+ spec/geoplugin_spec.rb
20
+ spec/google_spec.rb
21
+ spec/ibegin_spec.rb
22
+ spec/multi_spec.rb
23
+ spec/numerex_spec.rb
24
+ spec/spec.opts
25
+ spec/spec_helper.rb
26
+ tasks/rspec.rake
@@ -0,0 +1,70 @@
1
+ = reverse_geocoder
2
+
3
+ * http://github.com/billeisenhauer/reverse_geocoder
4
+
5
+ == DESCRIPTION:
6
+
7
+ Ruby gem containing production-quality reverse geocoding client service.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Supported reverse geocoders:
12
+
13
+ - Google
14
+ - Geonames
15
+ - Numerex
16
+ - GeoPlugin
17
+ - iBegin
18
+
19
+ Failover support provided through the Multi reverse geocoder which is built to
20
+ respect an order preference.
21
+
22
+ Exception handling to support query limits exceeded.
23
+
24
+ == INSTALL:
25
+
26
+ sudo gem install reverse_geocoder
27
+
28
+ == USAGE:
29
+
30
+ $ ReverseGeocoder::Google.geocode(32.921943,-96.951299, :sensor => true)
31
+ => {:geocoder=>"Google", :country_code=>"US", :country_name=>"USA",
32
+ :address=>"8500 Wellington Point Dr, Irving, TX 75063, USA"}
33
+
34
+ Where the return is a Hash and the :address can be used as needed.
35
+
36
+ Some exception handling is performed to enable detection of query limit
37
+ issues. In those cases, rescue ReverseGeocoder::QueryLimitExceeded.
38
+
39
+ Other exceptions are below, but see the RDOC for more info:
40
+
41
+ - ReverseGeocoder::AuthenticationError
42
+ - ReverseGeocoder::UnknownServiceError
43
+ - ReverseGeocoder::UnavailableAddress
44
+ - ReverseGeocoder::MalformedRequest
45
+
46
+
47
+ == LICENSE:
48
+
49
+ (The MIT License)
50
+
51
+ Copyright (c) 2009 Bill Eisenhauer
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining
54
+ a copy of this software and associated documentation files (the
55
+ "Software"), to deal in the Software without restriction, including
56
+ without limitation the rights to use, copy, modify, merge, publish,
57
+ distribute, sublicense, and/or sell copies of the Software, and to
58
+ permit persons to whom the Software is furnished to do so, subject to
59
+ the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be
62
+ included in all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
65
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
67
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
68
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
69
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
70
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/reverse_geocoder'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'reverse_geocoder' do
14
+ self.developer 'FIXME full name', 'FIXME email'
15
+ self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ # self.extra_deps = [['activesupport','>= 2.0.2']]
18
+
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # remove_task :default
26
+ # task :default => [:spec, :features]
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+
3
+ gem 'httparty', '>= 0.4.5'
4
+ require 'httparty'
5
+
6
+ dir = Pathname(__FILE__).dirname.expand_path
7
+ require "#{dir}/reverse_geocoder/version"
8
+ require "#{dir}/reverse_geocoder/exceptions"
9
+ require "#{dir}/reverse_geocoder/geocoder"
10
+ require "#{dir}/reverse_geocoder/google"
11
+ require "#{dir}/reverse_geocoder/geonames"
12
+ require "#{dir}/reverse_geocoder/geoplugin"
13
+ require "#{dir}/reverse_geocoder/ibegin"
14
+ require "#{dir}/reverse_geocoder/numerex"
15
+ require "#{dir}/reverse_geocoder/multi"
@@ -0,0 +1,20 @@
1
+ module ReverseGeocoder
2
+
3
+ # Exception raised when API key or service credentials are rejected
4
+ class AuthenticationError < StandardError; end
5
+
6
+ # Exception raised when query limits are exceeded for a service.
7
+ class QueryLimitExceeded < StandardError; end
8
+
9
+ # Exception raised when an unknown service error occurs.
10
+ class UnknownServiceError < StandardError; end
11
+
12
+ # Exception that is raised when an address is unknown or cannot be returned
13
+ # for other reasons.
14
+ class UnavailableAddress < StandardError; end
15
+
16
+ # Exception that is raised when a service query is not understood by the
17
+ # service.
18
+ class MalformedRequest < StandardError; end
19
+
20
+ end
@@ -0,0 +1,104 @@
1
+ module ReverseGeocoder
2
+
3
+ # This module is meant to be mixed in to a class to enable it to perform
4
+ # reverse geocoding. The including class needs to implement three hook
5
+ # methods:
6
+ #
7
+ # request(lat, lng, options)
8
+ # normalize(response)
9
+ # validate_geocoder_options(options)
10
+ #
11
+ # The request method uses HTTParty syntax to effect the service call and
12
+ # should return its reponse. Exception handling is not required for the
13
+ # HTTP response code.
14
+ #
15
+ # The normalize method takes the response parameter which behaves like a
16
+ # Hash and maps it to an expected Hash structure. As an alternative
17
+ #
18
+ # The validate_geocoder_options method is used to validate options provided
19
+ # to a geocoder. The geocoder can throw an exception or snuff out the
20
+ # options as needed.
21
+ #
22
+ module Geocoder
23
+
24
+ # Mix class methods into the base.
25
+ def self.included(base) # :nodoc:
26
+ base.extend(ClassMethods)
27
+ base.send(:include, HTTParty)
28
+ end
29
+
30
+ module ClassMethods
31
+
32
+ def geocode(lat, lng, options={})
33
+ validate_coordinates(lat, lng)
34
+ validate_options(options)
35
+ process_request(lat, lng, options)
36
+ end
37
+
38
+ private
39
+
40
+ ### HOOK METHODS ###
41
+
42
+ # Perform the request from the web service. This is the only required
43
+ # hook method.
44
+ #
45
+ def request(lat, lng, options)
46
+ end
47
+
48
+ # Normalize the hash response into a common response so responses are
49
+ # common between geocoders. By default, return the non-normalized
50
+ # response if implementers don't wish to perform normalization.
51
+ #
52
+ def normalize(response)
53
+ response
54
+ end
55
+
56
+ # Used by geocoders to validate specific options. If options are
57
+ # found to be invalid, simply raise an ArgumentError.
58
+ #
59
+ def validate_geocoder_options(options)
60
+ end
61
+
62
+ ### VALIDATION ###
63
+
64
+ def validate_coordinates(lat, lng)
65
+ if lat.to_f < -90 || lat.to_f > 90
66
+ raise ArgumentError, "Latitude #{lat} is invalid, try -90 to 90"
67
+ end
68
+ if lng.to_f < -180 || lng.to_f > 180
69
+ raise ArgumentError, "Longitude #{lng} is invalid, try -180 to 180"
70
+ end
71
+ end
72
+
73
+ def validate_options(options)
74
+ unless options.is_a?(Hash)
75
+ raise ArgumentError, "Options should be Hash, but is a #{options.class.name}"
76
+ end
77
+ validate_geocoder_options(options)
78
+ end
79
+
80
+ ### REQUEST HANDLING ###
81
+
82
+ def process_request(lat, lng, options={})
83
+ response = request(lat, lng, options)
84
+ handle_response(response)
85
+ end
86
+
87
+ def handle_response(response)
88
+ case response.code.to_i
89
+ when 200
90
+ normalize(response)
91
+ when 401..403
92
+ raise AuthenticationError
93
+ when 404
94
+ raise UnavailableAddress
95
+ else
96
+ raise UnknownServiceError
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,35 @@
1
+ module ReverseGeocoder
2
+
3
+ # Geonames Reverse Geocoder
4
+ #
5
+ class Geonames
6
+ include ReverseGeocoder::Geocoder
7
+
8
+ base_uri 'http://ws.geonames.org'
9
+
10
+ private
11
+
12
+ def self.request(lat, lng, options)
13
+ query = {:lat => "#{lat}", :lng => "#{lng}"}.merge(options)
14
+ get("/findNearestAddress", :query => query)
15
+ end
16
+
17
+ def self.normalize(response)
18
+ results = {:geocoder => 'Geonames'}
19
+ results[:address] = assemble_address_from(response)
20
+ results[:country_code] = results[:country_name] = response['geonames']['address']['countryCode']
21
+ results
22
+ end
23
+
24
+ def self.assemble_address_from(response)
25
+ address = response['geonames']['address']
26
+ str = "#{address['placename']}, "
27
+ str += "#{address['streetNumber']} #{address['street']}, "
28
+ str += "#{address['adminName2']}, #{address['adminCode1']}, "
29
+ str += "#{address['postalcode']}, "
30
+ str += "#{address['countryCode']}"
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,29 @@
1
+ module ReverseGeocoder
2
+
3
+ # GeoPlugin Reverse Geocoder
4
+ #
5
+ class GeoPlugin
6
+ include ReverseGeocoder::Geocoder
7
+
8
+ base_uri 'http://www.geoplugin.net/extras'
9
+ default_params :format => 'xml'
10
+
11
+ private
12
+
13
+ def self.request(lat, lng, options)
14
+ query = {:lat => "#{lat}", :long => "#{lng}"}.merge(options)
15
+ get("/location.gp", :query => query)
16
+ end
17
+
18
+ def self.normalize(response)
19
+ results = {:geocoder => 'GeoPlugin'}
20
+ address = response['geoPlugin']
21
+ results[:address] = "#{address['geoplugin_place']}, #{address['geoplugin_regionAbbreviated']}, #{address['geoplugin_countryCode']}"
22
+ results[:country_code] = address['geoplugin_countryCode']
23
+ results[:country_name] = address['geoplugin_countryCode']
24
+ results
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,56 @@
1
+ module ReverseGeocoder
2
+
3
+ # Google Reverse Geocoder
4
+ #
5
+ class Google
6
+ include ReverseGeocoder::Geocoder
7
+
8
+ class << self; attr_accessor :api_key end
9
+
10
+ base_uri 'http://maps.google.com/maps'
11
+ default_params :key => api_key
12
+
13
+ private
14
+
15
+ def self.request(lat, lng, options)
16
+ query = {:q => "#{lat},#{lng}"}.merge(options)
17
+ get("/geo", :query => query)
18
+ end
19
+
20
+ def self.normalize(response)
21
+ handle_status(response['Status']['code'])
22
+ results = {:geocoder => 'Google'}
23
+ results[:address] = response['Placemark'][0]['address']
24
+ results[:country_code] = response['Placemark'][0]['AddressDetails']['Country']['CountryNameCode']
25
+ results[:country_name] = response['Placemark'][0]['AddressDetails']['Country']['CountryName']
26
+ results
27
+ end
28
+
29
+ def self.handle_status(status)
30
+ case status.to_i
31
+ when 500
32
+ raise UnknownServiceError
33
+ when 601
34
+ raise MalformedRequest
35
+ when 602, 603
36
+ raise UnavailableAddress
37
+ when 610
38
+ raise AuthenticationError
39
+ when 620
40
+ raise QueryLimitExceeded
41
+ end
42
+ end
43
+
44
+ VALID_OPTIONS = [:sensor]
45
+
46
+ def self.validate_geocoder_options(options)
47
+ options.keys.each do |key|
48
+ unless VALID_OPTIONS.include?(key)
49
+ raise ArgumentError, "Options cannot include :#{key}"
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,37 @@
1
+ module ReverseGeocoder
2
+
3
+ # iBegin Reverse Geocoder
4
+ #
5
+ class IBegin
6
+ include ReverseGeocoder::Geocoder
7
+
8
+ class << self; attr_accessor :api_key end
9
+
10
+ base_uri 'http://geocoder.ibegin.com'
11
+ default_params :apikey => api_key
12
+
13
+ private
14
+
15
+ def self.request(lat, lng, options)
16
+ query = {:latitude => "#{lat}", :longitude => "#{lng}"}.merge(options)
17
+ get("/geoxml/", :query => query)
18
+ end
19
+
20
+ def self.normalize(response)
21
+ results = {:geocoder => 'iBegin'}
22
+ address = response['geocode']
23
+ results[:address] = assemble_address_from(address)
24
+ results[:country_code] = "US"
25
+ results[:country_name] = "USA"
26
+ results
27
+ end
28
+
29
+ def self.assemble_address_from(address)
30
+ str = "#{address['stnumber']} #{address['stname']}, "
31
+ str += "#{address['stcity']}, #{address['ststate']}, "
32
+ str += "#{address['stzip']}, US"
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,21 @@
1
+ module ReverseGeocoder
2
+
3
+ @geocoder_order = [Google, Numerex, Geonames, GeoPlugin]
4
+ class << self; attr_accessor :geocoder_order end
5
+
6
+ # Multi Reverse Geocoder
7
+ #
8
+ class Multi
9
+
10
+ def self.geocode(lat, lng, options={})
11
+ ReverseGeocoder::geocoder_order.each do |geocoder_class|
12
+ begin
13
+ return geocoder_class.send(:geocode, lat, lng, options)
14
+ rescue
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,34 @@
1
+ module ReverseGeocoder
2
+
3
+ # Numerex Reverse Geocoder
4
+ #
5
+ class Numerex
6
+ include ReverseGeocoder::Geocoder
7
+
8
+ base_uri 'http://geo.numerex.com/api/rest/'
9
+ default_params :function => 'getAddress'
10
+
11
+ private
12
+
13
+ def self.request(lat, lng, options)
14
+ query = {:lat => "#{lat}", :lon => "#{lng}"}.merge(options)
15
+ get("/AddressFinder.php", :query => query)
16
+ end
17
+
18
+ def self.normalize(response)
19
+ results = {:geocoder => 'Numerex'}
20
+ address = response['address']
21
+ results[:address] = assemble_address_from(address)
22
+ results[:country_code] = ''
23
+ results[:country_name] = ''
24
+ results
25
+ end
26
+
27
+ def self.assemble_address_from(address)
28
+ str = address['addr_line1'] + ', '
29
+ str += address['addr_line2']
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,3 @@
1
+ module ReverseGeocoder #:nodoc:
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/reverse_geocoder.rb'}"
9
+ puts "Loading reverse_geocoder gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ class TestGeocoder
4
+ include ReverseGeocoder::Geocoder
5
+ end
6
+
7
+ describe 'Geocoder Validations' do
8
+
9
+ it "should reject invalid latitude" do
10
+ lambda do
11
+ TestGeocoder.geocode(180,180)
12
+ end.should raise_error(ArgumentError, "Latitude 180 is invalid, try -90 to 90")
13
+ end
14
+
15
+ it "should reject invalid longitude" do
16
+ lambda do
17
+ TestGeocoder.geocode(0,200)
18
+ end.should raise_error(ArgumentError, "Longitude 200 is invalid, try -180 to 180")
19
+ end
20
+
21
+ it "should reject invalid longitude" do
22
+ lambda do
23
+ TestGeocoder.geocode(90,180, Array.new)
24
+ end.should raise_error(ArgumentError, "Options should be Hash, but is a Array")
25
+ end
26
+
27
+ end
28
+
29
+ describe 'Geocoder Processing' do
30
+
31
+ describe 'valid response' do
32
+
33
+ it 'should return hash response' do
34
+ response = stub_httparty_response({:status => '200'}, '200')
35
+ TestGeocoder.stub!(:request).and_return(response)
36
+ response = TestGeocoder.geocode(90, 180)
37
+ response.should eql({:status => '200'})
38
+ end
39
+
40
+ end
41
+
42
+ describe 'with non-200 responses' do
43
+
44
+ it 'should raise an authorization error' do
45
+ response = stub_httparty_response({}, '401')
46
+ TestGeocoder.stub!(:request).and_return(response)
47
+ lambda do
48
+ TestGeocoder.geocode(90, 180)
49
+ end.should raise_error(ReverseGeocoder::AuthenticationError)
50
+ end
51
+
52
+ it 'should raise an unknown server error' do
53
+ response = stub_httparty_response({}, '404')
54
+ TestGeocoder.stub!(:request).and_return(response)
55
+ lambda do
56
+ TestGeocoder.geocode(90, 180)
57
+ end.should raise_error(ReverseGeocoder::UnavailableAddress)
58
+ end
59
+
60
+ it 'should raise an unknown server error' do
61
+ response = stub_httparty_response({}, '500')
62
+ TestGeocoder.stub!(:request).and_return(response)
63
+ lambda do
64
+ TestGeocoder.geocode(90, 180)
65
+ end.should raise_error(ReverseGeocoder::UnknownServiceError)
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'Geonames Geocoder Processing' do
4
+
5
+ describe 'valid response' do
6
+
7
+ valid_response_hash = {
8
+ 'geonames' => {
9
+ 'address' => {
10
+ 'street' => 'Kenwood Dr',
11
+ 'streetNumber' => '598',
12
+ 'postalcode' => '94025',
13
+ 'placename' => 'Menlo Park',
14
+ 'adminName2' => 'San Mateo',
15
+ 'adminCode1' => 'CA',
16
+ 'countryCode' => 'US'
17
+ }
18
+ }
19
+ }
20
+
21
+ normalized_hash = {
22
+ :geocoder => 'Geonames',
23
+ :address => 'Menlo Park, 598 Kenwood Dr, San Mateo, CA, 94025, US',
24
+ :country_code => 'US',
25
+ :country_name => 'US'
26
+ }
27
+
28
+ it 'should return normalized data' do
29
+ response = stub_httparty_response(valid_response_hash, '200')
30
+ ReverseGeocoder::Geonames.stub!(:request).and_return(response)
31
+ response = ReverseGeocoder::Geonames.geocode(90, 180)
32
+ response.should eql(normalized_hash)
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'GeoPlugin Geocoder Processing' do
4
+
5
+ describe 'valid response' do
6
+
7
+ valid_response_hash = {
8
+ 'geoPlugin' => {
9
+ 'geoplugin_place' => 'Port Gibson',
10
+ 'geoplugin_countryCode' => 'US',
11
+ 'geoplugin_regionAbbreviated' => 'MS'
12
+ }
13
+ }
14
+
15
+ normalized_hash = {
16
+ :geocoder => 'GeoPlugin',
17
+ :address => 'Port Gibson, MS, US',
18
+ :country_code => 'US',
19
+ :country_name => 'US'
20
+ }
21
+
22
+ it 'should return normalized data' do
23
+ response = stub_httparty_response(valid_response_hash, '200')
24
+ ReverseGeocoder::GeoPlugin.stub!(:request).and_return(response)
25
+ response = ReverseGeocoder::GeoPlugin.geocode(32, 91)
26
+ response.should eql(normalized_hash)
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,105 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'Google Geocoder Validations' do
4
+
5
+ it "should reject invalid option" do
6
+ lambda do
7
+ ReverseGeocoder::Google.geocode(90, 180, :invalid => 'invalid')
8
+ end.should raise_error(ArgumentError, "Options cannot include :invalid")
9
+ end
10
+
11
+ end
12
+
13
+ describe 'Google Geocoder Processing' do
14
+
15
+ describe 'valid response' do
16
+
17
+ valid_response_hash = {
18
+ 'name' => '1600 Amphitheatre Parkway, Mountain View, CA',
19
+ 'Status' => {
20
+ 'code' => 200,
21
+ 'request' => 'geocode'
22
+ },
23
+ 'Placemark' => [
24
+ {
25
+ 'id' => 'p1',
26
+ 'address' => '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA',
27
+ 'AddressDetails' => {
28
+ 'Country' => {
29
+ 'CountryNameCode' => 'US',
30
+ 'CountryName' => 'USA'
31
+ }
32
+ }
33
+ }
34
+ ]
35
+ }
36
+
37
+ normalized_hash = {
38
+ :geocoder => 'Google',
39
+ :address => '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA',
40
+ :country_code => 'US',
41
+ :country_name => 'USA'
42
+ }
43
+
44
+ it 'should return normalized data' do
45
+ response = stub_httparty_response(valid_response_hash, '200')
46
+ ReverseGeocoder::Google.stub!(:request).and_return(response)
47
+ response = ReverseGeocoder::Google.geocode(90, 180)
48
+ response.should eql(normalized_hash)
49
+ end
50
+
51
+ end
52
+
53
+ describe 'with non-200 responses' do
54
+
55
+ it 'should raise a malformed request error' do
56
+ response = stub_httparty_response({'Status' => {'code' => '500'}}, 200)
57
+ ReverseGeocoder::Google.stub!(:request).and_return(response)
58
+ lambda do
59
+ ReverseGeocoder::Google.geocode(90, 180)
60
+ end.should raise_error(ReverseGeocoder::UnknownServiceError)
61
+ end
62
+
63
+ it 'should raise a malformed request error' do
64
+ response = stub_httparty_response({'Status' => {'code' => '601'}}, 200)
65
+ ReverseGeocoder::Google.stub!(:request).and_return(response)
66
+ lambda do
67
+ ReverseGeocoder::Google.geocode(90, 180)
68
+ end.should raise_error(ReverseGeocoder::MalformedRequest)
69
+ end
70
+
71
+ it 'should raise a malformed request error' do
72
+ response = stub_httparty_response({'Status' => {'code' => '602'}}, 200)
73
+ ReverseGeocoder::Google.stub!(:request).and_return(response)
74
+ lambda do
75
+ ReverseGeocoder::Google.geocode(90, 180)
76
+ end.should raise_error(ReverseGeocoder::UnavailableAddress)
77
+ end
78
+
79
+ it 'should raise a malformed request error' do
80
+ response = stub_httparty_response({'Status' => {'code' => '603'}}, 200)
81
+ ReverseGeocoder::Google.stub!(:request).and_return(response)
82
+ lambda do
83
+ ReverseGeocoder::Google.geocode(90, 180)
84
+ end.should raise_error(ReverseGeocoder::UnavailableAddress)
85
+ end
86
+
87
+ it 'should raise a malformed request error' do
88
+ response = stub_httparty_response({'Status' => {'code' => '610'}}, 200)
89
+ ReverseGeocoder::Google.stub!(:request).and_return(response)
90
+ lambda do
91
+ ReverseGeocoder::Google.geocode(90, 180)
92
+ end.should raise_error(ReverseGeocoder::AuthenticationError)
93
+ end
94
+
95
+ it 'should raise a malformed request error' do
96
+ response = stub_httparty_response({'Status' => {'code' => '620'}}, 200)
97
+ ReverseGeocoder::Google.stub!(:request).and_return(response)
98
+ lambda do
99
+ ReverseGeocoder::Google.geocode(90, 180)
100
+ end.should raise_error(ReverseGeocoder::QueryLimitExceeded)
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'iBegin Geocoder Processing' do
4
+
5
+ describe 'valid response' do
6
+
7
+ valid_response_hash = {
8
+ 'geocode' => {
9
+ 'stcity' => 'Washington',
10
+ 'ststate' => 'DC',
11
+ 'stzip' => '20502',
12
+ 'stnumber' => '1610',
13
+ 'stname' => 'Pennsylvania Ave'
14
+ }
15
+ }
16
+
17
+ normalized_hash = {
18
+ :geocoder => 'iBegin',
19
+ :address => '1610 Pennsylvania Ave, Washington, DC, 20502, US',
20
+ :country_code => 'US',
21
+ :country_name => 'USA'
22
+ }
23
+
24
+ it 'should return normalized data' do
25
+ response = stub_httparty_response(valid_response_hash, '200')
26
+ ReverseGeocoder::IBegin.stub!(:request).and_return(response)
27
+ response = ReverseGeocoder::IBegin.geocode(38.898747, -77.037947)
28
+ response.should eql(normalized_hash)
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'Multi Geocoder' do
4
+
5
+ it "should contain a list of geocoder providers" do
6
+ geocoder_order = [
7
+ ReverseGeocoder::Google,
8
+ ReverseGeocoder::Numerex,
9
+ ReverseGeocoder::Geonames,
10
+ ReverseGeocoder::GeoPlugin
11
+ ]
12
+ ReverseGeocoder.geocoder_order.should eql(geocoder_order)
13
+ end
14
+
15
+ google_normalized_hash = {
16
+ :geocoder => 'Google',
17
+ :address => '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA',
18
+ :country_code => 'US',
19
+ :country_name => 'USA'
20
+ }
21
+
22
+ numerex_normalized_hash = {
23
+ :geocoder => 'Numerex',
24
+ :address => '8513 Wellington Point Dr, Irving, TX 75063',
25
+ :country_code => '',
26
+ :country_name => ''
27
+ }
28
+
29
+ it 'should call the first geocoder provider' do
30
+ ReverseGeocoder::Google.stub!(:geocode).and_return(google_normalized_hash)
31
+ response = ReverseGeocoder::Multi.geocode(90, 180)
32
+ response.should eql(google_normalized_hash)
33
+ end
34
+
35
+ it 'should fail over to the second geocoder provider' do
36
+ ReverseGeocoder::Google.stub!(:geocode).and_raise(ReverseGeocoder::QueryLimitExceeded)
37
+ ReverseGeocoder::Numerex.stub!(:geocode).and_return(numerex_normalized_hash)
38
+ response = ReverseGeocoder::Multi.geocode(90, 180)
39
+ response.should eql(numerex_normalized_hash)
40
+ end
41
+
42
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'Numerex Geocoder Processing' do
4
+
5
+ describe 'valid response' do
6
+
7
+ valid_response_hash = {
8
+ 'address' => {
9
+ 'addr_line1' => '8513 Wellington Point Dr',
10
+ 'addr_line2' => 'Irving, TX 75063'
11
+ }
12
+ }
13
+
14
+ normalized_hash = {
15
+ :geocoder => 'Numerex',
16
+ :address => '8513 Wellington Point Dr, Irving, TX 75063',
17
+ :country_code => '',
18
+ :country_name => ''
19
+ }
20
+
21
+ it 'should return normalized data' do
22
+ response = stub_httparty_response(valid_response_hash, '200')
23
+ ReverseGeocoder::Numerex.stub!(:request).and_return(response)
24
+ response = ReverseGeocoder::Numerex.geocode(32.9219569943518, -96.9514012365146)
25
+ response.should eql(normalized_hash)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,19 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'reverse_geocoder'
11
+
12
+
13
+ Spec::Runner.configure do |config|
14
+
15
+ def stub_httparty_response(response_object, code)
16
+ HTTParty::Response.new(response_object, '', code, 'OK')
17
+ end
18
+
19
+ end
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reverse_geocoder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Bill Eisenhauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-06 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.3
24
+ version:
25
+ description: ReverseGeocoder Gem
26
+ email:
27
+ - bill@billeisenhauer.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.rdoc
36
+ files:
37
+ - History.txt
38
+ - Manifest.txt
39
+ - README.rdoc
40
+ - Rakefile
41
+ - lib/reverse_geocoder.rb
42
+ - lib/reverse_geocoder/exceptions.rb
43
+ - lib/reverse_geocoder/geocoder.rb
44
+ - lib/reverse_geocoder/geonames.rb
45
+ - lib/reverse_geocoder/geoplugin.rb
46
+ - lib/reverse_geocoder/google.rb
47
+ - lib/reverse_geocoder/ibegin.rb
48
+ - lib/reverse_geocoder/multi.rb
49
+ - lib/reverse_geocoder/numerex.rb
50
+ - lib/reverse_geocoder/version.rb
51
+ - script/console
52
+ - script/destroy
53
+ - script/generate
54
+ - spec/geocoder_spec.rb
55
+ - spec/geonames_spec.rb
56
+ - spec/geoplugin_spec.rb
57
+ - spec/google_spec.rb
58
+ - spec/ibegin_spec.rb
59
+ - spec/multi_spec.rb
60
+ - spec/numerex_spec.rb
61
+ - spec/spec.opts
62
+ - spec/spec_helper.rb
63
+ - tasks/rspec.rake
64
+ has_rdoc: true
65
+ homepage: http://github.com/billeisenhauer/reverse_geocoder
66
+ licenses: []
67
+
68
+ post_install_message: PostInstall.txt
69
+ rdoc_options:
70
+ - --main
71
+ - README.rdoc
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ version:
86
+ requirements: []
87
+
88
+ rubyforge_project: reverse_geocoder
89
+ rubygems_version: 1.3.5
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Ruby gem containing production-quality reverse geocoding client service.
93
+ test_files: []
94
+