开发者

Associated model validations in Rails - and some questions of design

开发者 https://www.devze.com 2023-02-02 21:09 出处:网络
I\'ve got a Task which has many LoggedTimes. I want to put a limit of 8 hours of logged time against a task on any given day. What is the best way to go about this so that I can check whether the pers

I've got a Task which has many LoggedTimes. I want to put a limit of 8 hours of logged time against a task on any given day. What is the best way to go about this so that I can check whether the person's latest logged time "takes their total over the limit" for a day, so to speak?

Here's where I am beginning (Task.rb):

validate :max_logged_daily_time

def max_logged_daily_time
    if (params[:session_time] + (logged_times.where(:created_at => Date.today).to_a.sum(&:session_time)/60)) > 8
      errors.add_to_base("Can't have more than 8 hours logged a day") 
      logged_time.errors.add('session_time', 'Logged times exceeded today')
    end
  end

Currently this validation is not working (adding another LoggedTime once 8 hours of previous logged times have been registered simply adds it to the rest, instead of throwing an error. Since no errors are being thrown, I'm struggling to pick my way through the problem. Is it something to do with the handling of params?

Which brings me to the question of design: in theory I could revise the view so that the user is only able to submit 8 hours minus the total amount of time they've l开发者_高级运维ogged that day; however this seems like a clunky solution and against the principle of keeping validations in the model. (And it certainly doesn't help me solve this model validations problem).

Any advice here?

TIA


class Task < ActiveRecord::Base
  has_many :logged_times

  def hours_today
    LoggedTime.daily_hours_by_task(self).to_a.sum(&:session_time)
  end

end

class LoggedTime < ActiveRecord::Base
  belongs_to :task

  scope :daily_hours_by_task, lambda { |task| task.\
    logged_times.\
    where('logged_times.created_at >= ? AND logged_times.created_at < ?',
          Date.today, Date.today + 1) }

  validate :max_logged_daily_time

  private

  def max_logged_daily_time
    if task && ((task.hours_today + session_time) / 60.0) > 8
      errors.add('session_time', 'Logged times exceeded today')
    end
  end

end

Some notes:

  • created_at is a DateTime, so you'll need to test both the start and end of the day

  • The validation also prevents the addition of a single LoggedTime which by itself exceeds the maximum.

  • Dividing by an integer truncates and will give the wrong result -- add the .0 to convert to a float.

  • This only validates the LoggedTime, not the Task, so you might want to add validates_associated in the Task model.

  • Validation is bypassed when Task is nil

EDIT

Well, hours_today should really be called minutes_today, but you get the idea.


I'd create a separate method for getting the sum of hours for a given day.

def total_hrs_logged_for_date(date)
  #some code
end

Test that method to make sure it works.

Possibly also do the same for calculating the current logged time.

then use those those two in your custom validator

so this line

if (params[:session_time] + (logged_times.where(:created_at => Date.today).to_a.sum(&:session_time)/60)) > 8

becomes

if total_hrs_logged_for_date(Date.today) + current_time_being_logged > 8

At the very least it will help you narrow down which of these isn't working.

I also notice that you have "params[:session_time]"

I think this is in Task.rb which sounds like a model. Probably what you want is just "session_time" instead.


I ended up revising Zetetic's response slightly, because I couldn't get it to work as is.

In the end, this worked:

class Task < ActiveRecord::Base
  has_many :logged_times
  validates_associated :logged_times

  def minutes_today
    logged_times.where('created_at >= ? AND created_at < ?', Date.today, Date.today + 1)
  end

end

and the LoggedTime model:

class LoggedTime < ActiveRecord::Base
  belongs_to :task

  validate :max_logged_daily_time

  private

  def max_logged_daily_time
    if task && ((task.minutes_today + session_time) / 60.0) > 8
      errors.add('session_time', 'Logged times exceeded today')
    end
  end

end

I'm not sure why the scope method baulked, but it did. Any hints Zetetic?

0

精彩评论

暂无评论...
验证码 换一张
取 消