geokit 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ === 1.3.1 / 2009-05-21
2
+ * Support for External geocoders file (thanks dreamcat4)
3
+ * Support multiple ip geocoders, including new setting for ip_provider_order (thanks dreamcat4)
4
+
1
5
  === 1.3.0 / 2009-04-11
2
6
  * Added capability to define multiple API keys for different domains that may be pointing to the same application (thanks Glenn Powell)
3
7
  * Added numeric accuracy accessor for Yahoo and Google geocoders (thanks Andrew Fecheyr Lippens)
@@ -96,6 +96,10 @@ If you're using this gem by itself, here are the configuration options:
96
96
  # and http://geocoder.ca/?register=1
97
97
  Geokit::Geocoders::geocoder_ca = false
98
98
 
99
+ # require "external_geocoder.rb"
100
+ # Please see the section "writing your own geocoders" for more information.
101
+ # Geokit::Geocoders::external_key = 'REPLACE_WITH_YOUR_API_KEY'
102
+
99
103
  # This is the order in which the geocoders are called in a failover scenario
100
104
  # If you only want to use a single geocoder, put a single symbol in the array.
101
105
  # Valid symbols are :google, :yahoo, :us, and :ca.
@@ -103,6 +107,10 @@ If you're using this gem by itself, here are the configuration options:
103
107
  # various geocoders. Make sure you read up on relevant Terms of Use for each
104
108
  # geocoder you are going to use.
105
109
  Geokit::Geocoders::provider_order = [:google,:us]
110
+
111
+ # The IP provider order. Valid symbols are :ip,:geo_plugin.
112
+ # As before, make sure you read up on relevant Terms of Use for each.
113
+ # Geokit::Geocoders::ip_provider_order = [:external,:geo_plugin,:ip]
106
114
 
107
115
  If you're using this gem with the [geokit-rails plugin](http://github.com/andre/geokit-rails/tree/master), the plugin
108
116
  creates a template with these settings and places it in `config/initializers/geokit_config.rb`.
@@ -123,7 +131,11 @@ creates a template with these settings and places it in `config/initializers/geo
123
131
  * Geoplugin.net -- another IP address geocoder
124
132
 
125
133
  ### The Multigeocoder
126
- * Multi Geocoder - provides failover for the physical location geocoders.
134
+ Multi Geocoder - provides failover for the physical location geocoders, and also IP address geocoders. Its configured by setting Geokit::Geocoders::provider_order, and Geokit::Geocoders::ip_provider_order. You should call the Multi-Geocoder with its :geocode method, supplying one address parameter which is either a real street address, or an ip address. For example:
135
+
136
+ Geokit::Geocoders::MultiGeocoder.geocode("900 Sycamore Drive")
137
+
138
+ Geokit::Geocoders::MultiGeocoder.geocode("12.12.12.12")
127
139
 
128
140
  ## MULTIPLE RESULTS
129
141
  Some geocoding services will return multple results if the there isn't one clear result.
@@ -165,6 +177,36 @@ geocoders.rb contains all the geocoder implemenations. All the gercoders
165
177
  inherit from a common base (class Geocoder) and implement the private method
166
178
  do_geocode.
167
179
 
180
+ ## WRITING YOUR OWN GEOCODERS
181
+
182
+ If you would like to write your own geocoders, you can do so by requiring 'geokit' or 'geokit/geocoders.rb' in a new file and subclassing the base class (which is class "Geocoder").
183
+ You must then also require such extenal file back in your main geokit configuration.
184
+
185
+ require "geokit"
186
+
187
+ module Geokit
188
+ module Geocoders
189
+
190
+ # Should be overriden as Geokit::Geocoders::external_key in your configuration file
191
+ @@external_key = 'REPLACE_WITH_YOUR_API_KEY'
192
+ __define_accessors
193
+
194
+ # Replace name 'External' (below) with the name of your custom geocoder class
195
+ # and use :external to specify this geocoder in your list of geocoders.
196
+ class ExternalGeocoder < Geocoder
197
+ private
198
+ def self.do_geocode(address)
199
+ # Main geocoding method
200
+ end
201
+
202
+ def self.parse_http_resp(body) # :nodoc:
203
+ # Helper method to parse http response. See geokit/geocoders.rb.
204
+ end
205
+ end
206
+
207
+ end
208
+ end
209
+
168
210
  ## GOOGLE GROUP
169
211
 
170
212
  Follow the Google Group for updates and discussion on Geokit: http://groups.google.com/group/geokit
data/Rakefile CHANGED
@@ -4,6 +4,14 @@ require 'rubygems'
4
4
  require 'hoe'
5
5
  require './lib/geokit.rb'
6
6
 
7
+ # undefined method `empty?' for nil:NilClass
8
+ # /Library/Ruby/Site/1.8/rubygems/specification.rb:886:in `validate'
9
+ class NilClass
10
+ def empty?
11
+ true
12
+ end
13
+ end
14
+
7
15
  project=Hoe.new('geokit', Geokit::VERSION) do |p|
