I wish I could say this time around I had learned all of the lessons from the last project and planned this out to the point that it was a painless and straightforward process. While that definitely didn't happen I realized that I have been in a combination of my Ruby comfort zone and the guidance of the curriculum's pre-built tests for long I've developed some really bad habits. In the HTML, CSS, and Ruby sections of the curriculum I often didn't read the content or watch the videos and just skipped to the labs. While not ideal this worked because of my prior experience. In addition on the labs themselves I would rush, trying to solve as many requirements as possible in a particular file before actually running the tests. Which again is an approach that works fine when someone else has already mapped out the application's architecture and scope but is likely not the best. When it came time to tackle SQL, ORMs, ActiveRecord, Rack, and Sinatra I was still employing the approach I had for the previous sections. As a consequence I failed to learn as much as I could have if I went through the sections slower, and it took me two weeks to complete this project because I had to refresh my knowledge and redo so much. All the time I saved by speeding through the content in order to catch up and my poor approach has now set back even further from my goals.
As for the actual process, when I reviewed the requirements I thought of an application to write books immediately. Since the model was fairly simple thats all the planning I really did. I wish I would have thought more about what views and routes would be necessary to support such a task. To be fair at the time I don't think my grasp of how this would all come together was good enough to realize my vision went beyond the minimum viable version and entertaining other ideas would have easily lead to an as simple model without the need for complex views and routes.
I started by building out the file structure with files for every model and setting up the database. This went fine until I ran creation and association tests with Tux. I revisted SQL relationships and realized that in has-many to belongs-to relationships the belongs-to table needs an ID column for its owner. I didn't have any many to many relationships but I also noted that in has-many to has-many relationships a join table with ID's for both classes is needed. After adding the migrations to fix this problem I started working through the views and routes to handle user accounts.
Next I worked through the routes for users to manage books. I made another crucial mistake here in that the routes I created weren't always RESTful, but more on that later. After I got to creating everything for chapters I struggled to decide on what the routes for chapters should be and ended up choosing "/book_title/chapter_number". It was here, roughly halfway through the project I realized in my original setup I hadn't thought out a way for users to write and save the actual content of chapters. I decided to create a SQL migration to add a content attibute to my existing chapters table and a seperate view for writing chapters. I thought that since users were going to be using an HTML textarea for writing that it should be seperate from the actual chapter creation process due to both visual limitations and the frustration of writing out a whole chapter only to have your submission rejected because the chapter title or number wasn't valid.
I had a meeting with my technical coach (Dakota) at this point, and after telling him that this wasn't going to be the project review (I had planned to be done a day or two before) we went over my progress. He explained how my routes weren't RESTful, how easy it was to bypass HTML form validations, and features like Sinatra's Before method I could be using to make this easier on myself. I corrected some of the previous Chapter routes I'd written and wrote out the remaining route paths to ensure I'd make them RESTful. It was around this time that I got very fed up with this project and considered not only leaving the program but abandoning the pursuit of the profession altogether. I took a break and during this time of reflection is when I realized I got myself in this mess because of how I've been approaching everything up to this point. I decided to stop trying to complete every route and view I worked on as the stress of trying to predict every user move, future links, and helpful piece of information to display was turning every hill into a mountain. My new approach was to finish the most basic version of one element of what I was doing (Example: finish the route for when users do everything correctly to see a book, and in the view to just display that books content. Adding helpful links that only show in appropriate situations and route logic to handle bad user input could and should wait.). This new strategy gave me a second wind.
After finishing the Chapter views and routes I started on the problems Dakota and I had noticed. I added validations to models so they would enforce length and content specifications. I went through and updated my routes to be RESTful, and in doing so broke all my helpful links. Despite my intial objections to a navigation bar due to needing some CSS for even the most basic (I wasn't sure how to properly add CSS in a Sinatra project and was concerned with the potential time increase it could create if the temptation to include it elsewhere struck but Dakota had talked me out of this), I added one. This allowed me to quickly delete the old bad links the views had and more easily add relevant links to the pages that didn't have them.
After making it so that deleting a book deletes all associated chapters I started testing bad user inputs. Initially I panicked because both the ones in my forms and models didn't seem to be functioning. I learned that HTML form pattern matching doesn't prevent bad data from being typed in, it only blocks form submission. Also the pattern attribute I was using for form validation isn't compatible with HTML input type number, so I reverted the form fields I switched back to text type. As for the models, directly modifying a value doesn't trigger ActiveRecord validation. Only the Create, Save, and Update methods do. After making the necessary validation updates I went though and added logic to handle the different user mistakes. This includes looking up books, chapters, or a user that don't exist or trying to perform an action that requires authentication or being the author when you're not.
While recording the walkthrough I ended up discovering more problems. The biggest being the my chapter views where using ActiveRecord's find_by method on ordinal's but that meant they could find a chapter for another book if the ordinal is missing or just if there's a chapter with a lower ID and the same ordinal. To fix this I created a custom method that uses a books chapters for its search target but still uses ordinals as the search term. Then I went through and turned all my repeated logic for handling bad user inputs (non-existent book or chapters, not being logged in, already being logged in, trying to change a resource that isn't your own) with helper methods. This is another one of those things that would have been extremely beneficial had I known about it sooner. At this point I was finally unable to fail any tests I ran.
I must say while I currently don't plan on improving and adding to this project like most of my previous end of unit assignments in the past. I do appreciate the reminder of the importance to having a viable system for completing a software project. Its fairly obvious but I don't think we'd have any major website we do today if their creators started by trying to create the current version of Facebook or Amazon. Hopefully going forward I'll remember both this and to properly plan out projects so these posts can be more about postive things I did than major gotchas.