The Board
By: Juliana Connors
Tags: Ruby on Rails, Software Development, Software Engineering, Rails

I created a ride scheduling app for barn managers or trainers to use to keep track of training schedules for their riders and horses. It’s common practice in most barns to write the day’s schedule out by hand on a white board. And a picture of the board is sent to staff to reference throughout the day.

However, the schedule is often changing. Perhaps the horse show is running behind schedule or a rider needs to lesson earlier than they originally planned. With this app the rides can be edited so the schedule for the day is easily updated.

Model relationships

I started with the model relationships. The user is a trainer or barn manager. The user logs in and creates a collection of horses and riders representing their stable. A rider could be a student or a staff member.

The user creates rides (the join model), which belong to both a horse and a rider.

A user has many horses

A user has many riders

A user has many rides

A rider belongs to a user

A rider has many rides

A rider has many horses through rides **the idea here being that a rider may ride one horse one day and another the next.

A horse belongs to a user

A horse has many rides

A horse has many riders through rides

If I were to expand this app, I would also add an owner model. A horse would belong to an owner and the owner would have many horses and have many riders through horses.

Third Party Login

After sorting out my model relationships, I set up the application foundation using rails resource generators. Then I got my login/signup functionality sorted. The basic login/logout was similar to what was implemented for the Sinatra project, but third party login was something new.

I chose to add third party login with Google. I started setting up third party login by adding the necessary gems to the gemfile. Then I added an omniauth.rb file to config/initializers and added the code to tell the application to use Omniauth to access Google.

Next, I registered my app with Google Developer Console to receive the Google client id and client secret. I added the client id and client secret to the dotenv file I created and added my dotenv file to my gitignore file.

Next I went into config/resources to add the callback route:

get “/auth/google_oauth2/callback”, to: “sessions#omniauth”

After the connection was established, I created methods within my sessions controller to handle the information by Google to register a new user or login an existing user.

I added a private auth method to access the request.env hash and I used an omniauth method to find or create a user by email, then use a yield block to assign password and name attributes.

Nesting

The next complicated part of the process for me was nesting. I started with nested resources. I wanted rides nested under riders with new, create and index resources. Rides also belong_to horses, but I made the decision to create rides by first selecting a rider. It made more sense to me that a user build a ride off the rider model rather than the horse model. From the riders show page, the user goes to the new_rider_ride_path to create a new ride.

To get the new ride form working properly, I needed to pass an instance of a ride and a rider id. I considered creating rider and ride instance variables in the new action:

However, this would mean calling on the database twice as the rider instance created in new was no longer available after submitting the form.

Instead, I just created a ride instance variable in the new action to pass to the form. And I used params[:rider_id] to pass the rider id to the form.

In the controller, I found the rider object in the create action and built the ride using the ride_params from the form.

Nested forms were a difficult concept during the curriculum, but so useful. In fact, my Sinatra project included a case where a nested form would have been perfect. Instead I wrote complicated logic in my controller to handle finding the “nested” object if it existed or creating a new one.

After spending a while trying to get my new ride form to work accepting nested horse attributes, I realized it wasn’t going to work with my domain model. Since my horse model contains a user_id attribute, I wasn’t able to create a new horse within a horse attributes writer because I didn’t have access to the session to set the horse user_id equal to the current_user.

Fortunately, I don’t think it’s a necessary feature for this application. A horse might be added to a barn’s collection once a month or once every few months. It wouldn’t be the case that a user would frequently be adding a horse when scheduling a ride.

Scope Methods

With a lot of the functionality sorted out, I then worked on the layout of the views and deciding how to display the information. I primarily needed to format the ride model attributes and scope the rides collection to organize the display. For instance, I wanted the rides sorted by date and time on all views. I also wanted the user show page to display a list of today’s rides sorted by time:

Debugging

I spent more time debugging on this project than I have on others so far. Rails made a lot of features easier to implement than with Sinatra. However, the abstraction made it more difficult to follow exactly what the code was doing.

I became very fond of raise and the inspect method to see the value of an instance variable or check out the data being passed through params. For me, learning to play with code in the console and use tools like pry and inspect has been a crucial part of learning to code and it was especially helpful in building a project with Rails.

Big Picture

In a more advanced version of this app, a trainer or barn manager acting as admin with access to schedule and update rides and riders would be able to login and see the schedule for the entire barn as well as their individual ride schedule. Additionally, I think users should receive notifications if there is a schedule change and perhaps reminder notifications 30 min prior to a ride similar to Google Calendar.