# Rails Fitness Tracker App Part 2: Routes and Views

*This is Part 2 of a  [series](https://blog.mydevdiary.net/prelude-to-upcoming-rails-project-series-ckarmzr1r04txbbs1vc2629g7)  of posts about my experience building my first Rails application. Read Part 1  [ here](https://blog.mydevdiary.net/rails-fitness-tracker-app-part-1-setup-and-devise-ckay92jpw01dzeds1146dcleh) *

At this point of the project, I have successfully pulled off 3rd-party authentication using Devise and OmniAuth! 


![Screen Shot 2020-06-05 at 8.21.34 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1591359714560/484uFXULv.png)

So now, along with signing up/ logging in the "old-fashioned way", a user can do the same thing using an account they already have with another provider (like Facebook or Google). 


# Routes and the Controllers
With my user login/signup out of the way, I can now focus on routing the controllers to my views (more on the views later). 

One of my pain points in this project was routing and working with controllers. By the end of this, I understood that routes are supposed to set up the endpoints of their related controller action. Those controller actions, then, perform a bunch of tasks with whatever data is available in the strong params. While I still have *much* to learn about controllers and Rails' MVC architecture, I feel I am coming out of this with a bit more understanding of them.

 I also wanted to use custom attribute setters for a newly-created routine's nested exercises, to guard against duplicate exercises being created. While I could have gone with something like `accepts_nested_attributes_for :exercises` in my `Routine` model, I wanted to ensure that both the nested attributes for exercises (1-level deep) *and* the related routine exercise (2-levels deep) were mass-assigned upon creation of the routine. So I went ahead and created custom attribute setters for the following:

```
# app/models/routine.rb

  def exercises_attributes=(exercise)
    if exercise[:name] != "" && exercise[:exercise_type] != "" && exercise[:description] != ""
      new_exercise = self.exercises.build
      new_exercise.name = exercise[:name]
      new_exercise.exercise_type = exercise[:exercise_type]
      new_exercise.description = exercise[:description]
    end
  end
```
If a user decides they don't want to create a new exercise while creating a new routine, the outer-if statement allows for that.

```
# app/models/exercise.rb
  def routine_exercises_attributes=(routine_exercise)
    new_routine_exercise = self.routine_exercises.find_by(exercise_id: self.id)
    new_routine_exercise.update(sets: routine_exercise[:sets], reps: routine_exercise[:reps])
  end
```
When editing/ updating an exercise inside a routine, the `Exercise` model needs its own custom attribute setter for `routine_exercises_attributes` since it is stored inside of `exercises_attributes` (which is ultimately stored as strong params in the routine's `routine_params`). 

Inside the controllers for `Routine` and `Exercise`, I added some private-level set methods to DRY out some of the controller actions: 

```
# app/controllers/routines_controller.rb

class RoutinesController < ApplicationController
  before_action :authenticate_user!, :set_routine
      # other code here
    def set_routine
      @routine = Routine.find_by(id: params[:id])
    end
end
```


```
# app/controllers/exercises_controller.rb
class ExercisesController < ApplicationController
  before_action :authenticate_user!, :set_exercise

      # other code here
    def set_exercise
      @exercise = Exercise.find_by(id: params[:id])
    end
end
```

This is going to be covered in more detail later, but it should be mentioned that, in conjunction with these class-level setter methods, there is a form with two levels of data entry: 

1. A nested form for if the user wants to create a new exercise while creating a new routine
2. A nested form *within* the previous nested form that records the `sets` and `reps` for the exercise's (and, by extension, also the routine's) `RoutineExercise`. 

Speaking of nested, I also decided to go with nested resources for when I user is dealing with their exercises in a particular routine. In other words, in `config/routes.rb`, I added nested resources for `:routines` and `:exercises`, scoped to certain views, that allow for the 

# The Views
At this point in the project, it's time to start thinking about how I want the user experience to play out while they are creating, editing, updating and deleting routines and exercises. 

In `config/routes.rb`, I have set nested routes that describe the relationship between the `Routine` and `Exercise` models. This provides for some nifty paths including `routine_exercises_path`. 

```
  resources :routines, only: [:show] do
    resources :exercises, only: [:show, :index, :edit, :update, :destroy]
  end
```
My application should be nimble enough to "know" which routine and which exercise we're dealing with at a given time. (e.g. `routines/1/exercises/3`) As such, we should have the following views: 

**Routines**  
* `new` via `routines#new` *
* `edit` via `routines#edit` *
* `show` via `routines#show`
* `index` via `routines#show`

**Exercises**   
* `edit` via `exercises#edit` *
* `show` via `exercises#show`
* `index` via `exercises#show`

*These views will be supplied with a `_form.erb` partial to DRY out the forms a bit.

For the next post, I'm going to write about the struggles that awaited me after this point in the project. This included things like keeping track of nested attributes in multiple models, preventing changed data from overwriting other data and a lot more. 

Until then, 

Happy Coding! 

SIDENOTE: As always, if you want to  [check out the project](https://github.com/Dusch4593/flatiron_fitness_rails_app)  for yourself, play around with it and maybe give some feedback, that'd be awesome!
