This post was originally published at https://jessesbyers.github.io./ on February 13, 2020, when I was a Software Engineering student at Flatiron School.
*MyDIY is a collaborative project planning and blogging app for Do-It-Yourself-ers, House-Flippers, and Property Managers. Each User can work on multiple projects and collaborate in different ways with other users. Users can create project goals, updates, resources, and post images as their goals are being completed. *
The requirements for this project were significant, and required managing complex relationships and nested forms and resources. It was fairly easy to build in the required associations, however, the main challenges I encountered were related to setting up and managing collaborations amongst users and managing different levels of privileges based on a user's role.
I created collaborative relationships in my project through a Collaboration model that served as a join table between the User model and the Project model.
The Collaborations table included project_id, user_id, and a role, and this allowed users to have multiple projects, and projects to have multiple users, with different roles for each project.
A User becomes a Primary Project Owner automatically when they create a Project. The Primary Project Owner can then add and edit Collaborations with other Users.
As I quickly learned, managing Collaborations is very complex. It involves managing access to individual pages through the controllers, and managing which buttons users can see on specific views.
In the models, I created scope methods that could be called throughout the application to identify and filter users based on their roles. For example, the following methods return a collection of projects in which a user has a certain role.
These methods can then be used in the controller to manage access to certain actions and views:
In the views, there were many instances in which I had to use logic in the views to display different options to different Users based on their role. For an example, a Primary Project Owner should have options to edit the project, add collaborators, and add goals on the Project show page, but a View Only collaborator should only be able to see links to view the project and goal details. This led to a lot of conditional statements that pulled in the model methods described above. Initially, a view would be very difficult to read because of all of the logic.
Once I got the views working as planned, I got to work on refactoring.
The task of refactoring seemed colossal. I started by moving chunks of repeated code into partials first, and then tackled more of the logic using helpers, sometimes in conjunction with the partials I had already created.
The most obvious first step in tackling the views was to address the forms. I created an errors partial to display error messages and a form partial for each model to render form content more cleanly and efficiently. I was also able to create partials for some common actions such as delete or edit buttons.
I then created helper methods to render HTML from Ruby code to address some of the conditional logic in the views. For example, on the Goals index page, there were three sections that included conditional logic for displaying the latest update, resource, and image for the goal. I was able to create helper methods which took care of some of the logic, and used them in a partial to render the details of each latest activity section:
I then called the partial into the view:
In the end, this created a clean display of latest updates in the browser, with all of the logic hidden from view.
Collaboration is complex! Looking back at the project, knowing what I know now, there are some changes I would make for my next attempt at collaboration.
Instead of using role names such as "Primary Project Owner", I would assign numerical values to each role. Then I would be able to use simple operators in the methods to control access (if @collaboration.role >3 ....)
I would explore using a gem such as Can Can Can to help manage user roles and privileges. While I learned a lot through creating the roles manually, I did spend the majority of my project time figuring out the ins and outs of access restrictions, testing, fixing bugs, and re-testing.
While refactoring code will often make it cleaner and easier to read, there is a point at which too much refactoring makes the code harder to understand. It is important to strike the right balance in order to write clean code that is also highly understandable by other programmers who might work on your project in the future.
Testing user roles and privileges is very time-consuming and needs to be done repeatedly to make sure there are no unintended consequences of changing the code to address a particular bug. This is a case in which Capybara Feature Tests could be very useful, and I would like to explore these tests more so I could implement them the next time I develop a project that relies on collaborative relationships.
All in all, I loved building the project and working through the challenge of incorporating user roles and collaborations. I look forward to using MyDIY for my own home projects, and hope you'll check it out too!