As I was brainstorming what to write about the process of developing My Reading List, my Rails portfolio project, it became clear to me that my two biggest take-aways from this project were: the complex relationships I learned how to implement and the benefits of test driven development.
Takeaway 1: Learning more complex relationships
I knew that I wanted to implement some pretty complex relationships into my Rails application, an app called My Reading List. To explain these complex relationships, I’ll first explain a little about how the app should work from a user’s standpoint. The user can visit the home page and either create a new account or login to an existing account. Once they’ve logged in, they can view the app’s Book Library and add books from that list to their personal reading list. A user can also add comments to any book in the app library, whether it’s in their personal reading list or not. This was a tricky relationship to set up, as users can have many books (in their reading list) and can also have many books on which they’ve commented. This presented me with a bit of a problem. I needed to determine how to set up these relationships in a way that I could call two separate methods on users, once which would return all books in the users reading list and one which would return all books on which the user commented. Obviously the method to call each of these could not be user.books, so I could not simply say that a user ‘has_many :books’ and ‘has_many :books, through: :comments’ since these refer to different relationships. In researching different association options in the Rails guide, I determined that I could set the association name for one of these relationships as something other than ‘books’, and then include the class_name option. So I defined the association of users having many books in their reading list with ‘has_and_belongs_to_many :books’. Side note, this is also a new association I learned while developing this app - it’s just another way to define a many to many relationship, similar to ‘has_many :through’ but more simple, as you do not need to define a join model with ‘has_and_belongs_to_many’. It is best used when you don’t need to do anything with the relationship model (i.e. the relationship model will not have any submittable attributes of its own). Back to my complex user/books associations - I defined the association of users having many books on which they’ve commented as a ‘has_many :through’ relationship. However, instead of simply saying a user ‘has_many :books, through: :comments’, I defined the association as a user ‘has_many :books_commented, through: :comments, source: “book_commented”’. The source option refers to the way the association with books was defined in the comments model, i.e. a comment ‘belongs_to :book_commented, class_name: “Book”, foreign_key: “book_id”’. It took me a bit to wrap my head around, but it all started to make sense once I played around in the console, calling the different methods on a user, i.e. user.books to return all books in their reading list and user.books_commented to return all books on which they’ve commented.
Takeaway 2: The wonders of Test Driven Development
Prior to starting work on my Rails portfolio project, I hadn’t spent much time writing my own tests. I’ve had a lot of experience working with test driven development throughout the labs in the Flatiron program, but more in terms of running tests and writing code to pass them, rather than writing the tests my self. I decided at the start of this portfolio project, I wanted to take a shot at writing my own test suite. I think the biggest benefit I noticed, was the reduced manual QA time. When working on my Sinatra portfolio project, I spent hours trying to “break” my application - for ex., I would try every variation of feeding a form bad input, to test if the validations I set up were correct. However, in my Rails app, before defining the models and adding the various validations to them, I wrote tests for each model in relation to all the variations I could think of where bad data could be introduced to the model if a validation did not work as I expected (for ex., testing that when required fields are not filled out in the form, the form is re-rendered with error messages displayed, rather than a new object being created with that field being blank). It took me a while to write out these tests as they were rather detailed and I wanted to make sure I covered every possible scenario that could potentially introduce bad data into my database. There was even a point where I thought, is it even worth it to write all this out when I could just test all of this in the local server? However, once I finished writing the tests and then finished defining the models, I ran the tests on each model and was able to get them all passing pretty quickly. At that point I realized I had just saved myself a significant amount of time writing those tests. There’s no way I would have been able to manually test all the validations for all my models that quickly!
Another huge benefit I discovered to writing a test suite was how it helped me to break down the application and form a more targeted approach to development. It forced me to focus on one set of functionality, or feature, at a time, which I think in the long run helped me to develop the application more quickly and efficiently. When working on my Sinatra portfolio project, I kind of just decided what I would work on next from moment to moment, as I tested out different things in the local server. I might go from working on the users log in form to the books index view. There was no real method or thought put into my approach. Writing tests for my Rails app forced me to take a step back and think about what I wanted to implement next, and in a more logical manner. For example, after getting all my models working, I decided the first set of features I’d implement would relate the the books model. I decided the first thing I would focus on was the Book Library (i.e. the book index view). Before I starting writing code in the books controller or view, I wrote out my tests in regards to everything I expected to happen and to be displayed when a user visited the route ‘/books’. I realized that writing out the tests is almost like writing yourself a step by step guide for how you will go about developing each feature. For example, the first bit of my test for this feature was about how when a user visits ‘/books’, I would expect the current path to be ‘/books’ and for the page to have the content “Book Library”. So in the same, I knew that the first thing I needed to do was set up my route, define the index action in the books controller, and then add the content to the books index view. It just really simplified the development process as a whole for me.
In summary, my Rails portfolio project was a great learning experience, as I was able to wrap my head around some more complex associations and also discover the real beauty and benefits of test driven development!