Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

added auto_geocode to acts_as_mappable

git-svn-id: http://geokit.rubyforge.org/svn/trunk@35 9265c765-0211-4c68-b2df-6d1bd6e20c4d
  • Loading branch information...
commit 47e3d0c8a1d7de1f6f95ae5d6a648c91312e3e62 1 parent 4f53732
lewisac authored
39 lib/geo_kit/acts_as_mappable.rb
@@ -8,7 +8,7 @@ module GeoKit
8 8 # 2. a geocodeable string -- :origin=>'100 Spear st, San Francisco, CA'
9 9 # 3. an object which responds to lat and lng methods, or latitude and longitude methods,
10 10 # or whatever methods you have specified for lng_column_name and lat_column_name
11   - #
  11 + #
12 12 # Other finder methods are provided for specific queries. These are:
13 13 #
14 14 # * find_within (alias: find_inside)
@@ -34,6 +34,18 @@ module ClassMethods # :nodoc:
34 34 # GeoKit::default_formula. Also, by default, uses lat, lng, and distance for respective
35 35 # column names. All of these can be overridden using the :default_units, :default_formula,
36 36 # :lat_column_name, :lng_column_name, and :distance_column_name hash keys.
  37 + #
  38 + # Can also use to auto-geocode a specific column on create. Syntax;
  39 + #
  40 + # acts_as_mappable :auto_geocode=>true
  41 + #
  42 + # By default, it tries to geocode the "address" field. Or, for more customized behavior:
  43 + #
  44 + # acts_as_mappable :auto_geocode=>{:field=>:address,:error_message=>'bad address'}
  45 + #
  46 + # In both cases, it creates a before_validation_on_create callback to geocode the given column.
  47 + # For anything more customized, we recommend you forgo the auto_geocode option
  48 + # and create your own AR callback to handle geocoding.
37 49 def acts_as_mappable(options = {})
38 50 # Mix in the module, but ensure to do so just once.
39 51 return if self.included_modules.include?(GeoKit::ActsAsMappable::InstanceMethods)
@@ -48,9 +60,34 @@ def acts_as_mappable(options = {})
48 60 self.default_formula = options[:default_formula] || GeoKit::default_formula
49 61 self.lat_column_name = options[:lat_column_name] || 'lat'
50 62 self.lng_column_name = options[:lng_column_name] || 'lng'
  63 + if options.include?(:auto_geocode) && options[:auto_geocode]
  64 + # if the form auto_geocode=>true is used, let the defaults take over by suppling an empty hash
  65 + options[:auto_geocode] = {} if options[:auto_geocode] == true
  66 + cattr_accessor :auto_geocode_field, :auto_geocode_error_message
  67 + self.auto_geocode_field = options[:auto_geocode][:field] || 'address'
  68 + self.auto_geocode_error_message = options[:auto_geocode][:error_message] || 'could not locate address'
  69 +
  70 + # set the actual callback here
  71 + before_validation_on_create :auto_geocode_address
  72 + end
51 73 end
52 74 end
53 75
  76 + # this is the callback for auto_geocoding
  77 + def auto_geocode_address
  78 + address=self.send(auto_geocode_field)
  79 + geo=GeoKit::Geocoders::MultiGeocoder.geocode(address)
  80 +
  81 + if geo.success
  82 + self.send("#{lat_column_name}=", geo.lat)
  83 + self.send("#{lng_column_name}=", geo.lng)
  84 + else
  85 + errors.add(auto_geocode_field, auto_geocode_error_message)
  86 + end
  87 +
  88 + geo.success
  89 + end
  90 +
54 91 # Instance methods to mix into ActiveRecord.
55 92 module InstanceMethods #:nodoc:
56 93 # Mix class methods into module.
23 test/acts_as_mappable_test.rb
@@ -15,6 +15,11 @@ class Location < ActiveRecord::Base #:nodoc: all
15 15 acts_as_mappable
16 16 end
17 17
  18 +# for auto_geocode
  19 +class Store < ActiveRecord::Base
  20 + acts_as_mappable :auto_geocode=>true
  21 +end
  22 +
18 23 # Uses deviations from conventions.
19 24 class CustomLocation < ActiveRecord::Base #:nodoc: all
20 25 belongs_to :company
@@ -38,7 +43,7 @@ class ActsAsMappableTest < Test::Unit::TestCase #:nodoc: all
38 43 #puts "Rails Path #{RAILS_ROOT}"
39 44 #puts "Fixture Path: #{self.fixture_path}"
40 45 #self.fixture_path = ' /Users/bill_eisenhauer/Projects/geokit_test/test/fixtures/'
41   - fixtures :companies, :locations, :custom_locations
  46 + fixtures :companies, :locations, :custom_locations, :stores
42 47
43 48 def setup
44 49 @location_a = GeoKit::GeoLoc.new
@@ -457,4 +462,20 @@ def test_find_within_bounds_with_array_conditions
457 462 assert_equal 1, locations.size
458 463 end
459 464
  465 + def test_auto_geocode
  466 + GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("Irving, TX").returns(@location_a)
  467 + store=Store.new(:address=>'Irving, TX')
  468 + store.save
  469 + assert_equal store.lat,@location_a.lat
  470 + assert_equal store.lng,@location_a.lng
  471 + assert_equal 0, store.errors.size
  472 + end
  473 +
  474 + def test_auto_geocode_failure
  475 + GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("BOGUS").returns(GeoKit::GeoLoc.new)
  476 + store=Store.new(:address=>'BOGUS')
  477 + store.save
  478 + assert store.new_record?
  479 + assert_equal 1, store.errors.size
  480 + end
460 481 end
0  test/fixtures/stores.yml
No changes.
6 test/schema.rb
@@ -22,4 +22,10 @@
22 22 t.column :latitude, :decimal, :precision => 15, :scale => 10
23 23 t.column :longitude, :decimal, :precision => 15, :scale => 10
24 24 end
  25 +
  26 + create_table :stores, :force=> true do |t|
  27 + t.column :address, :string
  28 + t.column :lat, :decimal, :precision => 15, :scale => 10
  29 + t.column :lng, :decimal, :precision => 15, :scale => 10
  30 + end
25 31 end

0 comments on commit 47e3d0c

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