geokit 1.3.0 → 1.3.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.
@@ -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