Off the Rails!
Tags: ruby, rails
Published: 2021-05-14 01:46:37

For Flatiron School's Phase 3 final project, we were tasked with creating a web application using the Ruby on Rails framework. I had the idea to make a Book Club app, as it sounded like an idea that I as a user would be into. It would be easy to meet the requirements of a many-to-many relationships, since users would logically be able to belong to many different individual clubs, and clubs are going to have any number of users. Once I dove into the project creation, I had plenty of issues harnessing Rails' "magic" (ie. the convenient assumptions and built-in helper methods that Rails offers that save lots of explicit coding- IF you are comfortable with them.)
My models were my first course of action. I had a straight line from Users => Clubs <= Books in which they would associate. I created join tables to properly associate them: Users => Club_Users <=> Clubs <=> Club_Users <=> Books. These would store the foreign keys of their joining clubs. Additionally, the Club_Users would have a boolean value in the table for that user's role as an admin to the associated club. I implemented the with some aforementioned Rails "magic": 
```rails g resource users username email password```
The above command line creates the model, controller, and database migration for Users- with the attributes starting with 'username' assumed as strings, unless specified otherwise. It would have been tedious to created each file for each of my models, so this is a prime example of the Rails "magic" at work. 
Once I migrated all of these to the database, it was time to think about my app's routes. In the 'config/routes' file that's provided upon starting a new Rails project, I waved the magic wand another time:
```resources :clubs do 
    resources :books, :only => [:new, :index, :show]
  end```
With the 'resources' keyword, the framework sets up every RESTful route associated with that model, with corresponding helper method for each one- ie. now '/clubs', an index of all clubs was now available to be implemented, and could be called with 'clubs_path' throughout my app. The above code demonstrates my nested resource of 'books', in which I specify which of the RESTful routes I am opting to use. 
I set up my log in functionality similarly to the Sinatra project of the previous phase- setting up validations for email, username and checking for presence and uniqueness, and the has_secure_password ActiveRecord method that works in the .authenticate function on a given user to check that their password matches. Then I set the session_id equal to the ID of the user that just logged in, and delete the session on clicking the logout button in my navbar. Additionally, a requirement for this project was to allow a sign in via a 3rd party such as Facebook, Google, or Github- I installed the Omniauth gem and set about following the documentation for registering an app on Google, and taking the client_id and client_secret it provided, setting it in a .env file as it directed, and hiding that information from pushing to Github via .gitignore in my project (as this is sensitive information that could compromise the security of an actual app that gets regular use.) I understand that my app needed to use this stored information to communicate and verify with Google, then once it gets verified a user is able to use their account to log in. 
```scope :newest_books, -> { order(created_at: :desc).limit(5) }```
I used this scope method in my Books model, essentially as a class method that uses ActiveRecord's database querying directly to select the 5 most recent books that had been created across the app. I created a custom route outside of RESTful conventions called 'recent_books' and was able to directly display those 5 most recent books- using the Rails helper method 'link_to' to generate an HTML link to each book's show page. Another custom route my app called for was for a page that showed every book club a user belonged to in order to make a user's experience navigating easier. 
```  get "/your_clubs", to: "clubs#your_clubs"```
This took every club in which the current user had joined and iterated over each and displayed it for easy access.
I wanted to find a way to leverage the 'admin' boolean value of my club_users join table- naturally I only wanted users with the admin value set to true to be able to implement the create, edit, and delete actions in each club. So anytime a user created their own brand new club, that would set their admin value to true, giving them control over that club- in the 'show' view for each club, I put some conditional value to if the current user is in fact that admin, display the buttons for choosing the new book to read, ability to update the group, and ability to delete it altogether. Otherwise, simply by joining an existing group, the functionality that is unlocked is access to the virtual meeting link that is provided by the club's admin (again through a conditional 'if' statement in my .erb show page.)
It took fighting through plenty of Rails server error messages to get all of these application features up and running, but each one taught me more about how to wield the logic of this powerful framework, and why it is extremely popular in modern day web applications.