8
16
  #p.rubyforge_name = 'geokit' # if different than lowercase project name
9
17
  p.developer('Andre Lewis', 'andre@earthcode.com')
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{geokit}
5
- s.version = "1.3.0"
5
+ s.version = "1.3.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Andre Lewis and Bill Eisenhauer"]
9
- s.date = %q{2009-4-11}
9
+ s.date = %q{2009-04-11}
10
10
  s.description = %q{Geokit Gem}
11
11
  s.email = ["andre@earthcode.com / bill_eisenhauer@yahoo.com"]
12
12
  s.extra_rdoc_files = ["Manifest.txt", "README.markdown"]
@@ -18,7 +18,10 @@ Gem::Specification.new do |s|
18
18
  s.rubyforge_project = %q{geokit}
19
19
  s.rubygems_version = %q{1.3.1}
20
20
  s.summary = %q{none}
21
- s.test_files = ["test/test_base_geocoder.rb", "test/test_bounds.rb", "test/test_ca_geocoder.rb", "test/test_geoloc.rb", "test/test_google_geocoder.rb", "test/test_latlng.rb", "test/test_multi_geocoder.rb", "test/test_us_geocoder.rb", "test/test_yahoo_geocoder.rb"]
21
+ s.test_files = ["test/test_base_geocoder.rb", "test/test_bounds.rb", "test/test_ca_geocoder.rb", "test/test_geoloc.rb",
22
+ "test/test_geoplugin_geocoder.rb", "test/test_google_geocoder.rb", "test/test_google_reverse_geocoder.rb",
23
+ "test/test_inflector.rb", "test/test_ipgeocoder.rb", "test/test_latlng.rb", "test/test_multi_geocoder.rb",
24
+ "test/test_multi_ip_geocoder.rb", "test/test_us_geocoder.rb", "test/test_yahoo_geocoder.rb"]
22
25
 
23
26
  if s.respond_to? :specification_version then
24
27
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -26,3 +29,4 @@ Gem::Specification.new do |s|
26
29
  end
27
30
  end
28
31
 
32
+
@@ -1,5 +1,5 @@
1
1
  module Geokit
2
- VERSION = '1.3.0'
2
+ VERSION = '1.3.1'
3
3
  # These defaults are used in Geokit::Mappable.distance_to and in acts_as_mappable
4
4
  @@default_units = :miles
5
5
  @@default_formula = :sphere
@@ -37,6 +37,10 @@ module Geokit
37
37
  '%' + $1.unpack('H2' * $1.size).join('%').upcase
38
38
  end.tr(' ', '+')
39
39
  end
40
+
41
+ def camelize(str)
42
+ str.split('_').map {|w| w.capitalize}.join
43
+ end
40
44
  end
41
45
 
42
46
  # Contains a range of geocoders:
@@ -70,38 +74,37 @@ module Geokit
70
74
  @@geocoder_ca = false
71
75
  @@geonames = false
72
76
  @@provider_order = [:google,:us]
77
+ @@ip_provider_order = [:geo_plugin,:ip]
73
78
  @@logger=Logger.new(STDOUT)
74
79
  @@logger.level=Logger::INFO
75
80
  @@domain = nil
76
81
 
