# Model Validation - with a twist

**URL:** <https://www.ruby-forum.com/t/model-validation-with-a-twist/98981>\
**Category:** Rails\
**Created:** [July 19, 2006, 3:20pm UTC](https://www.ruby-forum.com/t/model-validation-with-a-twist/98981 "2006-07-19T15:20:41Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![supasta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@supasta](https://www.ruby-forum.com/u/supasta)\
**Post date:** [July 19, 2006, 3:20pm UTC](https://www.ruby-forum.com/t/model-validation-with-a-twist/98981/1 "2006-07-19T15:20:41Z")

</div>

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?](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

---

<div class="post-metadata">

**Author:** ![supasta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@supasta](https://www.ruby-forum.com/u/supasta)\
**Post date:** [July 19, 2006, 4:49pm UTC](https://www.ruby-forum.com/t/model-validation-with-a-twist/98981/2 "2006-07-19T16:49:19Z")

</div>

On 7/19/06, Chris W. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> params[:bookstore][:city] + ", " + params[:bookstore][:state] + " " +  
> retstatus = root.elements  
> if @bookstore.save  
> check the address and try again.’  
> Rails mailing list  
> removed\_email\_address@domain.invalid  
> [http://lists.rubyonrails.org/mailman/listinfo/rails](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/?](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.

---

<div class="post-metadata">

**Author:** ![supasta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@supasta](https://www.ruby-forum.com/u/supasta)\
**Post date:** [July 19, 2006, 5:14pm UTC](https://www.ruby-forum.com/t/model-validation-with-a-twist/98981/3 "2006-07-19T17:14:11Z")

</div>

On 7/20/06, Chris W. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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 😉

Hence the code that I had:

---

<div class="post-metadata">

**Author:** ![supasta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@supasta](https://www.ruby-forum.com/u/supasta)\
**Post date:** [July 19, 2006, 5:06pm UTC](https://www.ruby-forum.com/t/model-validation-with-a-twist/98981/4 "2006-07-19T17:06:14Z")

</div>

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/?](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.

---

<div class="post-metadata">

**Author:** ![supasta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@supasta](https://www.ruby-forum.com/u/supasta)\
**Post date:** [July 19, 2006, 7:14pm UTC](https://www.ruby-forum.com/t/model-validation-with-a-twist/98981/5 "2006-07-19T19:14:28Z")

</div>

Nevermind.

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

---

<div class="post-metadata">

**Author:** ![supasta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ebca7d/32.png) [@supasta](https://www.ruby-forum.com/u/supasta)\
**Post date:** [July 19, 2006, 5:52pm UTC](https://www.ruby-forum.com/t/model-validation-with-a-twist/98981/6 "2006-07-19T17:52:37Z")

</div>

Sorry to bug you again (I’m still learning. 🙂 ) 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?](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
