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