I'm building a Rails 4.2+ application that has Workflows and Tasks. A task.due_date can either be a specified date (such as "01/01/2017") or it can be relative to a date on the parent workflow record.
class Workflow < ApplicationRecord
# closing_date:date
after_save :update_relative_tasks
has_many :tasks
# Production code would make sure closing_date changed before updating associations
def update_relative_tasks
tasks.where(has_relative_date: true).each do |task|
task.update_relative_due_date(closing_date)
end
end
end
class Task < ApplicationRecord
# due_date:date
# has_relative_date:boolean (default: false)
# number:integer (default: 0)
# days_or_weeks:string (default: "days")
# before_or_after:string (default: "after")
# workflow:references
belongs_to :workflow
def number_of_days
@number_of_days ||= if days_or_weeks == "weeks"
number * 7 # relative date is in "weeks"
else
number # assume relative date is in "days"
end
end
# Pseudo-ish code, not tested, but shows concept
def update_relative_due_date(date)
new_due_date = if before_or_after == "before"
date - number_of_days.days
else
date + number_of_days.days
end
update_attribute(:due_date, new_due_date)
end
end
Assigning a specific date on the task is simple, I just use a calendar widget and it sets the date, no problem.
The tricky part is assigning a relative task.due_date based off of the workflow.closing_date.
For example, let's say the workflow.closing_date is 01/01/2017 and I want to have a task due 5 days before (or after) that date.
I can add some form fields that do something like, where the brackets represent input fields:
Due Date: [x] number of [days|weeks] [before|after] workflow.closing_date
and then I can parse that out in Ruby code and update the task with the proper due_date.
However, if the workflow.closing_date changes, I then would need to go find all the associated tasks and re-calculate their relative dates. Also, if the workflow.closing_date wasn't known initially and it was added later on, it would again need to re-calculate the due dates for all associated tasks.
Eventually I'm also going to add reminders to tasks, and those also will be relative to the task.due_date. Very similar to how Google Calendar's notifications work.
How is something like this solved? I know I'll need to add a background processor to handle sending reminders. But I'm uncertain how to solve the issue of setting a task.due_date relative to some date on another record.
I may be missing the difficulty in this issue so will try to talk through it. Could you set an after_save filter on workflow to handle any changes to related object if the workflow is saved?
workflow.rb
This way the tasks closing dates are always relative to the workflows closing_date and you don't have to worry about complicated update logic. Maybe I missed something above..