I have an Event model with a finish_time field and a form checkbox called whenever. When whenever is checked I want to set finish_time to nil regardless of its value in parameters, when whenever is not checked I want to make sure that finish_time is a valid date before I save it in the database.
Currently I am achieving this using a whenever attr_accessor on Event:
params[:event][:finish_time] = nil if whenever = params[:event].delete(:whenever)
@event = Event.new(params[:event])
@event.whenever = whenever
And using conditional validations to check for finish_time if whenever is false
validates_presence_of :finish_time, :unless => @whenever
I am not happy with the duplication this creates. The only way that finish_time can be nil is if whenever is true, and if you change one you often have to change the other. If the Event is updated with a new finish_time then whenever will have to be changed to false as well.
Ideally I would like to move the validation to my controller. That way when doing direct model access whenever can be easily indicated by setting finish_time to null, but users posting to /events will get yelled at if they haven’t indicated a choice. However I can’t figure out a way to conditionally add or remove validations to Event from the controller or even if this is the best approach.
What is the best way to remove this duplication?
Instead of trying to solve problem via validation, you might try to address it in “a Ruby way”.
In addition to validation, you might want to hook into mutators of
finish_timeandwhenevermethods.Then you can just try (however, I’m not sure whether it works):
Edit: However, this will make the resultant fields depend on the order assignemnts happen, so the results for the same parameter set may differ if the order of assignments changes. That’s unlikely what you want. So you must tune your opertators the way you want.
And you also should understand that you won’t be able to make your code DRY unless you make your requirements DRY.
Edit #2: I think you should consider dropping the requirement for
finish_timeto benilifwheneveris set to true. You can then modify accessors in the same way as described above.