Rails App - Habit Tracker
Tags: beginners, rails, ruby, sql
Published: 2019-08-02 04:32:01

When thinking about my Rails app, I wanted to choose something that was useful in everyday life. I've been trying to create good habits in my own life like drinking more water, waking up early, exercising, and learning more about programming every day. Even though it may be a little overdone, I decided to try to make a simple habit tracker app where users can login everyday and "check off" their habit for the day. 

# Overview

With this Rails app, users are able to view habit categories or make a new category, view habits, or make new habits, and create goals for a particular habit they would like to create. The app lets the user choose a start date for the goal and allows the user to input a description / notes for their goal. Once the goal is created, the user can mark their habit complete for the day with the click of a button and see a few stats on the progress of their goal.

# Setting up the Foundation

I decided to use the model and controller Rails generators for my project. I didn't want to add a lot of unnecessary files to my project, but appreciated the time I saved by using these generators instead of creating all the files manually. The models I ended up using for my app are as follows:

- Habit (belongs_to :category, has_many :goals, and has_many :users, through: :goals)
- Category (has_many :habits)
- User (has_many :goals and has_many :habits, through: :goals)
- Goal (belongs_to :user, belongs_to :habit, and has_many :completion_dates)
- CompletionDate (belongs_to :goal) 
-- I added CompletionDate later on in the process of writing my app because I wanted a table to track which days a user completed a goal, to ensure the user couldn't complete a goal more than once per day.

I included simple validations for my models like requiring the presence of an email for a user, name for category, etc. 

# Authentication

There are two ways for users to login / sign up for Habit Tracker. They can either manually enter an email, password, and name or login through Facebook. 

- For authentication, I used the bcrypt gem and has_secure_password macro, which also validates that the user enters a password
- For the Facebook login I used the omniauth and omniauth-facebook gems

# Nested Routing

For my project, I decided to nest a few routes: 

- Goals (:new, :show, and :edit) actions are nested under Habit which made sense to me, as they belong to a habit.
- Habits (:index and :new) actions are nested under Categories since a habit belongs to a specific category.

# Bootstrap

This project was my first time using Bootstrap. After reading a few blogs about it and skimming the documentation, I decided to install the bootstrap gem. It was pretty easy to get bootstrap up and running thanks to this [Medium blog](https://medium.com/@biancapower/how-to-add-bootstrap-4-to-a-rails-5-app-650118459a1e) I found.

I used the bootstrap documentation to apply styling to the lists on my app, add a navigation bar, and format links and buttons. Other than that, I left my design pretty minimal.


# Refactoring

Refactoring my code was one of the more challenging and rewarding parts of my project. It was sometimes difficult to find patterns among the many files in my Rails app. I was able to use a few partials to clean up my code for rendering error messages on pages as well as rendering the link to login via Facebook. I also used a partial and locals to render a form for my new and edit goals actions. 

I was also able to keep my controllers relatively thin by writing class methods for any logic in my controllers, as well as helper methods within my controllers for repeated code. 


# Things I Got Stuck On


1. When reviewing my app, a peer found a bug where completion dates weren't being destroyed when the associated goal was destroyed, meaning that if she deleted a goal with a completion date and then created a new goal, the new goal would be assigned the completion dates from the previous deleted goal since they shared the same id. It took a little while for me to figure out what was going on and realize that the CompletionDates table wasn't being updated when a goal was destroyed. After some googling, I realized I needed to add this line of code (:dependent => :destroy) to my has_many association in my goal model, so that when a goal was destroyed, the callbacks would destroy the dependent association (the completion dates). Thank you to my friend for testing and finding the bug :).

2. Working with dates proved to be confusing and difficult. If I used UTC times only, the transition between days would happen at 5pm in Pacific Standard Time. This created some non-intuitive behavior in my app. I decided to store my goal start dates as UTC and my completion dates as local time. To keep things simple, I used the to_date method and local time whenever I compared dates in order to remove the complication involving hours. I also converted to local time whenever my logic needed to compare the date to the date stored in my database. These two strategies allowed my app to function accurately, even if users travelled between time zones while using my app. 

# Planning for the Future

Here are the things I would like to revisit if I have time to continue developing my app:

- I would like to add some sort of data visualization to show the user how far they have come to meeting their goal.
- I would like to allow the user to set other time frames for a goal than just the 66 days that my app currently allows for, as well as let users decide the frequency in which they would like to perform a habit.
- I would like to improve the front-end for my application as I gain more CSS skills.

### If you have made it this far in reading about my app thank you so much! Here is the link if you would like to check it out and feel free to send me feedback.

[Habit Tracker](https://github.com/racheladaw/habit-tracker-rails-app)

[Video Walkthrough](https://youtu.be/kg6SMv52VWk)

![T. Hanks](https://media.giphy.com/media/KJ1f5iTl4Oo7u/giphy.gif)