Model Validation - with a twist

I’m stuck in a catch-22 type problem. I have an insert form for a table
that uses the validation in the model. i.e. When you click submit on the
form and haven’t filled in the required fields then you get the nice
rails error messages saying “field can not be blank”. This works fine
until I introduce some more logic.

What I’m trying to do is to validate the form first and then perform
some more validation (using google to verify the address is real) before
saving the whole thing.

The link if @bstore.valid? just renders the action add but doesn’t show
the nice error messages saying the field was blank. Any ideas?

Here’s my code so far:

def create
@bstore = Bookstore.new(params[:bookstore])

if @bstore.valid?
	addresscheck = params[:bookstore][:address] + " " + 

params[:bookstore][:city] + ", " + params[:bookstore][:state] + " " +
params[:bookstore][:zipcode]
address2 = ‘q=’ + addresscheck
url=‘http://maps.google.com/maps/geo?
address = address2
address1 = URI.escape(address)
googleoutput = ‘&output=xml’
googlekey = ‘&key=mykey’
result=URI(url+address1+googleoutput+googlekey ).read
doc = Document.new(result.to_s)
root = doc.root
retstatus = root.elements[‘/kml/Response/Status/code’].text

	if retstatus.to_i == 200
		coordinates = 

root.elements[‘/kml/Response/Placemark/Point/coordinates’].text
long1, lat1, = coordinates.split(‘,’).map { |v| v.to_f }
params[:bookstore][:latitude] = lat1
params[:bookstore][:longitude] = long1
@bookstore = Bookstore.new(params[:bookstore])
if @bookstore.save
redirect_to :action => ‘add’
flash[:notice] = ‘Store was successfully added.’
else
render :action => ‘add’
end

	else
 		redirect_to :action => 'add'
 		flash[:notice] = 'We could not find that address. Please double 

check the address and try again.’
end
else
render :action => ‘add’
end
end

On 7/19/06, Chris W. [email protected] wrote:

params[:bookstore][:city] + ", " + params[:bookstore][:state] + " " +
retstatus = root.elements
if @bookstore.save
check the address and try again.’
Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails

It looks like there is a lot of code that could/(should?) go into the
model.

I think the entire address check should go in there. Since this is
effectively a data contrstraint.

In your validations you could use the validate method directly and check
the
address that way. Rails will automagically call this as part of it’s
validation check. Couple this with composed of and it should work out
pretty neat. Actually I don’t think you even need to use composed_of,
but
hey.

composed_of :store_address, :mapping => [%w( address address), %w(city
city), %w(state state), %w(zipcode zipcode)]

#(I’m guessing a bit here, ok well a lot)
protected
def validate
tmp = []

put a can’t be blank on these and collect the values

[ :address, :city, :state, :zipcode].each do |method|
errors.add( method, “can’t be blank”) if !self.store_address.send(
method )
tmp << self.store_address.send(method)
end
return if tmp.include?(nil) # If one was blank, return and don’t
bother
with the rest since the address won’t be valid

check the google map. I can’t comment on this since I’ve never used

