I have trawled the API and spent two nights staring at this code and unfortunately while I sense I'm close to the solution, I can't quite get it to work. I have a model representing shifts, which among other attributes have a start time.
create_table "shifts", :force => true do |t|
t.datetime "start"
The user interacts with this as a simple time as the date is set elsewhere, so I have set up virtual accessors to get and set.
class Shift < ActiveRecord::Base
def start_time
start.strftime("%H:%M")
end
def start_time=(time)
date = start.midnight
self.start = add_string_time_to_date(date, time)
end
def add_string_time_to_date(date, string_time)
t = DateTime.strptime(string_time, "%H:%M")
DateTime.new(date.year, date.month, date.mday, t.hour, t.min)
end
end
This all works fine when manipulated through a standard controller with @shift.update_attributes(params[:shift])
provided one enters the time in the expected format.
I would like to validate the time format entered but its not as simple as using the method below as strptime()
raises an ArgumentError before the validation is reached.
validates :start_time, :format => { :with => /^([0-1][0-9]|[2][0-3]):([0-5][0-9])$|^/,
:message => "Invalid format." }
I can work round this by putting a conditional call in start_time()
such as the example below, but that doesn't really work satisfactorily for two reasons.
def start_time=(time)
if time =~ /^([0-1][0-9]|[2][0-3]):([0-5][0-9])$|^/
date = start.midnight
self.start = add_string_time_to_date(date, time)
else
errors.add :start, "format invalid."
nil
end
end
First ActiveRecord doesn't seem to know that the setter has failed. I deduce this from the fact update_attributes() in the controller returns as if all was well, even though it wasn't. Second, there is no feedback returned to the user, so the call to errors.add is probably out of context. Its also a bit ugly.
The desired functionality is that of a standard validation, so if it fails the user sees an error on the form just as they would with any other validation failure. So in short, how do I achieve this functionality, and am I approaching this in the right way?
Do I need to raise a specific exception in order to notify ActiveRecord of troubles, and if so where is best to do so? It is best just to rescue and use the flash?
You should better split the logic of these attributes.
This would look like:
class Shift < ActiveRecord::Base
attr_accessor :start_time
after_initialize :default_values
before_save :set_start
def add_string_time_to_date(date, string_time)
t = DateTime.strptime(string_time, "%H:%M")
DateTime.new(date.year, date.month, date.mday, t.hour, t.min)
end
private
def default_values
self.start_time = start.strftime("%H:%M") if start_time.nil? && start.present?
end
def set_start
if self.start_time.present?
date = start.midnight
self.start = add_string_time_to_date(date, time)
end
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With