77
- def self.domain
78
- @@domain
79
- end
80
-
81
- def self.domain=(obj)
82
- @@domain = obj
83
- end
84
-
85
- [:yahoo, :google, :geocoder_us, :geocoder_ca, :geonames, :provider_order, :timeout,
86
- :proxy_addr, :proxy_port, :proxy_user, :proxy_pass, :logger].each do |sym|
87
- class_eval <<-EOS, __FILE__, __LINE__
88
- def self.#{sym}
89
- value = if defined?(#{sym.to_s.upcase})
90
- #{sym.to_s.upcase}
91
- else
92
- @@#{sym}
93
- end
94
- if value.is_a?(Hash)
95
- value = (self.domain.nil? ? nil : value[self.domain]) || value.values.first
96
- end
97
- value
82
+ def self.__define_accessors
83
+ class_variables.each do |v|
84
+ sym = v.delete("@").to_sym
85
+ unless self.respond_to? sym
86
+ module_eval <<-EOS, __FILE__, __LINE__
87
+ def self.#{sym}
88
+ value = if defined?(#{sym.to_s.upcase})
89
+ #{sym.to_s.upcase}
90
+ else
91
+ @@#{sym}
92
+ end
93
+ if value.is_a?(Hash)
94
+ value = (self.domain.nil? ? nil : value[self.domain]) || value.values.first
95
+ end
96
+ value
97
+ end
98
+
99
+ def self.#{sym}=(obj)
100
+ @@#{sym} = obj
101
+ end
102
+ EOS
98
103
  end
99
-
100
- def self.#{sym}=(obj)
101
- @@#{sym} = obj
102
- end
103
- EOS
104
+ end
104
105
  end
106
+
107
+ __define_accessors
105
108
 
106
109
  # Error which is thrown in the event a geocoding error occurs.
107
110
  class GeocodeError < StandardError; end
@@ -118,9 +121,8 @@ module Geokit
118
121
  # empty one with a failed success code.
119
122
  def self.geocode(address)
120
123
  res = do_geocode(address)
121
- return res.success? ? res : GeoLoc.new
124
+ return res.nil? ? GeoLoc.new : res
122
125
  end
123
-
124
126
  # Main method which calls the do_reverse_geocode template method which subclasses
125
127
  # are responsible for implementing. Returns a populated GeoLoc or an
126
128
  # empty one with a failed success code.
@@ -478,7 +480,7 @@ module Geokit
478
480
  response = self.call_geocoder_service("http://www.geoplugin.net/xml.gp?ip=#{ip}")
479
481
  return response.is_a?(Net::HTTPSuccess) ? parse_xml(response.body) : GeoLoc.new
480
482
  rescue
481
- logger.error "Caught an error during GeloPluginGeocoder geocoding call: "+$!
483
+ logger.error "Caught an error during GeoPluginGeocoder geocoding call: "+$!
482
484
  return GeoLoc.new
483
485
  end
484
486
 
@@ -491,7 +493,7 @@ module Geokit
491
493
  geo.country_code = xml.elements['//geoplugin_countryCode'].text
492
494
  geo.lat = xml.elements['//geoplugin_latitude'].text.to_f
493
495
  geo.lng = xml.elements['//geoplugin_longitude'].text.to_f
494
- geo.success = !geo.city.empty?
496
+ geo.success = !!geo.city && !geo.city.empty?
495
497
  return geo
496
498
  end
497
499
  end
@@ -544,7 +546,8 @@ module Geokit
544
546
  # -------------------------------------------------------------------------------------------
545
547
 
546
548
  # Provides methods to geocode with a variety of geocoding service providers, plus failover
547
- # among providers in the order you configure.
549
+ # among providers in the order you configure. When 2nd parameter is set 'true', perform
550
+ # ip location lookup with 'address' as the ip address.
548
551
  #
549
552
  # Goal:
550
553
  # - homogenize the results of multiple geocoders
@@ -552,18 +555,21 @@ module Geokit
552
555
  # Limitations:
553
556
  # - currently only provides the first result. Sometimes geocoders will return multiple results.
554
557
  # - currently discards the "accuracy" component of the geocoding calls
555
- class MultiGeocoder < Geocoder
556
- private
558
+ class MultiGeocoder < Geocoder
557
559
 
560
+ private
558
561
  # This method will call one or more geocoders in the order specified in the
559
562
  # configuration until one of the geocoders work.
560
563
  #
561
564
  # The failover approach is crucial for production-grade apps, but is rarely used.
562
565
  # 98% of your geocoding calls will be successful with the first call
563
566
  def self.do_geocode(address)
564
- Geokit::Geocoders::provider_order.each do |provider|
567
+ geocode_ip = !!/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(address)
568
+ provider_order = geocode_ip ? Geokit::Geocoders::ip_provider_order : Geokit::Geocoders::provider_order
569
+
570
+ provider_order.each do |provider|
565
571
  begin
566
- klass = Geokit::Geocoders.const_get "#{provider.to_s.capitalize}Geocoder"
572
+ klass = Geokit::Geocoders.const_get "#{Geokit::Inflector::camelize(provider.to_s)}Geocoder"
567
573
  res = klass.send :geocode, address
568
574
  return res if res.success?
569
575
  rescue
@@ -403,7 +403,7 @@ module Geokit
403
403
 
404
404
  # Returns a string representation of the instance.
405
405
  def to_s
406
- "Provider: #{provider}\n Street: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nSuccess: #{success}"
406
+ "Provider: #{provider}\nStreet: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nSuccess: #{success}"
407
407
  end
408
408
  end
409
409
 
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), 'test_base_geocoder')
2
+
3
+ Geokit::Geocoders::ip_provider_order=[:geo_plugin,:ip]
4
+
5
+ class MultiIpGeocoderTest < BaseGeocoderTest #:nodoc: all
6
+
7
+ def setup
8
+ @ip_address = '10.10.10.10'
9
+ @success = Geokit::GeoLoc.new({:city=>"SAN FRANCISCO", :state=>"CA", :country_code=>"US", :lat=>37.7742, :lng=>-122.417068})
10
+ @success.success = true
11
+ @failure = Geokit::GeoLoc.new
12
+ end
13
+
14
+ def test_successful_first
15
+ Geokit::Geocoders::GeoPluginGeocoder.expects(:geocode).with(@ip_address).returns(@success)
16
+ assert_equal @success, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
17
+ end
18
+
19
+ def test_failover
20
+ Geokit::Geocoders::GeoPluginGeocoder.expects(:geocode).with(@ip_address).returns(@failure)
21
+ Geokit::Geocoders::IpGeocoder.expects(:geocode).with(@ip_address).returns(@success)
22
+ assert_equal @success, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
23
+ end
24
+
25
+ def test_failure
26
+ Geokit::Geocoders::GeoPluginGeocoder.expects(:geocode).with(@ip_address).returns(@failure)
27
+ Geokit::Geocoders::IpGeocoder.expects(:geocode).with(@ip_address).returns(@failure)
28
+ assert_equal @failure, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
29
+ end
30
+
31
+ def test_invalid_provider
32
+ temp = Geokit::Geocoders::ip_provider_order
33
+ Geokit::Geocoders.ip_provider_order = [:bogus]
34
+ assert_equal @failure, Geokit::Geocoders::MultiGeocoder.geocode(@ip_address)
35
+ Geokit::Geocoders.ip_provider_order = temp
36
+ end
37
+
38
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geokit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andre Lewis
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-11 00:00:00 -07:00
12
+ date: 2009-05-22 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,9 +20,9 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 1.9.0
23
+ version: 1.12.2
24
24
  version:
25
- description:
25
+ description: ""
26
26
  email:
27
27
  - andre@earthcode.com
28
28
  executables: []
@@ -57,6 +57,8 @@ files:
57
57
  - test/test_yahoo_geocoder.rb
58
58
  has_rdoc: true
59
59
  homepage:
60
+ licenses: []
61
+
60
62
  post_install_message:
61
63
  rdoc_options:
62
64
  - --main
@@ -78,9 +80,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
80
  requirements: []
79
81
 
80
82
  rubyforge_project: geokit
81
- rubygems_version: 1.3.1
83
+ rubygems_version: 1.3.2
82
84
  signing_key:
83
- specification_version: 2
85
+ specification_version: 3
84
86
  summary: Geokit provides geocoding and distance calculation in an easy-to-use API
85
87
  test_files:
86
88
  - test/test_base_geocoder.rb
@@ -94,5 +96,6 @@ test_files:
94
96
  - test/test_ipgeocoder.rb
95
97
  - test/test_latlng.rb
96
98
  - test/test_multi_geocoder.rb
99
+ - test/test_multi_ip_geocoder.rb
97
100
  - test/test_us_geocoder.rb
98
101
  - test/test_yahoo_geocoder.rb