it. Just copied what you had.
result = URI(“http://maps.google.com/maps/geo/?” <<
URI.escape(tmp.join("
")) << “&output=xml&key=mykey”).read
doc = Document.new( result.to_s )
retstatus = doc.root.elements[‘/kml/Response/Status/code’].text

if retstatus.to_i == 200
self.longitude, self.latitude =
doc.root.elements[‘/kml/Response/Placemark/Point/coordinates’].text.split(“,”).map
{ |v| v.to_f }
else
errors.add( “store_address”, “could not be found. Please double
check
and try again”)
end
end

This should add, blank errors, and also errors to say that an address
could
not be found on google.

Then in your controller it becomes very simple

def create
@bstore = Bookstore.new( params[:bookstore] )
if @bstore.save
flash[:notice] = “Store was successfully added”
redirect_to :action => “success_action” and return
end
redirect_to :action => “add”
end

I hope this works. I haven’t tested it.

On 7/20/06, Chris W. [email protected] wrote:

[‘/kml/Response/Placemark/Point/coordinates’].text.split(“,”).map
{ |v| v.to_f }

I’m not familiar with what
doc.root.elements[‘/kml/Response/Placemark/Point/coordinates’].text
returns but looking at your code I think that line should just do what
you
had but all in one go. Provided that your model has longitude and
latitude
attributes of course.

I hope it works. As I said, it’s not tested :wink:

Hence the code that I had:

Hi Daniel,

Thank you very much for the reply. I had a feeling that the google map
address search should go into the model but I really didn’t know how to
do it. Part of the google call is to get the latitude and longitude of
the address in question and insert that along with all the other data.

Will this line do it? self.longitude, self.latitude =
doc.root.elements[‘/kml/Response/Placemark/Point/coordinates’].text.split(“,”).map
{ |v| v.to_f }

Hence the code that I had:

coordinates =
root.elements[‘/kml/Response/Placemark/Point/coordinates’].text
long1, lat1, = coordinates.split(‘,’).map { |v| v.to_f }
params[:bookstore][:latitude] = lat1
params[:bookstore][:longitude] = long1

If your method works then that’ll be awesome!!

I’ll try to out and post any questions or problems I run into. Thanks
very much.

Chris

Daniel ----- wrote:

composed_of :store_address, :mapping => [%w( address address), %w(city
city), %w(state state), %w(zipcode zipcode)]

#(I’m guessing a bit here, ok well a lot)
protected
def validate
tmp = []

put a can’t be blank on these and collect the values

[ :address, :city, :state, :zipcode].each do |method|
errors.add( method, “can’t be blank”) if !self.store_address.send(
method )
tmp << self.store_address.send(method)
end
return if tmp.include?(nil) # If one was blank, return and don’t
bother
with the rest since the address won’t be valid

check the google map. I can’t comment on this since I’ve never used

it. Just copied what you had.
result = URI(“http://maps.google.com/maps/geo/?” <<
URI.escape(tmp.join("
")) << “&output=xml&key=mykey”).read
doc = Document.new( result.to_s )
retstatus = doc.root.elements[‘/kml/Response/Status/code’].text

if retstatus.to_i == 200
self.longitude, self.latitude =
doc.root.elements[‘/kml/Response/Placemark/Point/coordinates’].text.split(“,”).map
{ |v| v.to_f }
else
errors.add( “store_address”, “could not be found. Please double
check
and try again”)
end
end

This should add, blank errors, and also errors to say that an address
could
not be found on google.

Then in your controller it becomes very simple

def create
@bstore = Bookstore.new( params[:bookstore] )
if @bstore.save
flash[:notice] = “Store was successfully added”
redirect_to :action => “success_action” and return
end
redirect_to :action => “add”
end

I hope this works. I haven’t tested it.

Nevermind.

With your help, I got it to work. Excellent. Thank you so much!

Sorry to bug you again (I’m still learning. :slight_smile: ) I reverted back to my
code so as to understand it a little better but I’m getting an
Application error when I try to run this. Any suggestions on what could
be causing the problem?

Here is the code for my model:

class Bookstore < ActiveRecord::Base

require ‘open-uri’
require “rexml/document”
include REXML

validates_presence_of :name
validates_presence_of :address
validates_presence_of :city
validates_presence_of :zipcode

protected
def validate

addresscheck = [:address] + " " + [:city] + ", " + [:state] + " " +
[:zipcode]
address2 = ‘q=’ + addresscheck
url=‘http://maps.google.com/maps/geo?
address = address2
address1 = URI.escape(address)
googleoutput = ‘&output=xml’
googlekey = ‘&key=mykey’
result=URI(url+address1+googleoutput+googlekey ).read
doc = Document.new(result.to_s)
root = doc.root
retstatus= root.elements[‘/kml/Response/Status/code’].text

if retstatus.to_i == 200
self.longitude, self.latitude =
doc.root.elements[‘/kml/Response/Placemark/Point/coordinates’].text.split(“,”).map{
|v| v.to_f }
else
errors.add( “address”, “could not be found. Please double check and
try again”)
end
end

end