acts_as_geocodable 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ debug.log
2
+ pkg
@@ -0,0 +1,13 @@
1
+ 0.2.1 - 2008-8-8
2
+ * Results are now WillPaginate compatible
3
+
4
+ 0.2.0 - 2007-10-27
5
+ * Added validates_as_geocodable (Mark Van Holstyn)
6
+ * Allow address mapping to be a single field (Mark Van Holstyn)
7
+
8
+ 0.1.0
9
+ * Added remote_location to get a users location based on their remote_ip
10
+ * renamed :city to :locality in address mapping to be consistent with Graticule 0.2
11
+ create a migration with:
12
+ rename_column :geocodes, :city, :locality
13
+ * replace #full_address with #to_location
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2006 Daniel Morrison, Collective Idea
2
+ http://collectiveidea.com
3
+
4
+ Testing code was heavily influenced by Rick Olson. Thanks, Rick!
5
+ Testing Portions Copyright (c) 2006 Rick Olson
6
+ http://techno-weenie.net
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining
9
+ a copy of this software and associated documentation files (the
10
+ "Software"), to deal in the Software without restriction, including
11
+ without limitation the rights to use, copy, modify, merge, publish,
12
+ distribute, sublicense, and/or sell copies of the Software, and to
13
+ permit persons to whom the Software is furnished to do so, subject to
14
+ the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be
17
+ included in all copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,104 @@
1
+ = acts_as_geocodable
2
+
3
+ acts_as_geocodable is a plugin to help build geo-aware applications. It automatically geocodes your models when they are saved, giving you the ability to search by location and calculate distances between records.
4
+
5
+ == Usage
6
+
7
+ event = Event.create :street => "777 NE Martin Luther King, Jr. Blvd.",
8
+ :locality => "Portland", :region => "Oregon", :postal_code => 97232
9
+
10
+ event.geocode.latitude #=> 45.529100000000
11
+ event.geocode.longitude #=> -122.644200000000
12
+
13
+ event.distance_to "49423" #=> 1807.66560483205
14
+
15
+ Event.find(:all, :within => 50, :origin => "97232")
16
+
17
+ Event.find(:nearest, :origin => "Portland, OR")
18
+
19
+ == Upgrading
20
+
21
+ If you're upgrading from a previous version of this plugin, note that :city has been renamed to :locality to be consistent with Graticule 0.2. Create a migration that has:
22
+
23
+ rename_column :geocodes, :city, :locality
24
+
25
+ Also remember to change your mapping in your geocodable classes to use the :locality key instead of :city:
26
+
27
+ class Event < ActiveRecord::Base
28
+ acts_as_geocodable :address => {:street => :address1, :locality => :city,
29
+ :region => :state, :postal_code => :zip}
30
+ end
31
+
32
+ == Installation
33
+
34
+ Graticule[link:http://rubyforge.org/projects/graticule] is used for all the heavy lifting.
35
+
36
+ gem install graticule --include-dependencies
37
+
38
+ Install as a plugin
39
+
40
+ script/plugin install git://github.com/collectiveidea/acts_as_geocodable.git
41
+
42
+ Or, install as a gem
43
+
44
+ gem install acts_as_geocodable --source=http://gemcutter.org
45
+
46
+
47
+ == Upgrading
48
+
49
+ Before October 2008, precision wasn't included in the Geocode model. Make sure you add a string precision column to your geocode table if you're upgrading from an older version, and update Graticule.
50
+
51
+ == Configuration
52
+
53
+ Create the required tables
54
+
55
+ script/generate geocodable_migration add_geocodable_tables
56
+ rake db:migrate
57
+
58
+ Set the default geocoder in your environment.rb file.
59
+
60
+ Geocode.geocoder = Graticule.service(:yahoo).new 'your_api_key'
61
+
62
+ Then, in each model you want to make geocodable, add acts_as_geocodable.
63
+
64
+ class Event < ActiveRecord::Base
65
+ acts_as_geocodable
66
+ end
67
+
68
+ The only requirement is that your model must have address fields. By default, acts_as_geocodable looks for attributes called +street+, +locality+, +region+, +postal_code+, and +country+. To change these, you can provide a mapping in the <tt>:address</tt> option:
69
+
70
+ class Event < ActiveRecord::Base
71
+ acts_as_geocodable :address => {:street => :address1, :locality => :city, :region => :state, :postal_code => :zip}
72
+ end
73
+
74
+ If that doesn't meet your needs, simply override the default +to_location+ method in your model, and return a Graticule::Location with those attributes set.
75
+
76
+ acts_as_geocodable can also update your address fields with the data returned from the geocoding service:
77
+
78
+ class Event < ActiveRecord::Base
79
+ acts_as_geocodable :normalize_address => true
80
+ end
81
+
82
+ == IP-based Geocoding
83
+
84
+ acts_as_geocodable adds a remote_location method in your controllers that uses http://hostip.info to guess remote users location based on their IP address.
85
+
86
+ def index
87
+ @nearest = Store.find(:nearest, :origin => remote_location) if remote_location
88
+ @stores = Store.find(:all)
89
+ end
90
+
91
+ Keep in mind that IP-based geocoding is not always accurate, and often will not return any results.
92
+
93
+ == Development
94
+
95
+ The source code is available at:
96
+ http://github.com/collectiveidea/acts_as_geocodable
97
+ git://github.com/collectiveidea/acts_as_geocodable.git
98
+
99
+ Patches and suggestions are welcome!
100
+
101
+ == To Do
102
+
103
+ * Documentation!!!
104
+ * configurable formulas
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'load_multi_rails_rake_tasks'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc 'Test the acts_as_geocodable plugin.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the acts_as_geocodable plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'ActsAsGeocodable'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = 'acts_as_geocodable'
29
+ gemspec.summary = 'Simple geocoding for Rails ActiveRecord models'
30
+ gemspec.description = 'Simple geocoding for Rails ActiveRecord models. See the README for more details.'
31
+ gemspec.email = 'info@collectiveidea.com'
32
+ gemspec.homepage = 'http://github.com/collectiveidea/acts_as_geocodable'
33
+ gemspec.authors = ['Daniel Morrison', 'Brandon Keepers']
34
+ end
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,7 @@
1
+ author:
2
+ name: Brandon Keepers and Daniel Morrison, Collective Idea
3
+ homepage: http://collectiveidea.com
4
+ summary: A plugin to help build geo-aware applications
5
+ homepage: http://opensoul.org/2007/2/13/geocoding-as-easy-as-1-2
6
+ plugin: http://source.collectiveidea.com/public/rails/plugins/acts_as_geocodable
7
+ license: MIT
@@ -0,0 +1,69 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{acts_as_geocodable}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Daniel Morrison", "Brandon Keepers"]
12
+ s.date = %q{2009-10-21}
13
+ s.description = %q{Simple geocoding for Rails ActiveRecord models. See the README for more details.}
14
+ s.email = %q{info@collectiveidea.com}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "CHANGELOG",
21
+ "MIT-LICENSE",
22
+ "README",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "about.yml",
26
+ "acts_as_geocodable.gemspec",
27
+ "generators/geocodable_migration/USAGE",
28
+ "generators/geocodable_migration/geocodable_migration_generator.rb",
29
+ "generators/geocodable_migration/templates/migration.rb",
30
+ "install.rb",
31
+ "lib/acts_as_geocodable.rb",
32
+ "lib/acts_as_geocodable/geocode.rb",
33
+ "lib/acts_as_geocodable/geocoding.rb",
34
+ "lib/acts_as_geocodable/remote_location.rb",
35
+ "lib/acts_as_geocodable/tasks/acts_as_geocodable_tasks.rake",
36
+ "rails/init.rb",
37
+ "test/acts_as_geocodable_test.rb",
38
+ "test/db/database.yml",
39
+ "test/db/schema.rb",
40
+ "test/fixtures/cities.yml",
41
+ "test/fixtures/geocodes.yml",
42
+ "test/fixtures/geocodings.yml",
43
+ "test/fixtures/vacations.yml",
44
+ "test/geocode_test.rb",
45
+ "test/test_helper.rb",
46
+ "uninstall.rb"
47
+ ]
48
+ s.homepage = %q{http://github.com/collectiveidea/acts_as_geocodable}
49
+ s.rdoc_options = ["--charset=UTF-8"]
50
+ s.require_paths = ["lib"]
51
+ s.rubygems_version = %q{1.3.5}
52
+ s.summary = %q{Simple geocoding for Rails ActiveRecord models}
53
+ s.test_files = [
54
+ "test/acts_as_geocodable_test.rb",
55
+ "test/db/schema.rb",
56
+ "test/geocode_test.rb",
57
+ "test/test_helper.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
65
+ else
66
+ end
67
+ else
68
+ end
69
+ end
@@ -0,0 +1,12 @@
1
+ Description:
2
+ The geocodable migration generator creates a migration which you can use to generate two tables:
3
+
4
+ geocodes - Contains the latitude and longitude coordinates, with the address they refer to.
5
+ geocodings - The polymorphic join table.
6
+
7
+
8
+ Example:
9
+ ./script/generate geocodable_migration add_geocodable_tables
10
+
11
+ With 4 existing migrations, this will create an AddGeocodableTables migration in the
12
+ file db/migrate/5_add_geocodable_tables.rb
@@ -0,0 +1,7 @@
1
+ class GeocodableMigrationGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'migration.rb', 'db/migrate'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ class <%= class_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table "geocodes" do |t|
4
+ t.column "latitude", :decimal, :precision => 15, :scale => 12
5
+ t.column "longitude", :decimal, :precision => 15, :scale => 12
6
+ t.column "query", :string
7
+ t.column "street", :string
8
+ t.column "locality", :string
9
+ t.column "region", :string
10
+ t.column "postal_code", :string
11
+ t.column "country", :string
12
+ t.column "precision", :string
13
+ end
14
+
15
+ add_index "geocodes", ["longitude"], :name => "geocodes_longitude_index"
16
+ add_index "geocodes", ["latitude"], :name => "geocodes_latitude_index"
17
+ add_index "geocodes", ["query"], :name => "geocodes_query_index", :unique => true
18
+ add_index "geocodes", ["locality"], :name => "geocodes_locality_index"
19
+ add_index "geocodes", ["region"], :name => "geocodes_region_index"
20
+ add_index "geocodes", ["postal_code"], :name => "geocodes_postal_code_index"
21
+ add_index "geocodes", ["country"], :name => "geocodes_country_index"
22
+ add_index "geocodes", ["precision"], :name => "geocodes_precision_index"
23
+
24
+
25
+ create_table "geocodings" do |t|
26
+ t.column "geocodable_id", :integer
27
+ t.column "geocode_id", :integer
28
+ t.column "geocodable_type", :string
29
+ end
30
+
31
+ add_index "geocodings", ["geocodable_type"], :name => "geocodings_geocodable_type_index"
32
+ add_index "geocodings", ["geocode_id"], :name => "geocodings_geocode_id_index"
33
+ add_index "geocodings", ["geocodable_id"], :name => "geocodings_geocodable_id_index"
34
+ end
35
+
36
+ def self.down
37
+ drop_table :geocodes
38
+ drop_table :geocodings
39
+ end
40
+ end
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,286 @@
1
+ require 'acts_as_geocodable/geocoding'
2
+ require 'acts_as_geocodable/geocode'
3
+ require 'acts_as_geocodable/remote_location'
4
+
5
+ module CollectiveIdea #:nodoc:
6
+ module Acts #:nodoc:
7
+ module Geocodable #:nodoc:
8
+
9
+ def self.included(mod)
10
+ mod.extend(ClassMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ # Make a model geocodable.
16
+ #
17
+ # class Event < ActiveRecord::Base
18
+ # acts_as_geocodable
19
+ # end
20
+ #
21
+ # == Options
22
+ # * <tt>:address</tt>: A hash that maps geocodable attirbutes (<tt>:street</tt>,
23
+ # <tt>:locality</tt>, <tt>:region</tt>, <tt>:postal_code</tt>, <tt>:country</tt>)
24
+ # to your model's address fields, or a symbol to store the entire address in one field
25
+ # * <tt>:normalize_address</tt>: If set to true, you address fields will be updated
26
+ # using the address fields returned by the geocoder. (Default is +false+)
27
+ # * <tt>:units</tt>: Default units-<tt>:miles</tt> or <tt>:kilometers</tt>-used for
28
+ # distance calculations and queries. (Default is <tt>:miles</tt>)
29
+ #
30
+ def acts_as_geocodable(options = {})
31
+ options = {
32
+ :address => {
33
+ :street => :street, :locality => :locality, :region => :region,
34
+ :postal_code => :postal_code, :country => :country},
35
+ :normalize_address => false,
36
+ :distance_column => 'distance',
37
+ :units => :miles
38
+ }.merge(options)
39
+
40
+ write_inheritable_attribute :acts_as_geocodable_options, options
41
+ class_inheritable_reader :acts_as_geocodable_options
42
+
43
+ define_callbacks :after_geocoding
44
+
45
+ has_one :geocoding, :as => :geocodable, :include => :geocode, :dependent => :destroy
46
+
47
+ after_save :attach_geocode
48
+
49
+ include CollectiveIdea::Acts::Geocodable::InstanceMethods
50
+ extend CollectiveIdea::Acts::Geocodable::SingletonMethods
51
+ end
52
+
53
+ end
54
+
55
+ module SingletonMethods
56
+
57
+ # Extends ActiveRecord's find method to be geo-aware.
58
+ #
59
+ # Model.find(:all, :within => 10, :origin => "Chicago, IL")
60
+ #
61
+ # Whenever find is called with an <tt>:origin</tt>, a +distance+ attribute
62
+ # indicating the distance to the origin is added to each of the results:
63
+ #
64
+ # Model.find(:first, :origin => "Portland, OR").distance #=> 388.383
65
+ #
66
+ # +acts_as_geocodable+ adds 2 other retrieval approaches to ActiveRecord's default
67
+ # find by id, find <tt>:first</tt>, and find <tt>:all</tt>:
68
+ #
69
+ # * <tt>:nearest</tt>: find the nearest location to the given origin
70
+ # * <tt>:farthest</tt>: find the farthest location from the given origin
71
+ #
72
+ # Model.find(:nearest, :origin => "Grand Rapids, MI")
73
+ #
74
+ # == Options
75
+ #
76
+ # * <tt>:origin</tt>: A Geocode, String, or geocodable model that specifies
77
+ # the origin
78
+ # * <tt>:within</tt>: Limit to results within this radius of the origin
79
+ # * <tt>:beyond</tt>: Limit to results outside of this radius from the origin
80
+ # * <tt>:units</tt>: Units to use for <tt>:within</tt> or <tt>:beyond</tt>.
81
+ # Default is <tt>:miles</tt> unless specified otherwise in the +acts_as_geocodable+
82
+ # declaration.
83
+ #
84
+ def find(*args)
85
+ options = args.extract_options!
86
+ origin = location_to_geocode options.delete(:origin)
87
+ if origin
88
+ options[:units] ||= acts_as_geocodable_options[:units]
89
+ add_distance_to_select!(origin, options)
90
+ with_proximity!(args, options) do
91
+ geocode_conditions!(options, origin) do
92
+ join_geocodes { super *args.push(options) }
93
+ end
94
+ end
95
+ else
96
+ super *args.push(options)
97
+ end
98
+ end
99
+
100
+ # Extends ActiveRecord's count method to be geo-aware.
101
+ #
102
+ # Model.count(:within => 10, :origin => "Chicago, IL")
103
+ #
104
+ # == Options
105
+ #
106
+ # * <tt>:origin</tt>: A Geocode, String, or geocodable model that specifies
107
+ # the origin
108
+ # * <tt>:within</tt>: Limit to results within this radius of the origin
109
+ # * <tt>:beyond</tt>: Limit to results outside of this radius from the origin
110
+ # * <tt>:units</tt>: Units to use for <tt>:within</tt> or <tt>:beyond</tt>.
111
+ # Default is <tt>:miles</tt> unless specified otherwise in the +acts_as_geocodable+
112
+ # declaration.
113
+ #
114
+ def count(*args)
115
+ options = args.extract_options!
116
+ origin = location_to_geocode options.delete(:origin)
117
+ if origin
118
+ options[:units] ||= acts_as_geocodable_options[:units]
119
+ with_proximity!(args, options) do
120
+ geocode_conditions!(options, origin) do
121
+ join_geocodes { super *args.push(options) }
122
+ end
123
+ end
124
+ else
125
+ super *args.push(options)
126
+ end
127
+ end
128
+
129
+ # Convert the given location to a Geocode
130
+ def location_to_geocode(location)
131
+ case location
132
+ when Geocode then location
133
+ when InstanceMethods then location.geocode
134
+ when String, Fixnum then Geocode.find_or_create_by_query(location)
135
+ end
136
+ end
137
+
138
+ def validates_as_geocodable(options = {})
139
+ options = options.reverse_merge :message => "Address could not be geocoded.", :allow_nil => false
140
+ validate do |geocodable|
141
+ if !(options[:allow_nil] && geocodable.to_location.attributes.all?(&:blank?)) &&
142
+ !Geocode.find_or_create_by_location(geocodable.to_location)
143
+ geocodable.errors.add_to_base options[:message]
144
+ end
145
+ end
146
+ end
147
+
148
+ private
149
+
150
+ def add_distance_to_select!(origin, options)
151
+ (options[:select] ||= "#{table_name}.*") <<
152
+ ", #{sql_for_distance(origin, options[:units])} AS
153
+ #{acts_as_geocodable_options[:distance_column]}"
154
+ end
155
+
156
+ def with_proximity!(args, options)
157
+ if [:nearest, :farthest].include?(args.first)
158
+ raise ArgumentError, ":include cannot be specified with :nearest and :farthest" if options[:include]
159
+ direction = args.first == :nearest ? "ASC" : "DESC"
160
+ args[0] = :first
161
+ with_scope :find => { :order => "#{acts_as_geocodable_options[:distance_column]} #{direction}"} do
162
+ yield
163
+ end
164
+ else
165
+ yield
166
+ end
167
+ end
168
+
169
+ def join_geocodes(&block)
170
+ with_scope :find => { :joins => "JOIN geocodings ON
171
+ #{table_name}.#{primary_key} = geocodings.geocodable_id AND
172
+ geocodings.geocodable_type = '#{class_name}'
173
+ JOIN geocodes ON geocodings.geocode_id = geocodes.id" } do
174
+ yield
175
+ end
176
+ end
177
+
178
+ def geocode_conditions!(options, origin)
179
+ units = options.delete(:units)
180
+ conditions = []
181
+ conditions << "#{sql_for_distance(origin, units)} <= #{options.delete(:within)}" if options[:within]
182
+ conditions << "#{sql_for_distance(origin, units)} > #{options.delete(:beyond)}" if options[:beyond]
183
+ if conditions.empty?
184
+ yield
185
+ else
186
+ with_scope(:find => { :conditions => conditions.join(" AND ") }) { yield }
187
+ end
188
+ end
189
+
190
+ def sql_for_distance(origin, units = acts_as_geocodable_options[:units])
191
+ Graticule::Distance::Spherical.to_sql(
192
+ :latitude => origin.latitude,
193
+ :longitude => origin.longitude,
194
+ :latitude_column => "geocodes.latitude",
195
+ :longitude_column => "geocodes.longitude",
196
+ :units => units
197
+ )
198
+ end
199
+
200
+ end
201
+
202
+ module InstanceMethods
203
+
204
+ # Get the geocode for this model
205
+ def geocode
206
+ geocoding.geocode if geocoding
207
+ end
208
+
209
+ # Create a Graticule::Location
210
+ def to_location
211
+ returning Graticule::Location.new do |location|
212
+ [:street, :locality, :region, :postal_code, :country].each do |attr|
213
+ location.send "#{attr}=", geo_attribute(attr)
214
+ end
215
+ end
216
+ end
217
+
218
+ # Get the distance to the given destination. The destination can be an
219
+ # acts_as_geocodable model, a Geocode, or a string
220
+ #
221
+ # myhome.distance_to "Chicago, IL"
222
+ # myhome.distance_to "49423"
223
+ # myhome.distance_to other_model
224
+ #
225
+ # == Options
226
+ # * <tt>:units</tt>: <tt>:miles</tt> or <tt>:kilometers</tt>
227
+ # * <tt>:formula</tt>: The formula to use to calculate the distance. This can
228
+ # be any formula supported by Graticule. The default is <tt>:haversine</tt>.
229
+ #
230
+ def distance_to(destination, options = {})
231
+ units = options[:units] || acts_as_geocodable_options[:units]
232
+ formula = options[:formula] || :haversine
233
+
234
+ geocode = self.class.location_to_geocode(destination)
235
+ self.geocode.distance_to(geocode, units, formula)
236
+ end
237
+
238
+ protected
239
+
240
+ # Perform the geocoding
241
+ def attach_geocode
242
+ new_geocode = Geocode.find_or_create_by_location self.to_location unless self.to_location.blank?
243
+ if new_geocode && self.geocode != new_geocode
244
+ self.geocoding = Geocoding.new :geocode => new_geocode
245
+ self.update_address self.acts_as_geocodable_options[:normalize_address]
246
+ callback :after_geocoding
247
+ elsif !new_geocode && self.geocoding
248
+ self.geocoding.destroy
249
+ end
250
+ rescue Graticule::Error => e
251
+ logger.warn e.message
252
+ end
253
+
254
+
255
+ def update_address(force = false)
256
+ unless self.geocode.blank?
257
+ if self.acts_as_geocodable_options[:address].is_a? Symbol
258
+ method = self.acts_as_geocodable_options[:address]
259
+ if self.respond_to?("#{method}=") && (self.send(method).blank? || force)
260
+ self.send "#{method}=", self.geocode.to_location.to_s
261
+ end
262
+ else
263
+ self.acts_as_geocodable_options[:address].each do |attribute,method|
264
+ if self.respond_to?("#{method}=") && (self.send(method).blank? || force)
265
+ self.send "#{method}=", self.geocode.send(attribute)
266
+ end
267
+ end
268
+ end
269
+
270
+ update_without_callbacks
271
+ end
272
+ end
273
+
274
+ def geo_attribute(attr_key)
275
+ if self.acts_as_geocodable_options[:address].is_a? Symbol
276
+ attr_name = self.acts_as_geocodable_options[:address]
277
+ attr_key == :street ? self.send(attr_name) : nil
278
+ else
279
+ attr_name = self.acts_as_geocodable_options[:address][attr_key]
280
+ attr_name && self.respond_to?(attr_name) ? self.send(attr_name) : nil
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end