I have a model (named Order) with an underlying DB field that is boolean.
My migration file looks like this:
t.boolean :balloon, :null => false, :default => true
In my AR model I dutyfully have a line like this:
validates_inclusion_of :balloon, :in => [true, false]
In my test like the line below I expect an error, but I get none:
order.balloon = 'some text'
assert !order.valid?
I get no error because the value 'some text' is converted (via
type_cast) to false. Hence I get no error.
In general I find it confusing that my validation takes place after
the column_specific type cast. I would rather have a validation
message when my boolean field is set to "some arbitraty text" instead
of letting it be swallowed by some fall_back value in
ActiveRecord::ConnectionAdapters::Column::value_to_boolean.
In general I find it confusing and not clearly documented that
validations occur after the type_cast mechanism (is that true for
all types? also times/dates?)
Jarl
Yeah, it happens for all types otherwise you couldn't do something like this:
validates_inclusion_of :price, :in=>0..500
Form submission values are all strings and "30" wouldn't pass that
validation if it were done before typecasting. Similarly your
validation wouldn't work with check_box_tag.
--
Cheers
Koz
Thanks for replying.
Michael Koziarski <mic...@koziarski.com> writes:
>> In general I find it confusing that my validation takes place after
>> the column_specific type cast. I would rather have a validation
>> message when my boolean field is set to "some arbitraty text" instead
>> of letting it be swallowed by some fall_back value in
>> ActiveRecord::ConnectionAdapters::Column::value_to_boolean.
>>
>> In general I find it confusing and not clearly documented that
>> validations occur after the type_cast mechanism (is that true for
>> all types? also times/dates?)
>
> Yeah, it happens for all types otherwise you couldn't do something like this:
>
> validates_inclusion_of :price, :in=>0..500
I see. However it is unclear which validates_* macros that validates the raw
value and which that validates the type_casted value.
> Form submission values are all strings and "30" wouldn't pass that
> validation if it were done before typecasting. Similarly your
> validation wouldn't work with check_box_tag.
Well, this is exactly my problem, the current implementation does not
work with the suggested validation for booleans:
The current suggested way to validate booleans is
validates_inclusion_of :balloon, :in => [true, false]
But that does NOT work, providing "something else" as parameter to
:balloon passes the validation, becauase it is converted to false
before the validates_inclusion_of
So for all boolean fields I must write sometning like this:
validates_each(:balloon) do |record,attr_name,value|
raw_value = record.send("#{attr_name}_before_type_cast")
unless ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.union(ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES).member? raw_value
record.errors.add(attr_name, :inclusion, :default => "is not a boolean value", :value => raw_value)
end
end
I am quite surprised that I am the only/first one having this problem,
boolean values can't be that rare. I didn't do anything special, just
introduced a boolean field, and added some tests like (using shoulda):
should_allow_values_for :balloon, true, false
should_not_allow_values_for :balloon, nil, '', 'any ohter value'
And then the problem showed up.
Jarl
> Jarl,
> I'm wondering in what context you end up with user input that outputs
> a boolean?
>
> Is it from a web form,
> or XML,
> or JSON
> or console app?
Integration with an external system making REST calls to our app.
> My feeling is that you're only going to end up with a true/false if
> you are calling from a method inside your application,
> eg.
>
> something.make_order(params, :balloon => true)
>
> in which case "validation" may be the wrong use case.
Yes, but as in most other rails application, the input comes from a
http POST or PUT method, hence the input is unreliable and validation
is wanted.
> If you're trying to guard against your own input
> then perhaps what you want to invent something like
>
> guard_field :balloon, :values => [true, false]
I am trying to inform what-ever-have provided-the-input that the value
is not acceptable.
Here is an example A generic web-form, where all input fields are of
type text (in the HTML form), but of course some fields (like
:balloon) are typed, and therefore only certain values are
accepted. The value "sand" (which means 'true' in danish) is not an
acceptable boolean value, hence I would like a validation
message. Currently this would silently be converted to false, which
semantically means the complete opposite of what the user expected.
Jarl