---
title: ruby on fails
teaser:
tags: web,rails
author: Jared Carroll
published_on: 2007-05-29
---

So recently, I had a Rails app that raised the following exception:
`ActiveRecord::MultiparameterAssignmentErrors`.  This was raised in
ActiveRecord::Base#initialize.  It was caused by a model that had a date
attribute.  I was using ActionView::Helpers::DateHelper#date_select to get the
value for a `Job` model's `expires_on` attribute.  Apparently a user had
selected an invalid date, e.g. June 31, 2007.

`ActiveRecord::Base#initialize` basically executes the following code:

    Date.new 2007, 6, 31

Now, Ruby's `Date` class will raise an `ArgumentError` which Rails puts in an
`ActiveRecord::AttributeAssignmentError` object that is available via an array
from `ActiveRecord::MultiparameterAssignmentErrors#errors`.

So I thought, How can I handle this nicely?

How about overriding the setter in my model?

```ruby
class Job < ActiveRecord::Base

  def expires_on=(expires_on_date) # expires_on_date is a Ruby Date object already
  end

end
```

Nope.  Rails is going to pass a Ruby `Date` object to `Job#expires_on=`.  The
ArgumentError exception would have already occurred.

How about overriding `ActiveRecord::Base#initialize` on `Job`?

```ruby
class Job < ActiveRecord::Base

  def initialize(attributes)
    unless valid_date?(attributes['expires_on(1i)'],
                       attributes['expires_on(2i)'],
                       attributes['expires_on(3i)'])
      errors.add :expires_on, 'is invalid'
      attributes.delete 'expires_on(1i)'
      attributes.delete 'expires_on(2i)'
      attributes.delete 'expires_on(3i)'
    end
    super
  end

  def valid_date?(year, month, date)
    Date.new year, month, date
  rescue ArgumentError
    nil
  end

end
```

Nope.  Too bad adding errors to `ActiveRecord::Base#errors` outside any of the
validation callbacks, i.e. `#validate`, `#validate_on_create`,
`#validate_on_update` doesn't prevent the object from being saved.  In other
words, `ActiveRecord::Base#errors` is cleared before calling `#validate`.  I'm
glad of this behavior, because the code above sucks anyway.  Overriding
`ActiveRecord::Base#initialize` is crazy.

What if we just set `expires_on` to nil?  Then if we don't
`#validate_presence_of :expires_on`, the object will be saved with a null
`expires_on` date, even though the user thought they just entered an invalid
date.

So if I can't put it in the model nicely, I'll have to rescue
`ActiveRecord::MultiparameterAssignmentErrors` in the action in the controller,
like so:

```ruby
class JobsController < ApplicationController

  def create
    @job = Job.new params[:job]
    # save the job
  rescue ActiveRecord::MultiparameterAssignmentErrors
    @job.errors.add :expires_on, 'is invalid'
    render :action => :new
  end

end
```

Nope.  Since `ActiveRecord::MultiparameterAssignmentErrors` is raised in
`ActiveRecord::Base#initialize`, the job object is never created.  Therefore,
the job instance variable is nil and this code will raise a `NoMethodError`
because `NilClass` doesn't understand #errors.

How about:

```ruby
class JobsController < ApplicationController

  def create
    @job = Job.new params[:job]
    # save the job
  rescue ActiveRecord::MultiparameterAssignmentErrors
    flash[:notice] = 'Expiration date is invalid'
    render :action => :new
  end

end
```

Nope. Since a job object is never created, when rendering `JobsController#new`
you'll get a bunch of errors because the job instance variable is nil.

So we ended up replacing the year, month and day dropdowns created by
`ActionView::Helpers::DateHelper#date_select` with a Javascript calendar.
