Rails with JavaScript Portfolio Project

The overarching goal of this project was to take my existing Rails project (Subway Scheduler), and add dynamic features. To do this, we serialized the information in our ActiveRecord models, made them available via JSON API, and then used JavaScript to display that information, rather than utilizing only static Rails Views.

The requirements were further broken up as follows:

  1. Must translate JSON responses from your Rails app into JavaScript Model Objects using either ES6 class or constructor syntax. The Model Objects must have at least one method on the prototype.
  2. Must render at least one index page (index resource) via JavaScript and an Active Model Serialization JSON Backend.
  3. Must render at least one show page (show resource) via JavaScript and an Active Model Serialization JSON Backend.
  4. Your Rails application must dynamically render on the page at least one serialized has_many relationship through JSON using JavaScript.
  5. Must use your Rails application to render a form for creating a resource that is submitted dynamically and displayed through JavaScript and JSON without a page refresh.

JavaScript Models: Addresses and Routes

I serialized both my Address and Route models using ActiveModel::Serializers and made them available as JSON using Rails’ respond_to method.

As for the JavaScript models for each type of object, the constructors (and a formatter) looked like this:

Address

class Address {
  constructor(address) {
    this.id = address.id;
    this.line_1 = address.line_1;
    this.line_2 = address.line_2;
    this.city = address.city;
    this.zip = address.zip;
    this.path = `/addresses/${address.id}`;
    this.string = this.stringify();
  }

  stringify() {
    var addressAsString = "";

    addressAsString += `${this.line_1}, `;
    addressAsString += ((this.line_2) ? `${this.line_2}, ` : '' );
    addressAsString += `${this.city}, NY ${this.zip}`;

    return addressAsString;
  }

  ...

}

Route

class Route {
  constructor(route) {
    this.id = route.id;
    this.name = route.name;
    this.origin = new Address(route.origin);
    this.destination = new Address(route.destination);
    this.user = route.user;
    this.path = `/routes/${route.id}`;
    this.directions = [];
  }

  ...

}

Rendering Address index page, adding new Addresses

First things first, I wanted to render my Address index page using JavaScript. First, had to make sure I could select where I wanted to put the addresses on the page by making sure the div had an id of 'address_list'.

Then, I could call the Address‘s JavaScript Model’s getAll function once the page was ready:

class Address {

  ...

  static getAll(url) {
    allAddresses = [];

    $.get(`${url}.json`, function(response) {
      response.forEach(function(response_item) {
        var address = new Address(response_item);
        allAddresses.push(address);
      });
    }).then(() =>
      $("#address-list").html(HandlebarsTemplates['all-addresses'](
        {items: allAddresses})
      )
    );

    return
  }

  ...

}

That gets the addresses from the JSON API we created using our serializer, creates a new JavaScript Address for each, and then adds each to the DOM using a Handlebars template.

Adding a new Address

The Address index page also allows for adding new Addresses, and rendering them dynamically. A “New Address” form with id 'new_address' is watched for submissions. Upon being clicked, we serialize the form info, and POST it to our API for storing in the database. Then, it’s added to the DOM by creating a new Address JavaScript object and tacking it on the already-rendered list.

Showing a Route via our own API and Google Maps API

For showing a single Route, we have to get origin and destination Address information, plus use Google Maps’ Directions API to find actual routing information.

I wanted to actually load this information dynamically on a Route list page, as I thought it would be more fun. So, on the Route index page (or a User‘s nested Route index page), each Route has a “show route” button with class 'show_route', as well as a data-route-id tag for tracking which button is clicked.

When that button is clicked, we search through our JavaScript Route objects for the matching one, and call the showRoute function on it:

$(document).on('click', '.show_route', function(e) {
  e.preventDefault();
  const element = $(this)

  const route = allRoutes.find(function(rte) {
    return rte.id == element.data('route-id')
  }).showRoute(element)
});

showRoute gets the directions via Google Maps’ Directions API, and the object’s origin and destination Addresses, and dynamically adds them to the DOM using another Handlebars template (FYI, directionsService is a Google Maps DirectionsService object):

class Route {

  ...

  showRoute(el) {
    const route = this;

    var request = {
      origin: route.origin.string,
      destination: route.destination.string,
      travelMode: 'TRANSIT'
    };

    directionsService.route(request, function(result, status) {
      if (status == 'OK') {
        route.directions = result['routes'][0]['legs'][0]['steps']
        el.after(HandlebarsTemplates['show-route'](route));
      }
    });
  }

  ...

}

Video walkthrough

Links

Github

Rails Portfolio Project

For my Ruby on Rails portfolio project, I decided to do something using an additional API, as it had been fun to learn how to use Facebook’s OAuth one during the lessons.

As I have for my previous projects, I decided to incorporate one of my passions/hobbies: the New York City Subway system (actually, it uses all transit options in the city).

APIs

At first, I thought I would use the MTA’s set of APIs. However, I quickly realized that that would not allow me to provide transit directions (how to get from point A to point B) in the way that I wanted, as—duh!—they only provide real-time status of trains, buses, etc.

As it turns out, Google uses information gleaned from MTA’s API, which is formatted according to Google’s GTFS specification, and uses their computing power to figure out the directions. Ok, so I’m going to use Google Maps’ Directions API. I found a nice Gem that does that—all I had to do was provide my API key.

Models

Next, I had to begin thinking about what models I would use. Obviously, I’d need a User model:

User

  • Fields
    • First name
    • Last name
    • Email address
    • Password
  • Validations
    • Presence of, uniqueness of, and email-address-ness of Email address
    • has_secure_password

and an Address model:

Address

  • Fields
    • Line 1
    • Line 2
    • City
    • Borough
    • ZIP code
  • Validations
    • Presence of Line 1
    • Presence of Borough, and its inclusion in [‘Bronx’, ‘Brooklyn’, ‘Manhattan’, ‘Staten Island’, ‘Queens’]

Since I am building this app only for New York City, I didn’t need a State field. And I included Borough so users could filter addresses, a requirement for the project.

As for the has_many through requirement, it made sense to create a Route join table. A Route would belong to an Origin and a Destination (both Addresses), and a User. A User has_many Routes, and Addresses through Routes; an Address has_many Routes, and many Users through Routes.

Route

  • Fields
    • Name
  • Validations
    • Presence of Origin and Destination
    • Presence of Name, and its uniqueness at the User level

The Name field will be a user-supplied label for the Route.

Controllers

The controllers are very similar to what we’ve seen before. In addition to all seven RESTful routes for each of the models listed above, I built a Sessions controller to deal with login and logout logic. I wanted user to be able to login using either email/password combination OR OAuth2 through Google, so there are both “sessions#create” and “sessions#create_with_google” routes. A Sessions helper defines helper methods that interact with the session hash.

Views

Got to use a lot of partial views, which was fun. There are lots of different ways to display or list addresses. Of course, put the forms into partials. In addition, I moved the error-message-displaying stuff, included on each form, into it’s own partial in /app/views/shared.

The “routes#show” route is where the Google Directions API does its stuff. The array returned by the google_maps_service gem looks pretty complicated at first glance—it’s an array of hashes many levels deep—but once you get the hang of it, it’s actually pretty simple. Here’s an example of where the Subway stop at which to get off is stored:

[
  {
    :legs=>[
      :steps=>[
        {
          :html_instructions=>"Subway towards Wakefield - 241 St"
          :transit_details =>{
            :arrival_stop =>{
              :name=>"3 Av - 149 St"
            }
          }    
        }   
      ]    
    ]
  }
]

Damn!

Video walkthrough