Laziness and Stupidity; or, Model Validation in Rails
I thought my post on how to customize the ActiveRecord error header messages was a one-off about how we, as web developers, should treat our users as humans. But it looks like this is turning into an occasional series.
Today’s Victim
I’m an avid fan of Consumer Reports. I’ve subscribed for about as long as I can remember. Subscribers get to take part in their annual survey, which they mainly use for their car ratings. I kind of hated in the past, since their survey site was so lame and hard to use. I think it might have been an old-school perl-driven cgi script, if I remember correctly.
But this year, I was pleasantly surprised that it had been given a nice facelift. From the url I can see that they’re now using something more modern (ASP.NET).
But then, about four questions in, I got this:

Don’t use commas? Are you kidding me?
This is the kind of crap that permeates the web and is just sheer stupidity.
Would it really be so hard for the .NET app to remove commas from whatever value I enter? (No, it wouldn’t be, it’s actually pretty easy… but I digress). My guess is, the database column is an integer column, and if there’s a comma in the number, the insert statement will fail. So instead of writing code to take commas out, they tell the user to make sure they only enter integer numbers.
This is amazing. Shame on CR for letting their developers get away with this.
Since I want to help CR by taking the survey, I made sure not to use commas and entered “5000” and clicked Next.
It Gets Even Worse
But curiosity got the better of me, and so I clicked “Previous” to go back. Then I entered “5,000” with the comma, just to see what would happen. I began to think, maybe the bark is worse than the bite? Surely, the asp.net code really could deal with commas in numbers, right?
Nope:

Look at that awful hideous generic error message. Listen to the tone: you did something wrong, you idiot! Now, fix it!
We’ve already gone over how to customize these kinds of error messages in Rails. But even better is to avoid this situation in the first place.
If this kind of thing is hard to do in your favorite framework, then switch to Rails. Because it’s really easy to do in Rails.
Your Mission, Should You Choose To Accept It
I was planning on doing a little “Validation 101” here to demonstrate how your Rails applications can elegantly clean up user input before it gets to the database.
But I thought it might be more fun this time around to let you do the talking. So here’s my challenge to everyone reading this blog:
- Leave a comment to this post, giving the most elegant solution you can think of to remove commas from a text field in Rails.
- You can assume that there is an ActiveRecord class named
Vehiclethat has an attribute namedmiles. - IMPORTANT: Be sure to put your code inside of a big <pre> tag.
Bring it on!



Why do I need to put my code in a PRE tag? Surely the comment editor knows I’m going to be typing in code!
Sorry, couldn’t resist
But, this is pretty lame for a challenge.
Change #1 to be, “the most elegant solution to parse HUMAN numbers” and it’ll be a little better.
Human numbers being the stuff people normally enter, like “15,000” or “15,000KM” or “15,000 miles”
class Vehicle < ActiveRecord::Base before_save :normalize_miles end
In the Vehicle model:
def miles=(inputted_miles) write_attribute(:miles, String(inputted_miles).delete("^0-9")) endThis takes the user-given value and strips all non-digits (commas and units) from it. Another method could be used to format the value when retrieved (def miles … end)
String.gsub is great but String.delete is also helpful here, and perhaps more readable.
For any of the gsub examples above, you could use delete instead, like this:miles = miles.delete(',')will work.Chris Lloyd is closest to my preferred solution, but I normalize prior to the validation, rather than the save.
class Vehicle < ActiveRecord::Base before_validation :normalize_miles validates_format_of :miles, :with => /\d*/ private def normalize_miles self.miles = miles.delete("^0-9") end endI’m too tired to type actual code, but i would check if the data is ONLY numbers and commas. If so, I would strip the commas out, make an int, and be happy. If not, i would alert the user that he entered something wrong.
If you strip out decimals, then 1005.5 becomes 10055. Arron had it right by stripping out the comma but then doing to_i to get rid of the decimal. However, the code should probably go in the model instead of the controller.
if you’re in JRuby, NumberFormat offers a nice locale sensitive solutions (where commas and dots have the appropriate meaning in places where the comma is the decimal point equivalent).
Of course, it’s far from elegant!!!try { NumberFormat nf = NumberFormat.getNumberInstance(); Number n = nf.parse(miles); //note that getting that miles string isn't as easy as it looks } catch (ParseException p) { //this bit might get tricky }I think, the very first thing should be to give a kick within the client side UI itself, why to put the “MUST” extra work on the server. More or less whatever mentioned above is a tweak or FIX we are doing for the value that user has entered without letting the user know that there might be some tweaking going to happen with the data he entered. Of course there should be such a code in the model but things should also be handled within the browser using JS.
In my rails applications I always handle such things with a simple function by capturing the onKeyUp event on the text field. And the functionality should also work on everything apart from 0-9 if we are considering the integer, not only the commas but any other character but numeral.
So, the best here, and what I do is two things…
Client Side JS code
specifying in the text_field or text_field_tag options as adding the JS functionfunction check_integer(el) { if(String(parseInt(el.value))=="NaN") el.value = ''; else el.value = parseInt(el.value); }This JS function will automatically remove any character other than 0-9 and will left a valid numeric value in the text field while the user is typing in the box.
Server side Ruby code
definitely I will go with the more generic “gsub” to swipe out anything other than 0-9
Stupidity is the property a person, action or belief instantiates by virtue of having or being indicative of low intelligence or poor learning abilities.
I came into a problem like this in a recent project. There was a model called Sale which had a price, represented by a number without decimals and using dots instead of commans for hundred separation (used in Spain). The model looked like this:
class Sale < ActiveRecord::Base before_validation :remove_price_dots validates_presence_of :price validates_numericality_of :price def remove_price_dots self.price = price.delete(".") # I don't want to stripe out all non-number characters with grep(/[^\d]/, '') because # the user might be typing 'I walked 5 miles from home' and it would be valid end def price price.to_i.to_s.reverse.split('').in_groups_of(3,false).collect{|g| g.to_s}.join('.').reverse # for an amount of 50000 it will show 50.000, this will be used in update forms as well # It might exists another simpler solution for this end endI wrote a plugin to accomplish this same task for currency fields, where an amount needs to be entered in dollars. Users will often use a ’$’ to enter the number. The code can be found here:
http://github.com/faithfulgeek/acts_as_currency/tree
Just a complement on this post Jeff…I get so disappointed when I run into user interfaces like the horrid one you showed us.
As is shown in the writings of Galileo, our ideas stand in need to general logic.