Best practices: Async Reverse Geocoding with Ruby and Geocoder
What's up guys? Today I will share with you how I approach reverse geocoding using the Geocoder gem.
As you may know the Ruby gem Geocoder lets you do reverse geocoding "automatically" in the model by doing reverse_geocoded_by :latitude, :longitude
and that is cool, but I found a better way...
First, the Geocoder gem makes a request to the Google maps API, and that takes time making my response a slow. So I decided to do the reverse geocoding in an async process after my record detected a change in my latitude and longitude fields.
app/models/location.rb
class Location < ApplicationRecord
...
after_commit -> { ReverseGeocodingWorker.perform_async(id) }, if: :coordinates_changed?
private
def coordinates_changed?
latitude_changed? && longitude_changed?
end
...
end
Now, lets see the ReverseGeocodingWorker
class in charge of updating the location record with the results of the Google Maps API
app/workers/reverse_geocoding_worker.rb
class ReverseGeocodingWorker
include Sidekiq::Worker
sidekiq_options retry: true
def perform(location_id)
location = Location.find(location_id)
results = Geocoder.search([location.latitude, location.longitude])
if results
result = result.first
location.update_attributes(
street_address: result.street_address,
city: result.city,
state: result.state,
postal_code: result.postal_code
)
end
end
end
This is gonna speed up your response time since you are delegating the reverse geocoding process to the worker.
Lastly I want to share one bonus tip for the Geocoder setup which is gonna increase the response time and will also save your Google Maps quota, just by enabling the cache
option:
config/initializers/geocoder.rb
Geocoder.configure(
# Geocoding options
lookup: :google,
ip_lookup: :freegeoip,
use_https: false,
cache: Redis.new
)
Hope you good luck with your reverse geocoding feature.
Please let me know if you have questions or want me to write new posts about best practices for Ruby apps.
Cheers!