Rails Fitness Tracker App Part 1: Setup & Devise

This is Part 1 of a series of posts about my experience building my first Rails applicaiton. You can read the prelude to the project here.

Recap

For my first Rails app, I am going to build a fitness tracker that let's users sign up for an account where they can create routines and store exercises in them. They can then create more, edit and/ or delete those routines and exercises.

Initial Setup & Wireframing

The setup experience with Rails was pretty smooth. Unlike Sinatra, which doesn't usually come with an out-of-the-box setup process (with the fabulous exception of the 'corneal' gem, Rails does:

rails new flatiron_fitness_rails_app

This will create all the necessary files and folders to get the project to run on a server, on Rails!

I went ahead and made a flowchart that maps out the model associations and a typical user experience:

Flatiron_Fitness_RE.jpg

As shown above, a User can have many Routines. We have a join-table, RoutineExercise, that allows for each of a User's routines to have many exercises, which can also have many routines. One of my project requirements is for my join-table to have a least one user-submittable attribute (something besides a foreign key like routine_id). So I went ahead and added a reps and sets column for each RoutineExercise that is created for a Routine.

Flatiron_Fitness_RE (1).jpg NOTE: Some of the file names I have in this image weren't used in the actual codebase

Here, I'm mapping out what a simple user experience would look like in the app.

  • The user starts up the server and are directed to login or signup for an account
    • At this point, the user can opt for third-party authentication (though some :provider like Github or Facebook) instead of coming up with a new set of login credentials.
  • They are then taken to an index page that shows a list of their routines. From there:
    • They could create a new routine, or...
    • Click on a specific routine to see more information
  • Inside an individual routine, they are shown a list of exercises; each one can be clicked for more information.
  • Whether they're looking at a specific exercise or routine, they can either:
    • edit it (and be redirected back to the appropriate view)
    • delete a routine (along with any associated RoutineExercise objects, but not the associated Exercises themselves)
  • Inside an exercise show page, the user should be able to either remove it from the routine or delete it entirely (which will cause other instances of the Exercise object to be deleted).

Devise and OmniAuth

Next, I wanted to fulfill the user auth requirement for the project by going with the devise gem. Devise takes care of all the user- and session-related tasks including things like setting session cookies upon login, third-party authorization with OmniAuth(more on that, soon). I don't need to worry about creating a users_controller, sessions_controller or a separate migration for creating the users table -- Devise takes call of that!

To be honest, this was hard to set up at first. If you want to use more than one provider (more than one existing account to log in from), you have to do the following for each provider:

  • Register your Rails app with the provider of your choice by heading to their developer pages and following the setup instructions.

  • Each provider comes with their own special omniauth-<provider_name> that you must include in your Gemfile. In my opinion, it's best to do both that and also include the general 'omniauth' gem so that, for example, you're assured your login link for Facebook is omniauthable.

  • In config/initializers/devise.rb, there is a section in the source code where you must set your configurations for your omniauthable provider against the environment variables that are ( safely supplied from an .env file.

  • You have to refactor your config/routes.rb so that your user routes have access to an omniauth.rb (or callbacks.rb) controller (make sure you create the appropriate file in controllers ;). This gives your users table access to those provider links (allowing 3rd-party login/signup).

After much toil, it's incredibly worth. I can now simplify my life by not having to create a new set of login credentials by sticking with a set I use a lot more often.

Screen Shot 2020-06-01 at 11.20.30 AM.png

Now, the only caveat I've found with this is that, imagine if a user could choose between Facebook and Google as an option for login/signup and they chose Google. If they log off and then try to login through Facebook, they are weirdly redirected to the signup page. In other words, they can only login through Google since that was the provider they chose the first time.

For now, I'm going to let that go and just note in the README that when a user first starts the application and creates an account, if they want to do so through 3-rd party authentication, they must stick whichever they choose. *

** I'm almost certain there is a way to fix that issue and make it so that a user can flexibly login to the same Rails application from multiple providers. There are websites that can do that. But there are also websites that don't have that flexibility. I think that, for a first Rails application, I should appreciate that I got this far and that there is always room to grow.*

End of Part 1.

Happy Coding!

Resources

How To Configure Devise and OmniAuth For Your Rails Application

How To Use Environment Variables In Ruby

Comments (1)

Francisco Quintero's photo

Great here. Question. How did you feel using Rails after using Sinatra?

And regarding Omniauth and social sign-in. It's normal. In those services where you can log in with Facebook, Twitter or Google, I've seen you have to merge accounts in order to join with either of them. Otherwise you end up with several accounts.