views:

460

answers:

4

Hello I am trying to validate a dollar amount using a regex: ^[0-9]+\.[0-9]{2}$

This works fine, but whenever a user submits the form and the dollar amount ends in 0(zero), ruby(or rails?) chops the 0 off. So 500.00 turns into 500.0 thus failing the regex validation.

Is there any way to make ruby/rails keep the format entered by the user, regardless of trailing zeros?

+2  A: 

In general if you wish to “remember” the decimal precision of a floating point value, you should use a decimal type, not a binary float.

On the other hand, I'm not certain why you would wish to force the string representation in such a strict manner… How about accepting any number and formatting it with e.g. number_to_currency?

Arkku
The rigidity was requested by the CFO of the accounting department this app is for. He actually wants the people to type exactly in this format into the form. `@payment.amount = number_to_currency(@payment.amount, :unit => '')` before `@payment.save` (via controller) is producing the same results ... am I doing it wrong?
Chris Allison
I'm afraid you are... number_to_currency is meant for presentational purposes, i.e. to use when outputting the amount in the view. BTW, if the rules are so rigid you might consider doing some client-side validation, I'm sure there's a plenty of jQuery and prototype based solutions for this.
Mladen Jablanović
+2  A: 

I presume your dollar amount is of decimal type. So, any value user enters in the field is being cast from string to appropriate type before saving to the database. Validation applies to the values already converted to numeric types, so regex is not really a suitable validation filter in your case.

You have couple of possibilities to solve this, though:

  1. Use validates_numericality_of. That way you leave the conversion completely to Rails, and just check whether the amount is within a given range.
  2. Use validate_each method and code your validation logic yourself (e.g. check whether the value has more than 2 decimal digits).
  3. Validate the attribute before it's been typecasted:

This is especially useful in validation situations where the user might supply a string for an integer field and you want to display the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn‘t what you want.

So, in your case, you should be able to use:

validates_format_of :amount_before_type_cast, :with => /^[0-9]+\.[0-9]{2}$/, :message => "must contain dollars and cents, seperated by a period"

Note, however, that users might find it tedious to follow your rigid entry rules (I would really prefer being able to type 500 instead 500.00, for example), and that in some locales period is not a decimal separator (if you ever plan to internationalize your app).

Mladen Jablanović
Would ...`:amount_before_type_cast`... still result in the insertion of "500.0" into the DB? Passing validation is one thing, but the managers could still pull up the payment record and they would read "500.0" despite the users correct input? See reply to Arkku, the rigidity/strictness is by request.
Chris Allison
You (and especially the managers) shouldn't be concerned with how a value is stored in the database. It is a number, it should be stored as a number (however, there are numerous number formats in different databases). Number of decimals should be specified only when formatting output, i.e. in your database view.
Mladen Jablanović
Oh, and, please, never _ever_ consider changing that DB column type to string.
Mladen Jablanović
Thanks. I really was starting to think about changing the column to string lol ... Ok, so I'm just going to validate using `before_type_cast` then I'll format the view using `number_to_currency` - I know the managers don't care whats behind the DB, but I wanted to save the extra formatting step by just storing the number pre-formatted for the view. Thanks for your help and guidance!
Chris Allison
A: 

Could you allow 500, 500.0 and 500.00 with ^[0-9]+(.[0-9]{1,2})?$ ?

Mr. Ronald
Yes, but see my reply to Arkku. 500.00 is the desired form of input.
Chris Allison
A: 

Usually with money it's best to store it as an integer in cents (500 cents is $5.00). I use the Money gem to handle this.

guitsaru