Reputation: 4169
I'm having an issue with a date format. I have a time picker that has the date in a funky format (well, it's a nice format, actually, but not to the computer). I'm trying to have Chronic
parse the date so that it can be saved properly.
At first, I was doing, in the create action of my controller:
params[:event][:start] = Chronic.parse(params[:event][:start])
but if and when validation fails, it sends the parsed value back to the view, and my datetimepicker is all botched, then.
So, I thought... callback? In my model, I added:
private
def date_change
self.start = Chronic.parse(self.start)
end
I tried before_save
, before_validation
, after_validation
... but nothing seems to get that date formatted correctly.
As it stands, I keep getting ArgumentError in EventsController#create - Argument out of range
. I assume that's because the database is expecting a properly formatted datetime object.
Any idea on how I can accomplish my goal, here, of not changing the params
, but still being able to save a properly formatted object?
Upvotes: 2
Views: 423
Reputation: 50057
Assumption is the mother of all mess ups :)
Argument out of range
error.Most cases bugs are so hard to find/fix because we assume we know the error, but we are looking at the error in the wrong way.
Easy ways to test which attribute causes the error:
open rails console
, build an object with the parameters, save it, and ask the errors. Something like
e = Event.new(params[:event]) # copy params verbatim from your logfile e.save e.errors
and that will display which field causes the error.
Alternatively: use pry and add a line binding.pry
just after the save, so you inspect the errors
(more info)
I see two options to do what you want:
after_validation
callback, if you are sure the data will always be correct, and correctly parsed by Chronic
. This way if validation is passed, then convert the field and normally nothing can go wrong anymore, and the value is never sent to the browser again. Note: if some other attribute is causing the error, this callback is never hit, of course. Because it does not pass the validation.
start_str
, which is a visual representation of your start, and
before_save
convert it to start
. It does not really matter that much here, because if validation fails, you just show start_str
and not the "real" start
field.Upvotes: 0
Reputation: 434585
I'm guessing that the problem is occurring the the start=
mutator method that ActiveRecord supplies. If you're doing things like this in your controller:
@event.update_attributes(params[:events])
@event = Event.create(params[:event])
#...
then create
and update_attributes
should call start=
internally. That should allow you to put the Chronic stuff in your own start=
:
def start=(t)
super(Chronic.parse(t))
end
You might need to adjust that for non-String t
s, I'm not sure what Chronic.parse(Time.now)
, for example, would do. You could also call write_attribute(:start, Chronic.parse(t))
or self[:start] = Chronic.parse(t)
if you didn't want to punt to super
.
Note that before_validation
and similar handlers will be called too late to bypass whatever default string-to-timestamp conversion ActiveRecord is doing but a mutator override should happen at the right time.
Alternatively, you could parse the time in the controller with something like this:
event = params[:events].dup
events[:start] = Chronic.parse(events[:start])
@event = Event.create(event)
Upvotes: 2