The allow_nils are just to prevent the reporting of the same error (say,
a missing zipcode) multiple ways. The problem is that, when the user
enters data into the form:
/auth-1.9 821 > script/about
About your application's environment
Ruby version 1.9.1 (powerpc-darwin9.8.0)
RubyGems version 1.3.5
Rack version 1.0
Rails version 2.3.4
Active Record version 2.3.4
Active Resource version 2.3.4
Action Mailer version 2.3.4
Active Support version 2.3.4
Application root /Users/rick/auth-1.9
Environment development
Database adapter postgresql
Database schema version 20091111184210
Sure the code you've got matches what you've reported?
Yup. I did some googling around, and other people have apparently had
problems validating the length of numbers with validates_length_of as
well. No one seems to have filed a bug report about it, though.
I ended up replacing the validations with validates_format_of, and
that's working for now. I'll file a bug report in the next day or so,
since validates_length_of should either "just work," or be documented as
being only for strings.
> Sure the code you've got matches what you've reported?
Yup. I did some googling around, and other people have apparently had
problems validating the length of numbers with validates_length_of as
well. No one seems to have filed a bug report about it, though.
I ended up replacing the validations with validates_format_of, and
that's working for now. I'll file a bug report in the next day or so,
since validates_length_of should either "just work," or be documented as
being only for strings.
The length of a number isn't really a well defined concept (to me at
least), For example is 1236548 a number of length 7 (because it has 7
decimal digits, of length 6 because in hex it requires 6 digits or 3
because its binary representation has 3 bytes? If what you really want
is a range of numbers then validates_numericality_of has options like
greater_than and greater_than
I just want it to validate the number of digits. Since it won't do that,
I used a regex instead. It's still undocumented behavior, though, which
makes it a bug.
No, it wouldn't. How would that validate that a number is exactly 5
digits long? If someone puts in only 4, then it's still an error.
The only way to do it currently, as far as I can tell, is with a regex
like so:
validates_format_of :zipcode, :with => /\d{5}/
validates_length_of is clearly broken (or improperly documented) in
2.3.4. Luckily, there's more than one way to achieve the same validation
in this particular instance.
I think that is the problem, as noted earlier validates_length_of does
not really make much sense for an integer. How many digits in 0123
for example? It has four digits but will be stored in the db as 123
so will no longer have four digits when you read it back. Use
validates_numericality_of, specifying a range, to check it is an
integer in the required range. Whether integer is the right field
type for zipcode is another matter. If you needed to go international
you would find that often they are not numbers.
So, based on the what all (particularly Fred) have said...
1) The easiest fix is to treat the zip code and plus4 as strings.
After all, do you plan on using them as arithmetic values? This has
the unfortunate byproduct of allowing "00000" "0000" as valid fields.
2) If you do continue with the fields as integers, you could validate
with:
Both cases have the unfortunate result of allowing non-zips and non-
plus4s (in terms of USPS rules) into your database. I think you would
be better off to extend your model to acquire the street address from
the user and ask USPS for the valid zip and plus4.
I think that is the problem, as noted earlier validates_length_of does
not really make much sense for an integer. How many digits in 0123
for example? It has four digits but will be stored in the db as 123
so will no longer have four digits when you read it back. Use
validates_numericality_of, specifying a range, to check it is an
integer in the required range. Whether integer is the right field
type for zipcode is another matter. If you needed to go international
you would find that often they are not numbers.
Colin
Depending on how you get the integer out of that string, it might be considered octal.
"0123".to_i
=> 123
Integer("0123")
=> 83
As Colin points out, a "ZIP code" is not a number (and a "postal code" even less so) as the leading zeros and the USA's ZIP+4 extension. If you stop thinking about the field as a number just because it is frequently made up of digits, you'll likely have an easier time of it.
Validates_length_of is checking the .size property of the field; if
it's already been converted to an integer, that returns the size (in
bytes) not the number of digits.
The above validates_format_of isn't going to work on a repeat save, as
a zipcode of '01234' will be saved to the DB as 1234, and (unless
you're doing formatting not shown here) cause the validation to fail.
I tend to agree - zip codes aren't really numbers, even if they look
like them. And if you implement them as strings, you won't even break
a sweat when your boss comes in and says, "now we need to support
CANADA!"...