0

I have a Sinatra app with a Project model and a Task model, and a project has_many tasks.

It's a composition type of relationship, meaning a project cannot exist without tasks associated.

I create a project on /projects/new, then redirect to /projects/:id/tasks/new to add tasks to the project. On the latter page, I:

  1. instantiate the newly-created project instance from the param in the URL
  2. create a number of tasks
  3. validate all the tasks
  4. add them to the project.

The problem is, if one gets interrupted during any of the step above, then no tasks will be added and the project will be saved in the database with no tasks associated. This will result in errors when I calculate total task duration, and in other situations.

I could instantiate and save both records on the same page, and that would solve the problem.

But, is there a way to split Project and Task creation across dedicated URLs without this resulting in childless projects?

fullstackplus
  • 1,061
  • 3
  • 17
  • 31
  • 1
    *"This will result in errors when I calculate total task duration, and in other situations."* Why? Seems like this should be simple to handle and that a new "Project" could in theory have no tasks associated with it but that is really up to you. If this is truly a requirement I would recommend that your interface require at least one task during the creation of a Project. – engineersmnky Mar 09 '23 at 19:25
  • 1
    You need to do whatever you're doing inside a transaction, so it's either 100% complete or rolled back. With or without Rails, you still need to define transactions if you have non-atomic sequences of events. – Todd A. Jacobs Mar 09 '23 at 23:28
  • Thanks for your comments. The reason I don't want Projects without Tasks is that in my domain, a Project is basically just a datetime; it's an entity that is weakly defined by its own properties and strongly defined by its Tasks' properties. In other words, a Project sans Tasks doesn't make any sense on its own. I'll definitely have to look into transactions more; Sequel (the ORM I'm using) supports them as well. – fullstackplus Mar 10 '23 at 08:59

1 Answers1

1

I would advise you to not create the Project until at least one Task has been created. You can do this by saving the project details in session and then actually creating the Project from the tasks controller, for example (pseudocode):

# projects controller
def create
  session[:new_project] = <extract session params to PORO>
  redirect_to new_tasks_url
end

# tasks controller
def new
  @new_project_params = session[:new_project]
  # use this in the view as needed
end

def create
  Project.transaction do
    project = Project.create(session[:new_project])
    project.tasks.create(task_params)
  end
end

You can see how to use session in Sinatra here: https://sinatrarb.com/faq.html#sessions

Another option is to change your code to allow Projects with no Tasks. My hunch tells me this is what users expect; if they create a project and then don't have time to create any tasks; they would maybe assume that the project would be actually saved.

This will result in errors when I calculate total task duration, and in other situations.

It seems like this particular problem could be resolved by just defaulting total task duration to 0. But then again, I don't know the actual purpose of your application here, so maybe I'm wrong.

max pleaner
  • 26,189
  • 9
  • 66
  • 118
  • Thank you. As posted in a comment to the question, in my domain Projects don't really mean much on their own and so a Project without associated Tasks is basically just a datetime (the only Project attribute that's required). But you and the other commenters have a point: maybe I should allow the user to create a Project and then fill it out with Tasks later, so that they won't have to start over again. – fullstackplus Mar 10 '23 at 09:04