confusing behaviour of validates_inclussion_of

Hi all wonderfull core-developers. Rails is an amazing framework.

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

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

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.

Hi Michael.

Thanks for replying.

Michael Koziarski <michael@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?

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.

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]

where saying order.balloon = "purple" would instead raise an ArgumentError

"matthewrudyjacobs@gmail.com" <matthewrudyjacobs@gmail.com> writes:

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

Jarl, I've been listening in on this conversation and it brings to mind a discussion a while back on the use of presenters and reverse presenters. Since the dawn of time, ActiveRecord (and perhaps even ActiveModel) has been tightly wedded to the concept of unreliable human input (= HTML forms). Gradually there has been some decoupling, but nothing comprehensive. The idea of a "Presenter/Reverse Presenter" is needed to insulate the persisted (and canonical) model from various presentation/view layer technologies -you can perhaps find some more background by googling those terms.

Consider using something other than validations -matthewrudyjacobs "guard" macro seems like a reasonable step. You might still need to disable validations in various and sundry circumstances -bummer.

-Chris