Search code examples
ruby-on-railsrails-geocoder

Correct callbacks for geocoder validation


I hope this is not a dupe but since I haven't found a satisfying answer so far I'm gonna put my question. I'm a rails beginner so please be gentle on me ;)

For a small mapping project we're using geocoder gem. We have a model Place containing :street, :house_number, :postal_code, :city wrapped up as a simple string by :address as the virtual attribute which is being passed to geocoder. We're doing the querying using nominatim (-> OSM data). Unlike the google API nominatim returns nil if the given address does not correspond to a lat/lon pair. So far our model file looks like this

class Place < ActiveRecord::Base
  has_many :descriptions, :dependent => :delete_all
  accepts_nested_attributes_for :descriptions

  geocoded_by :address
  before_validation :geocode

  validates :address, presence: true
  validates :name, presence: true
  validates :longitude, presence: true,
                        numericality: { less_than_or_equal_to: 90, greater_than_or_equal_to: -90 }
  validates :latitude, presence: true,
                       numericality: { less_than_or_equal_to: 180, greater_than_or_equal_to: -180 }
  validates :categories, presence: true

  def address
    ["#{self.street} #{self.house_number}", self.postal_code, self.city].join(', ')
  end
end

Unlike most of the examples I've looked at I'm using before_validation :geocode because otherwise lat and lon would be nil before they become evaluated raising a validation error (not present). So far this works for creating new places but I'm unable to modify the validation to make it work for the update action inside my controller. Basically I want to check if the changes applied to address are valid (i.e. whether the API finds a lat/lon pair to the given address). Studying the documentation i found, that i do a conditional callback like before_validate :geocode, if: :address_changed? but since the parameters of the update request are not available inside my model I have no clue how to check that. I'd be glad on any hint!

edit:

Accepted answer below is right, wasn't working instantly because inside my controller I was using update instead of update_attributes.


Solution

  • Your model doesn't need to know the parameters to update the request. You can check if the address changed by checking if any of the columns which make up your address have changed. Since address isn't a column, you'll need to define address_changed? like so:

    before_validation :geocode, if: :address_changed?
    
    def address_changed?
      street_changed? || house_number_changed? || postal_code_changed? || city_changed?
    end
    

    Note that street, house_number, city and postal_code will all need to be columns in order for the above to work.