codeinspired
codeinspired

Reputation: 366

Need help writing a method to calculate duration (time) for Model/Views?

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:

  1. What's wrong with my current code?

  2. Is there a better way to approach the problem to accomplish my objective?

  3. 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

Answers (1)

Justin
Justin

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

Related Questions