Pregunta

Below is my class that I am using geocoder with. I am having trouble checking whether or not address that is put through geocoder is valid. This code works great when I am adding a company/editing a company that has addresses that when put through geocoder gives a latitude and longitude. But I need the code to work so when a user tries to put a address that does not give a latitude and longitude, on either an edit or creation of a company, that an error pops up and informs the user that the address is invalid.

Model

class Company < ActiveRecord::Base
    validates :name, presence: true, length: { maximum: 30 }
    validates :website, presence: true
    validates :address, presence: true
    validates :description, presence: true
    validates :primary_field, presence: true

    geocoded_by :address
    before_save :geocode, if: ->(obj){ obj.address.present? and obj.address_changed? }

    after_save :set_popup_value

    private

     ...

end

View

<% provide(:title, 'Add Company') %>
<h1>Add Company</h1>
<div class="row">
  <div class="span6 offset3">
    <%= form_for(@company) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :website %>
      <%= f.text_field :website %>

      <%= f.label :primary_field %>
      <%= f.select :primary_field, @primary_field %>

      <%= f.label :address %>
      <%= f.text_field :address, :placeholder => '123 Test St, City State Zip'%>

      <%= f.label :description %>
      <%= f.text_area :description, :size => "30x5" %>

      <%= f.submit "Add Company", class: "btn btn-large btn-primary" %>
    <% end %>
  </div>
</div>
¿Fue útil?

Solución

So, having been able to tinker around with this for a few days I have come up with a couple solutions. The first was really hacky, but accomplished what I needed. My second solution is much more elegant and works great from what I have been able to tell so far! I hope others find this useful.

I handle all of the logic in my controller in just a few lines.

geocoded_by :address
after_validation :geocode, if: ->(obj){ obj.address.present? and obj.address_changed? }
after_validation :lat_changed?

after_save :set_popup_value

private

# This is used to make sure that our address is actually parsed properly and we
# get a valuable lat/long
def lat_changed?
    if (self.address_changed?)
        if !(self.latitude_changed?)
            self.errors.add(:address, "is not valid")
            return false
        end
    end
    return true
end

By creating another after_validation after my geocode, I am able to check if the appropriate values changed, and if they changed in a certain way, than I know there was an issue in the geocoding.

If the address changed, but the latitude didnt change, than you know that when the address got put through geocoder that it didn't get a returned lat/long. And if that is the case then we just add an error to throw to the user and return false, which stops the save to the database.

I hope others with this issue are able to find this post!

Otros consejos

You can set in the model like this example below to validate address the geocode will be called only once when the address changed. In geocoded_by method we set explicitly to write latitude and longitude so than when the address would not be found those columns will be set to nil.

class Company < ActiveRecord::Base
   geocoded_by :address do |object, results|
    if results.present?
     object.latitude = results.first.latitude
     object.longitude = results.first.longitude
    else
     object.latitude = nil
     object.longitude = nil
    end
  end

  before_validation :geocode, if: :address_changed?

  validates :address, presence: true
  validates :found_address_presence

  def found_address_presence
    if latitude.blank? || longitude.blank?
      errors.add(:address, "We couldn't find the address")
    end
  end
end

you're going to probably want to do a web/service call to the geocoder service. How you do this depends on what you want: ajaxy request or background job,etc.

This is a pretty general question for SO, you might get some better traction in programmers.stackexchange.com

But, here are some links to help you out:

Google Geocoding API: https://developers.google.com/maps/documentation/geocoding/?csw=1

OpenStreetMap: http://wiki.openstreetmap.org/wiki/Nominatim#Reverse_Geocoding_.2F_Address_lookup

Since you're using Geocoder, you just need to have an opportunity to check the result of geocoder before saving. This process is explained in the section "Advanced geocoding" in it's github page. To just provide an example, you can do this:

geocoded_by :address do |obj,results|
  ....
end

After validation of the result, if you haven't obtained your coordinates, you can make your exception/add validation error/whatever.

Of course this will mean, that you have to call web service to see, if the address is ok - like others say, it would be nice to have some basic validation done before this.

What Sean Callahan said is ok. You can improve the code doing:

def lat_changed?
  if address_changed? && !latitude_changed?
    self.errors.add(:address, "is not valid")
  end
end

Is not necessary to return true/false

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top