Reputation: 366
My Rails 4 app has an appointments table, which has the following attributes/columns: "date" (datatype date), "starts_at" (datatype time), and "ends_at" (datatype time). I do not have a "duration" column, but want to calculate the difference between "ends-at" and "starts-at" times. I want the duration so I can put a validation in the Appointment model to prevent users from making appointments for less than 30 minutes or for more than 2 hours. I used Google to research the issue, but didn't find anything particularly helpful for what I'm trying to do. I'm learning Rails, and trying to pick up best practices. I'm assuming the place to put the "duration" logic is in the model, as opposed to the views. Right now, I don't believe users will need to see duration data. I started writing a "duration" method for the model, but can't get the logic to work properly. Here's what I've done so far:
protected
def duration
(self.ends_at - self.starts_at).to_i
end
I call the method with:
validate :duration, numericality: {greater_than: 30, less_than: 120}
When I execute the code it doesn't generate any errors, but the validation does not appear to do anything. Users are still able to make appointments for less than 30 minutes or for more than 2 hours. I need to prevent both scenarios from being stored in the DB. I played around with some if and else statements in the method, but no success.
Questions:
What's wrong with my current code?
Is there a better way to approach the problem to accomplish my objective?
If I do decide later to present "duration" information to users via the views, how do I accomplish that via embedded Ruby code?
I appreciate any help and advice!
Upvotes: 0
Views: 261
Reputation: 4940
Here is the approach I would probably take:
The comments to the right were added just to explain a little of what was going on.
validate :duration
private
def duration
length = (ends_at - starts_at) / 60
return if length.between?(30, 120) # bail out of validation if length falls between the two integers
errors.add(:base, 'Your error message') # you can change this to something like `:ends_at` if it's preferable..
end
More information on comparables in Ruby
If you want to present the duration later on, I would most likely go the route of using a presenter. But you could also just create an instance method on that model.
# Public: get the duration of the appointment in minutes
#
def duration
(ends_at - starts_at) / 60
end
if this is the case, then you can do a little refactoring to the valiation
validate :correct_duration
private
def correct_duration
length = self.duration
return if length.between?(30, 120) # bail out of validation if length falls between the two integers
errors.add(:base, 'Your error message') # you can change this to something like `:ends_at` if it's preferable..
end
Hope this works for ya.
Upvotes: 1