Category Archives: Programming

How to get started with Rails testing

Where to start with Rails testing

Getting started with Rails testing can be overwhelming because there’s so much to learn. I think the learning process can be made easier by following the following three steps.

Get familiar with testing principles

In order to write tests in Rails it’s necessary to have some understanding of the tools that are used for Rails testing. But also, perhaps more than that, you need to have an understanding of the technology-agnostic principles that apply no matter what language or framework you’re using.

The principles of automated testing are quite numerous. Just as you could spend a lifetime studying programming and never run out of things to learn, you could probably spend a lifetime studying testing and never run out of things to learn.

So the trick early on is to find the few testing principles that you need in order to get started and ignore all the rest. You can learn other principles as you go.

Here are a few principles I think are helpful to be familiar with at the beginning.

Test-driven development

I personally think TDD is life-changingly great. At the same time, I wouldn’t recommend being concerned with following TDD to the letter when you’re first getting started with testing.

The fundamental principle of TDD is the red-green-refactor loop. First you write a failing test for the behavior you want to bring into existence. Then you write the code to make it pass, only concerning yourself with making the test pass and not with code quality at all. Then, once the test is passing, go back to the code and refactor it to make it nice.

When I was getting started with testing I had many occasions where I wanted to write the test first but I wasn’t sure how. What should you do if that happens to you? My advice would be to give yourself permission to break the TDD “rules” and just write the code before the test. As you gain more experience with testing it will get easier and easier to write the test first.

Four-phase testing

Automated tests tend to consist of four steps: setup, exercise, verification and teardown. Here’s an example in MiniTest.

class UserTest < Minitest::Test
  def test_soft_delete_user
    user = User.create!(email: 'test@example.com') # setup
    user.update_attributes!(active: false)         # exercise
    assert user.active == false                    # assertion
    user.destroy!                                  # teardown
  end
end

In the first step, setup, we create the data that the test needs in order to do its stuff.

In the second step, exercise, we walk the system under test* through the steps that are necessary to get the system into the state we’re interested in.

In the third step, assertion, we ask, “Did the code do the thing it was expected to do?”

Finally, in the teardown, we put the system back the way we found it. (In Rails testing an explicit teardown step is usually not necessary because tests are often run inside of database transactions. The data doesn’t have to get deleted because it never gets persisted in the first place.)

*System under test (SUT) is a fancy way of saying “the part of the application we’re testing”.

Test independence/deterministic tests

It’s important that when a test runs, we get the same result every time. The passing or failing of the test shouldn’t depend on things like the date when the test was run or whether a certain other test was run before it or not.

That’s why the teardown step above is important. If a test leaves behind data after it runs, that data has the potential to interfere with another test that runs after it. Again, in Rails, developers tend not to explicitly destroy each test’s data but rather make use of database transactions to avoid persisting each test’s data in the first place. The transaction starts at the beginning of each test and at the end of each test the transaction is aborted.

Fixtures and factories

The first step of a test, setup, can get quite tedious and repetitive. There are easier ways of bringing the test data into existence than by instantiating objects and creating database records at the beginning of every single test.

There are two ways that setting up test data is normally handled: factories and fixtures. Both strategies have pros and cons. Most projects I’ve worked on use either one or the other but there’s no reason they couldn’t be used together.

Fixtures in Rails are usually defined in terms of YML files. The fixture data is persisted once at the beginning of the test suite run. Each test still runs in a database transaction so that any data modifications the test makes will be undone before the next test is run.

Fixtures have the advantage of speed, although the trade-off is that the data setup is a little bit distant from the test code itself. It might not always be clear what the data is that a test depends on or where it’s coming from.

With factories, the data for each test is defined inside the test code itself instead of in a separate YML file. Factories have the advantage of clarity. If the tests were written well then it’s very clear what data the test depends on to run. The trade-off is speed.

Mocks and stubs

I bring up mocks and stubs to tell you that you can safely ignore them when you’re getting started.

You can get quite a ways with Rails testing before a lack of mock and stub knowledge will hinder you. But if you’re curious, the books xUnit Patterns and Growing Object-Oriented Software, Guided by Tests do a pretty good job of explaining the concepts.

Get familiar with Rails testing tooling

Testing frameworks

The two dominant testing frameworks for Rails are RSpec and MiniTest. RSpec seems to be much more popular although that certainly doesn’t mean it’s better.

“Should I use RSpec or MiniTest?” is a question I see lot. Luckily you can’t really choose wrong. The underlying testing principles are pretty much the same whether you use RSpec or MiniTest. The main difference between the two is that RSpec is built on a domain-specific language (DSL) while MiniTest is just Ruby. A lot of people find that RSpec’s DSL improves readability although the tradeoff is that you have to learn the DSL.

I personally started out with Test::Unit (very similar to MiniTest) when I was doing Rails projects on my own. Later I switched to RSpec because almost every job or freelance client I’ve had used RSpec. So even if you pick the “wrong” testing framework to start with, you can always switch to the other one later.

Since I’m an RSpec guy I’ll focus mainly on RSpec-related tools.

Factory Bot

Factory Bot is the de-facto standard library for Rails. The main benefits of Factory Bot in my view are that it helps DRY up test setup code and that it eliminates the need to come up with arbitrary test data (e.g. fake names, addresses, etc.).

Faker

Factory Bot only goes so far with its ability to generate fake data. If I want to generate things like fake but valid email addresses, phone numbers, etc., I use Faker in conjunction with Factory Bot.

Capybara

Capybara is a tool that lets us use Ruby to drive the browser. This enables us to write tests that exercise the whole application stack including HTML, CSS and JavaScript.

Build some practice projects

Just like with programming itself, the way to get good at testing is to just start doing it.

Here’s the way I generally go about developing a new feature or resource in Rails. (I do a Git commit after each step if not more often.)

  1. Run rails generate scaffold <scaffold name> <options>
  2. Write some validation specs for the model I just created
  3. Write a feature spec for the “happy path” (i.e. all valid inputs) of the “create” and “update” functionality for the resource I just created

For the first few models I create in a Rails project I might not do much more than that. It’s usually not until a few models in that my application builds behavior that’s “interesting” enough to write a meaningful test for.

If you want to learn more about how I write Rails tests, I run free online Rails testing workshops about once a week. All the videos from previous sessions are up on YouTube (linked here). I also have a free Ruby testing micro-course.

RSpec Hello World

class Squarer
  def square(starting_number)
    starting_number * starting_number
  end
end

describe Squarer do
  it 'square a number' do
    squarer = Squarer.new
    expect(squarer.square(2)).to eq(4)
  end
end

You can run this test by naming the file `squarer_spec.rb` and running `rspec squarer_spec.rb` on the command line.

If you don’t already have RSpec installed you can install it by running `gem install rspec`.

Getting Started with Angular CLI

One of the main things I liked about Rails when I first started using it was its command line interface. You get so much leverage by being able to scaffold a whole resource with just one command.

I was very happy when I learned about Angular CLI, which is currently in beta. I’ve been using Yeoman for Angular 1 but as far as I’ve been able to tell so far the Yeoman generators aren’t quite caught up with Angular 2. It’s nice to have a tool for Angular 2 that helps me understand how to structure my project, how to get tests set up, and all that nuts-and-bolts stuff that I don’t want to have to think about when I’m trying to quickly get familiar with a new technology.

In this tutorial I show you how to spin up a sample Angular 2 project on a Rails backend. Since the focus is on Angular and not Rails, I’ve provided a Rails repo for you so you don’t have to think about the Rails part too much.

Setting up the project

You can use my repo as a starting point. We’ll blow away and then rebuild the Angular portion but keep the Rails portion.

$ git clone git@github.com:jasonswett/dream_cars.git
$ cd dream_cars

CD into the project directory, create a new branch for this tutorial, then blow away the Angular code (which I always keep in the “client” directory as my own personal convention).

$ cd dream_cars
$ git checkout -b tutorial
$ rm -rf client
$ git commit -a -m"Delete contents of client directory."

Install the angular-cli package globally.

$ npm install -g angular-cli

Spin up a new Angular 2 project using ng new.

$ ng new dream-cars

Since for my Angular/Rails projects I always keep my Angular code in client, let’s rename dream-cars to client.

$ mv dream-cars client
$ cd client
$ rm -rf .git

We can run ng serve to start a server on port 4200.

$ ng serve

If you navigate to http://localhost:4200, you should see a message that says, “dream-cars works!”

Adding the Car component

Generate the car component.

$ ng generate route car

Modify your src/app/dream-cars.component.html to look like this. What we’re doing here, of course, is adding some navigation links.

<h1>{{title}}</h1>

<nav>
  <ul>
    <li><a [routerLink]="['/']">Home</a></li>
    <li><a [routerLink]="['/car']">Cars</a></li>
  </ul>
</nav>

<router-outlet></router-outlet>

If you go to your browser and click on the “Cars” link, you should see “car works!”.

Pulling in some data from the server

I already have migrations and seed data set up for you in the Rails project. All you have to do is run rake db:setup and start the rails server.

$ rake db:setup
$ rails server

If you navigate to http://localhost:3000/api/cars.json, you should see some data.

Modify src/app/+car/car.component.ts to look like this.

import { Component, OnInit } from '@angular/core';
import { Http, HTTP_PROVIDERS } from '@angular/http';

@Component({
  moduleId: module.id,
  selector: 'app-car',
  templateUrl: 'car.component.html',
  styleUrls: ['car.component.css']
})
export class CarComponent implements OnInit {
  cars: any;

  constructor(public http: Http) {}

  ngOnInit() {
    this.http.get('/api/cars.json')
      .subscribe(response => this.cars = response.json());
  }

}

Notice the cars: any part. That’s probably not the way that should be done. Frankly, I don’t know the right way to do that yet. I’m learning too.

In order for Http to work, you’ll have to add it to your application’s bootstrap in src/main.ts.

import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { DreamCarsAppComponent, environment } from './app/';
import { Http, HTTP_PROVIDERS } from '@angular/http';

if (environment.production) {
  enableProdMode();
}

bootstrap(DreamCarsAppComponent, [HTTP_PROVIDERS]);

If you now navigate to http://localhost:4200/car, you’ll get an error. This is because our HTTP request is trying to go to http://localhost:4200/api/cars.json, which doesn’t exist. We need http://localhost:3000/api/cars.json.

To make our Angular server on port 4200 talk to our Rails server on port 3000, we can use a proxy. Kill your Angular server process and run this instead:

$ ng serve --proxy http://localhost:3000

This tells the server to route all XHR requests to port 3000. If you refresh the browser, you should no longer see the error.

There’s another problem, though. If you look at the log output for both the Angular server and the Rails server, you’ll see some wacky errors flying across the screen. This has to do with source map files not being included in /dist. You can fix the problem by modifying angular-cli-build.js in the following way.

/* global require, module */

var Angular2App = require('angular-cli/lib/broccoli/angular2-app');

module.exports = function(defaults) {
  return new Angular2App(defaults, {
    vendorNpmFiles: [
      'systemjs/dist/system-polyfills.js',
      'systemjs/dist/system.src.js',
      'zone.js/dist/*.{js,js.map}',
      'es6-shim/es6-shim.js',
      'reflect-metadata/*.{js,js.map}',
      'rxjs/**/*.{js,js.map}',
      '@angular/**/*.{js,js.map}'
    ]
  });
};

With that out of the way, let’s edit our src/app/+car/car.component.html to allow our car data to be shown.

<ul>
  <li *ngFor="let car of cars">
    {{ car.year }}
    {{ car.make }}
    {{ car.model }}
  </li>
</ul>

<car-form></car-form>

If you navigate to http://localhost:4200/car, you should now see something like the following, with a list of cars.

dream cars

There’s a lot I’ve skipped in this tutorial. There are probably some things I’ve done improperly. This is meant to be a quick and dirty intro to Angular CLI so you can get a real full-stack application functioning so you can start tinkering and making progress with Angular 2.

Where to put Rails modules

The TL;DR answer

You can put any module in /lib as long as you have autoloading set up properly.

The more nuanced answer

The truth is that you can put your modules anywhere. Personally, my main use for modules is to create namespaces for my Active Record models to help keep things organized. Those module definitions just end up in the same files as my Active Record models.

I have kind of a rule for myself when it comes to Rails file organization. My Rails apps tend to be structures as a mix of Active Record objects and Plain Old Ruby Objects (POROs). The rule is:

If a particular model class (whether it’s an Active Record model class or a PORO model class) pertains specifically to my application, it goes in app/models. If the model class has nothing specifically to do with my application (e.g. it’s a utility class that could conceivably be open-sourced and used in any application), it goes in lib.

How to set up autoloading for modules located in /lib

Modify your config/application.rb to include the following line.

# config/application.rb

module MyApp
  class Application < Rails::Application
    config.eager_load_paths += %W(#{config.root}/lib)
  end
end

Restart your Rails server and you should be good to go.

How to define and include a module

You can define a module like this:

# /lib/foo.rb

module Foo
  def hello
    "hello"
  end
end

When you include that module in a class, you can use the hello function in the class from which you included the module. See the below for how you would use this Foo module.

How to use a module in an ActiveRecord model

You can include a module in a class in your Rails project by using the include keyword followed by the name of your module.

# app/models/customer.rb

class Customer < ActiveRecord::Base
  include Foo
end

Now you should be able to do Customer.new.hello and get “hello” as a result.

Further reading

If you’d like to know more of my thoughts on structuring Rails applications, I’d recommend the following posts.

Extracting a tidy PORO from a messy Active Record model
For organizing Rails projects, domain objects are good and service objects are bad

How to Delete an Item From a List in AngularJS

I had a real bear of a time figuring out how to remove an item from a list in AngularJS. I found the solution in this Stack Overflow answer which I’m simply stealing and pasting here on my blog:

$scope.flumps.splice($scope.flumps.indexOf($scope.flump), 1);

So I guess you just have to do it with plain old JavaScript.

If you’re interested in seeing how you might update the server side as well, here’s an example for that:

Flump.delete({ id: $scope.flump.id }, function() {
  $scope.flumps.splice($scope.flumps.indexOf($scope.flump), 1);
});

 

Variable name anti-patterns

Overabbreviation

Bad examples:
passwd
fname
cust
o

Good examples:
password
first_name
customer
order

Explanation: Abbreviations like passwd violate the Principle of Least Astonishment. If I know a class has a password attribute I should be able to guess that that attribute is called `password`. Since `passwd` is pretty much totally arbitrary, it’s a practically unguessable. Plus, how valuable is it to save two keystrokes?

I have a saying: It’s better to save time thinking than to save time typing.

Exception: Abbreviations are fine when they match up with real-world abbreviations. For example, SSN, USA and admin are all fine because these are predictable and already familiar to most people.

Naming Scalars Like Objects

Bad example:
store = "Jason's Capsaicin Station"

Good example:
store_name = "Jason's Capsaicin Station"

Explanation: If I see a variable name like store, I think it’s an object. This problem becomes especially severe when the codebase does in fact have a class called Store and some stores are instances of that class and some stores are just store names.

Naming Counts Like Collections

Bad example:
products = 0

Good example:
product_count = 0

Explanation: If I see a variable called products I would probably expect its value to be a collection of some sort. I would be surprised when I tried to iterate over the collection only to discover that it’s actually an integer.

Naming a Variable After Its Type

Bad example:
array = []

Good example:
customers = []

Explanation: A variable called array or hash could contain pretty much anything. By choosing a name like this you’re forcing the reader to do all the thinking.

Use of Mass Nouns For Collections

Bad examples:
news[0]
inventory[0]

Good examples:
articles[0]
inventory_items[0]

Explanation: There are certain words that have no singular form. Examples include “stuff”, “garbage” and “laundry”. This anti-pattern especially applies to database table names.

Key/Value Mystery

Bad examples:
products.each { |key, value| puts value }
products.each { |k, v| puts v }

Good example:
products.each { |name, price| puts price }

Explanation: Names like key and value have almost no meaning. There’s almost always a more purpose-revealing option.

Angular/Rails CRUD Tutorial Part 1

The idea

I’ve decided to write a very long, comprehensive, multi-part tutorial. I came up with a fictional app called Cadence, a music lesson scheduling app. I wanted to think of something that would be a fitting use case for a single-page application, and I think a calendar-based application would make sense.

The tutorial starts with Angular by itself, no Rails.

The initial version

The initial version of this application will not be very impressive. You’ll be able to create “appointments”, each of which just consists of a date and client name. When you click Save, the appointment details will be added to a list of appointments on the screen. That’s about it. But as we’ll see, just doing that much is involved enough that we wouldn’t really want to bite off more than that for starters.

Since this initial version of the app is just Angular without Rails, we’ll be using localStorage as the data store.

We won’t even worry about tests yet in this initial version. We’ll first write a version without tests, because we already have enough to deal with without bringing tests into the picture, and then we’ll make a second pass with tests.

What you’ll learn

  • How to create an Angular project using Angular CLI
  • How to build components (and nested components)
  • How to use *ngFor to display lists
  • How to use services for inter-component interaction
  • How to use localStorage with Angular

We’re going to initialize this project using Angular CLI. We’re going to install Angular CLI using NPM, so if you don’t have NPM installed yet, you’ll need to install it before proceeding. (If you’re not familiar, NPM is a package manager for JavaScript. It’s kind of analogous to RubyGems for Ruby.)

$ npm install -g @angular/cli

The Angular CLI executable is ng. We can initialize our project with the ng new command, like this:

$ ng new cadence --prefix cadence

The format of the ng new command is ng new <your project name>. In this case we’re calling our project cadence.

Let’s cd into the cadence directory since everything we’ll be doing from this point on will happen inside this directory.

$ cd cadence

Angular CLI can also serve our new Angular application with ng serve.

$ ng serve

After the server spins up, you can visit localhost:4200 and you should see a screen that looks like this:

Creating some components

In a moment we’ll be creating some components. What’s a component? I’ll try to give the simplest answer possible.

Imagine you have an application that has a datepicker thingy that you use in multiple places. Rather than pasting in the full datepicker code wherever you want the datepicker or referring to some include file everywhere you want the datepicker, you could just do something like this:

<my-datepicker></my-datepicker>

If you have an Angular component whose selector matches my-datepicker, Angular will go find the template for that component as well as the TypeScript code that defines the behavior for that component. To me this seems like a pretty natural and convenient way to invoke some behavior.

The Angular Style Guide recommends that we give our selectors application-specific prefixes. The idea is to avoid naming conflicts. So for our app called Cadence, we could put a cadence- prefix on each of our components. For example, we might have cadence-calendar or cadence-datepicker.

The people who built Angular CLI were apparently aware of this style guide recommendation because they built this kind of prefixing into Angular CLI. Angular CLI give us the ability to generate components via the ng generate component command. Each time we do this, Angular CLI will automatically give the component a cadence- prefix.

We can use the ng generate component command to generate our first component. Let’s generate a calendar component. As a reminder, this application we’re creating is a music lesson scheduling app. The calendar component will give us a place to put all our appointments.

$ ng generate component calendar

Now that the component exists let’s get it to actually show up. The selector for the CalendarComponent is cadence-calendar. (If you look at src/app/calendar/calendar.component.ts, you can see the part near the top of the file where the selector for that component is specified.)

If we put
<cadence-calendar></cadence-calendar>
in any template file, it will show us CalendarComponent. Let’s put this selector in the AppComponent template now.

<!-- src/app/app.component.html -->

<cadence-calendar></cadence-calendar>

Now that we’ve made this change we should see a screen like this:

Adding Twitter Bootstrap

I like to use Twitter Bootstrap for most of the apps I create. Let’s just get adding Bootstrap out of the way now so that everything we do from this point on will look somewhat nice.

The first step is to install the Bootstrap NPM package. (The --save means we want NPM to add the bootstrap@next package to package.json so that Bootstrap is an explicit dependency of this project and will get installed anytime anyone does an npm install.)

$ npm install --save bootstrap@next

Next we have to add a few lines to .angular-cli.json. Bootstrap comes with some styles and JavaScripts and we have to tell Angular CLI about these things in order for Bootstrap to get wired up properly.

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": {
    "name": "cadence"
  },
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "cadence",
      "styles": [
        "../node_modules/bootstrap/dist/css/bootstrap.css",
        "styles.css"
      ],
      "scripts": [
        "../node_modules/jquery/dist/jquery.js",
        "../node_modules/tether/dist/js/tether.js",
        "../node_modules/bootstrap/dist/js/bootstrap.js"
      ],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ],
  "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json"
    },
    {
      "project": "src/tsconfig.spec.json"
    },
    {
      "project": "e2e/tsconfig.e2e.json"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  },
  "defaults": {
    "styleExt": "css",
    "component": {}
  }
}

After the very important step of restarting your server, you should see a screen like the one below. (To restart the server, kill the ng serve process and start it again.)

It’s also a good idea to put all our application’s content inside a container Let’s also add a header.

<!-- src/app/app.component.html -->

<div class="container">
  <h1>Cadence</h1>

  <cadence-calendar></cadence-calendar>
</div>

Adding NewAppointmentComponent

We haven’t yet put anything into CalendarComponent. The first thing we’ll actually put into it is another component. Angular recommends a component tree application structure where child components are nested inside of parent components. I thought it would make sense to make CalendarComponent contain a list of appointments as well as a component for creating new appointments. We’ll create that component for creating new appointments now.

First, let’s cd into the directory where CalendarComponent lives.

$ cd src/app/calendar

Now we can create NewAppointmentComponent itself.

$ ng generate component new-appointment

Finally, we’ll put the selector for NewAppointmentComponent in the component for CalendarComponent. We should see NewAppointmentComponent‘s template content (presently just “new-appointment works!”) show up on the page.

<!-- src/app/calendar/calendar.component.html -->

<p>
  calendar works!
</p>

<cadence-new-appointment></cadence-new-appointment>

Adding some form controls to the appointment form

Like I said near the beginning of this post, our appointments will only have two attributes for starters: date and client name. Let’s create two text inputs in NewAppointmentComponent‘s template, one for each of these attributes. Right now we’ll just accept arbitrary text and not worry about whether the date provided by the user is a valid date.

<!-- src/app/calendar/new-appointment/new-appointment.component.html -->

<div class="form-group">
  <label for="date">Date</label>
  <input type="text" class="form-control" name="date" id="date">
</div>

<div class="form-group">
  <label for="clientName">Client name</label>
  <input type="text" class="form-control" name="clientName" id="clientName">
</div>

<input type="submit" class="btn btn-primary" value="Save">

The form should look like this:

Make sure you’re in the calendar directory.

If you click Save, nothing happens, of course, We haven’t hooked up the Save button to anything.

The first step in getting the Save button to actually do something is to bind the form we just created to an Appointment object. We’ll bind the form to an Appointment, then when the user clicks Save, we’ll save that Appointment object (or, more precisely, we’ll save a representation of that Appointment object.)

If you’re wondering what this Appointment object is I’m talking about, it’s something that doesn’t exist yet. Let’s use Angular CLI to help us generate an Appointment class.

(We’re still in src/app/calendar, by the way.)

$ ng generate class appointment

Then we’ll open up the Appointment class definition and add date and clientName properties. Why do we have public date?: string instead of public date: string? The question mark means those arguments are optional. So when we create an empty Appointment instance, we can instantiate it by doing new Appointment() instead of having to do new Appointment('', ''), which would feel kind of dumb.

// src/app/calendar/appointment.ts

export class Appointment {
  constructor(public date?: string,
              public clientName?: string) {}
}

After we define the Appointment class we’ll pull it into NewAppointmentComponent.

// src/app/calendar/new-appointment/new-appointment.component.ts

import { Component, OnInit } from '@angular/core';
import { Appointment } from '../appointment';

@Component({
  selector: 'cadence-new-appointment',
  templateUrl: './new-appointment.component.html',
  styleUrls: ['./new-appointment.component.css']
})
export class NewAppointmentComponent implements OnInit {
  model = new Appointment();

  constructor() { }

  ngOnInit() {
  }

  save() {
    console.log(this.model);
  }
}

We’ve created a model property on NewAppointmentComponent and now we’ll bind model.date to the Date input and model.clientName to the Client name input. If you’re interested in learning more about data binding I’d recommend this article.

We’re also adding a form and putting an event on the form. When the form gets submitted, the save() function on NewAppointmentComponent gets invoked. For now we’re just logging the model. I don’t want to try to do anything fancier than that yet because I want to be sure that this step works first.

<!-- src/app/calendar/new-appointment/new-appointment.component.html -->

<form (submit)="save()">

  <div class="form-group">
    <label for="date">Date</label>
    <input type="text"
           class="form-control"
           name="date"
           id="date"
           [(ngModel)]="model.date">
  </div>

  <div class="form-group">
    <label for="clientName">Client name</label>
    <input type="text"
           class="form-control"
           name="clientName"
           id="clientName"
           [(ngModel)]="model.clientName">
  </div>

  <input type="submit" class="btn btn-primary" value="Save">
</form>

If we try to reload the page now we’ll get an error in the console that says, “Can’t bind to ‘ngModel’ since it isn’t a known property of ‘input’.” This is because ngModel needs FormsModule in order to work but we haven’t imported FormsModule.

// src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { CalendarComponent } from './calendar/calendar.component';
import { NewAppointmentComponent } from './calendar/new-appointment/new-appointment.component';

@NgModule({
  declarations: [
    AppComponent,
    CalendarComponent,
    NewAppointmentComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

You can see in the screenshot below that I’ve filled out the form and clicked Save. In the console appears the Appointment object that corresponds to the values I entered in the form.

Saving data to localStorage

If you’ve never used localStorage before, don’t worry. It’s pretty simple to use and you don’t have to do anything special to use localStorage with Angular.

Below we’re creating array called appointments which, for now, only has one element: the Appointment object we’re saving from the form. Then we’re saving that array to localStorage.

// src/app/calendar/new-appointment/new-appointment.component.ts

import { Component, OnInit } from '@angular/core';
import { Appointment } from '../appointment';

@Component({
  selector: 'cadence-new-appointment',
  templateUrl: './new-appointment.component.html',
  styleUrls: ['./new-appointment.component.css']
})
export class NewAppointmentComponent implements OnInit {
  model = new Appointment();

  constructor() { }

  ngOnInit() {
  }

  save() {
    let appointments = [this.model];
    localStorage.setItem('appointments', JSON.stringify(appointments));
  }
}

If you save an appointment now, then open up the Application tab in the Chrome developer toolbar and go to Local Storage, you should be be able to see the appointment you just saved.

Showing the saved appointments in CalendarComponent

To get the saved appointments out of localStorage and into CalendarComponent, we can just do JSON.parse(localStorage.getItem('appointments')).

// src/app/calendar/calendar.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'cadence-calendar',
  templateUrl: './calendar.component.html',
  styleUrls: ['./calendar.component.css']
})
export class CalendarComponent implements OnInit {
  appointments = [];

  constructor() { }

  ngOnInit() {
    this.appointments = JSON.parse(localStorage.getItem('appointments'));
  }

}

We can show the appointments on the page using an *ngFor loop.

<!-- src/app/calendar/calendar.component.html -->

<div *ngFor="let appointment of appointments">
  {{ appointment.date }}
  {{ appointment.clientName }}
</div>

<cadence-new-appointment></cadence-new-appointment>

Here’s what the page looks like with the appointments added. Not very nice-looking yet but we’ll improve the appearance shortly.

Preserving previously-saved appointments

Until now, NewAppointmentComponent saves an array of appointments that actually just consists of one element, the appointment that’s being saved right now. What it should really do is save an array of appointments consisting of all the existing appointments plus the appointment that’s being saved now. Let’s fix this.

// src/app/calendar/new-appointment/new-appointment.component.ts

import { Component, OnInit } from '@angular/core';
import { Appointment } from '../appointment';

@Component({
  selector: 'cadence-new-appointment',
  templateUrl: './new-appointment.component.html',
  styleUrls: ['./new-appointment.component.css']
})
export class NewAppointmentComponent implements OnInit {
  model = new Appointment();

  constructor() { }

  ngOnInit() {
  }

  save() {
    let appointments = JSON.parse(localStorage.getItem('appointments'));
    appointments.push(this.model);

    localStorage.setItem('appointments', JSON.stringify(appointments));
  }
}

Refactoring the appointment code into a service

The appropriate place to put business logic in Angular is in services. Components shouldn’t do very much. Components should only call services that do things. If there’s some saving of records going on, the component shouldn’t be aware of how that saving happens.

Let’s move our appointment-saving and appointment-retrieving code out of the components and into a service.

We’ll use Angular CLI to generate a service. Before you do so, make sure you’re in the calendar directory.

$ ng generate service appointment

This command will generate a service called AppointmentService. Let’s create a function called save() which accepts an appointment. In the body of our function goes the code we’ve been using inside NewAppointmentComponent so far to save an appointment.

// src/app/calendar/appointment.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class AppointmentService {

  constructor() { }

  save(appointment) {
    let appointments = JSON.parse(localStorage.getItem('appointments'));
    appointments.push(appointment);

    localStorage.setItem('appointments', JSON.stringify(appointments));
  }
}

Then NewAppointmentComponent can just call the service’s save() function.

// src/app/calendar/new-appointment/new-appointment.component.ts

import { Component, OnInit } from '@angular/core';
import { Appointment } from '../appointment';
import { AppointmentService } from '../appointment.service';

@Component({
  selector: 'cadence-new-appointment',
  templateUrl: './new-appointment.component.html',
  styleUrls: ['./new-appointment.component.css']
})
export class NewAppointmentComponent implements OnInit {
  model = new Appointment();

  constructor(private appointmentService: AppointmentService) { }

  ngOnInit() {
  }

  save() {
    this.appointmentService.save(this.model);
  }
}

We’ll need to add AppointmentService as a provider in order for it to be able to be injected into components.

// src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { CalendarComponent } from './calendar/calendar.component';
import { NewAppointmentComponent } from './calendar/new-appointment/new-appointment.component';
import { AppointmentService } from './calendar/appointment.service';

@NgModule({
  declarations: [
    AppComponent,
    CalendarComponent,
    NewAppointmentComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [AppointmentService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Similar to how we moved the saving code from the component to the service, we can move the retrieval code as well. Let’s create a getList() function in AppointmentService.

// src/app/calendar/appointment.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class AppointmentService {

  constructor() { }

  getList() {
    return JSON.parse(localStorage.getItem('appointments'));
  }

  save(appointment) {
    let appointments = JSON.parse(localStorage.getItem('appointments'));
    appointments.push(appointment);

    localStorage.setItem('appointments', JSON.stringify(appointments));
  }
}

Then CalendarComponent can call getList().

// src/app/calendar/calendar.component.ts

import { Component, OnInit } from '@angular/core';
import { AppointmentService } from './appointment.service';

@Component({
  selector: 'cadence-calendar',
  templateUrl: './calendar.component.html',
  styleUrls: ['./calendar.component.css']
})
export class CalendarComponent implements OnInit {
  appointments = [];

  constructor(private appointmentService: AppointmentService) { }

  ngOnInit() {
    this.appointments = this.appointmentService.getList();
  }
}

Now that the getList() function exists, we can remove some duplication between the getList() function and the save() function by just calling getList() to get the list of appointments.

// src/app/calendar/appointment.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class AppointmentService {

  constructor() { }

  getList() {
    return JSON.parse(localStorage.getItem('appointments'));
  }

  save(appointment) {
    let appointments = this.getList();
    appointments.push(appointment);

    localStorage.setItem('appointments', JSON.stringify(appointments));
  }
}

Getting the components to talk to each other

At this point, saving appointments works and getting appointments back out works but we have to refresh the page in order for our appointments to show up. This is of course not what we ultimately want. We want the saved appointments to show up on the page immediately.

We need to get NewAppointmentComponent and CalendarComponent to talk to each other. The Angular docs describe a method of inter-component communication where component A sends some data to a service, then component B gets that data out of the same service. At a high level, this is a simple enough concept to understand.

Where things start to get somewhat complicated is the fact that the example from the Angular docs use observables to achieve this communication. I’d recommend you take a look at my blog post Observables Made Simple before proceeding. It took me a long time to grasp observables. I think that post will help you understand them a little more quickly than I first did.

The way we’re going to use observables in this case is the following: when an appointment is saved, AppointmentService will emit that appointment. When the appointment is emitted, CalendarComponent will become aware of it because CalendarComponent has subscribed to the observable that emits appointments.

The first step is to add the observable to AppointmentService.

// src/app/calendar/appointment.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class AppointmentService {

  private appointmentCreatedSource = new Subject<string>();

  appointmentCreated$ = this.appointmentCreatedSource.asObservable();

  constructor() { }

  getList() {
    return JSON.parse(localStorage.getItem('appointments'));
  }

  save(appointment) {
    let appointments = this.getList();
    appointments.push(appointment);

    localStorage.setItem('appointments', JSON.stringify(appointments));
    this.appointmentCreatedSource.next(appointment);
  }
}

Then modify CalendarComponent so it subscribes to the appointment-emitting service and reloads the appointment list when it knows a new appointment has been saved.

// src/app/calendar/calendar.component.ts

import { Component, OnInit } from '@angular/core';
import { AppointmentService } from './appointment.service';

@Component({
  selector: 'cadence-calendar',
  templateUrl: './calendar.component.html',
  styleUrls: ['./calendar.component.css']
})
export class CalendarComponent implements OnInit {
  appointments = [];

  constructor(private appointmentService: AppointmentService) {
    appointmentService.appointmentCreated$.subscribe(appointment => {
      this.loadAppointments();
    });
  }

  loadAppointments() {
    this.appointments = this.appointmentService.getList();
  }

  ngOnInit() {
    this.loadAppointments();
  }
}

Now when you save an appointment it will show up immediately.

Making it look nicer

It would be nice if the appointment form actually cleared itself when we save an appointment. We can do this by calling reset() on the form when the submit button is clicked.

<!-- src/app/calendar/new-appointment/new-appointment.component.html -->

<form #appointmentForm="ngForm" (submit)="save(); appointmentForm.reset()">

  <div class="form-group">
    <label for="date">Date</label>
    <input type="text"
           class="form-control"
           name="date"
           id="date"
           [(ngModel)]="model.date">
  </div>

  <div class="form-group">
    <label for="clientName">Client name</label>
    <input type="text"
           class="form-control"
           name="clientName"
           id="clientName"
           [(ngModel)]="model.clientName">
  </div>

  <input type="submit" class="btn btn-primary" value="Save">
</form>

A little padding would also help.

/* src/styles.css */

body {
  padding-top: 30px;
}

Lastly, we’ll give the appointment form a containing gray box. First we’ll give it a class name.

<!-- src/app/calendar/new-appointment/new-appointment.component.html -->

<div class="new-appointment-form-container">
  <form #appointmentForm="ngForm" (submit)="save(); appointmentForm.reset()">

    <div class="form-group">
      <label for="date">Date</label>
      <input type="text"
             class="form-control"
             name="date"
             id="date"
             [(ngModel)]="model.date">
    </div>

    <div class="form-group">
      <label for="clientName">Client name</label>
      <input type="text"
             class="form-control"
             name="clientName"
             id="clientName"
             [(ngModel)]="model.clientName">
    </div>

    <input type="submit" class="btn btn-primary" value="Save">
  </form>
</div>

Then we’ll add the styles.

/* src/app/calendar/new-appointment/new-appointment.component.css */

.new-appointment-form-container {
  background-color: #DDD;
  padding: 30px;
}

Then we’ll change the markup of the calendar page to make it look a little nicer.

<!-- src/app/calendar/calendar.component.html -->

<div class="row">
  <div class="col-lg-6">
    <cadence-new-appointment></cadence-new-appointment>
  </div>

  <div class="col-lg-6">
    <div *ngFor="let appointment of appointments">
      <hr>
      {{ appointment.date }}
      {{ appointment.clientName }}
    </div>
  </div>
</div>

Your application should now roughly match the screenshot below.

How to Wire Up Ruby on Rails and AngularJS as a Single-Page Application (Gulp Version)

Important note: This tutorial no longer works if followed verbatim. I would recommend my more recent updated-for-2016 version.

Why this tutorial exists

I wrote this tutorial because I had a pretty tough time getting Rails and Angular to talk to each other as an SPA. The best resource I could find out there was Ari Lerner’s Riding Rails with AngularJS. I did find that book very helpful and I thought it was really well-done, but it seems to be a little bit out-of-date by now and I couldn’t just plug in its code and have everything work. I had to do a lot of extra Googling and head-scratching to get all the way there. This tutorial is meant to be a supplement to Ari’s book, not a replacement for it. I definitely recommend buying the book because it really is very helpful.

Refreshed for 2015

I had written a tutorial with almost the same title last summer. In that tutorial I used Grunt instead of Gulp, HTML instead of HAML or Jade, and regular JavaScript instead of CoffeeScript or ES6. Not only do cooler alternatives to those traditional technologies exist today, they did back then, too. My tutorial was sorely in need of a reboot. So here it is.

The sample app

There’s a certain sample app I plan to use throughout AngularOnRails.com called Lunch Hub. The idea with Lunch Hub is that office workers can announce in the AM where they’d like to go for lunch rather than deciding as they gather around the door and waste half their lunch break. Since Lunch Hub is a real project with its own actual production code, I use a different project here called “Fake Lunch Hub.” You can see the Fake Lunch Hub repo here.

Setting up our Rails project

Instead of regular Rails we’re going to use Rails::API. I’ve tried to do Angular projects with full-blown Rails, but I end up with a bunch of unused views, which feels weird. First, if you haven’t already, install Rails::API.

$ gem install rails-api

Creating a new Rails::API project works the same as creating a regular Rails project.

$ rails-api new fake_lunch_hub -T -d postgresql

Get into our project directory.

$ cd fake_lunch_hub

Create our PostgreSQL user.

$ createuser -P -s -e fake_lunch_hub

Create the database.

$ rake db:create

Now we’ll create a resource so we have something to look at through our AngularJS app. (This might be a good time to commit this project to version control.)

Creating our first resource

Add gem 'rspec-rails' to your Gemfile (in the test group) and run:

$ bundle install
$ rails g rspec:install

When you generate scaffolds from now on, RSpec will want to create all kinds of spec files for you automatically, including some kinds of specs (like view specs) that in my opinion are kind of nutty and really shouldn’t be there. We can tell RSpec not to create these spec files:

require File.expand_path('../boot', __FILE__)

# Pick the frameworks you want:
require "active_model/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module FakeLunchHub
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    config.generators do |g|
      g.test_framework :rspec,
        fixtures: false,
        view_specs: false,
        helper_specs: false,
        routing_specs: false,
        request_specs: false,
        controller_specs: true
    end
  end
end

(Now might be another good time to make a commit.)

In Lunch Hub, I want everybody’s lunch announcements to be visible only to other people in the office where they work, not the whole world. And there’s actually a good chance a person might want to belong not only to a group tied to his or her current workplace, but perhaps a former workplace or totally arbitrary group of friends. So I decided to create the concept of a Group in Lunch Hub. Let’s create a Group resource that, for simplicity, has only one attribute: name.

$ rails g scaffold group name:string

Since groups have to have names, let’s set null: false in the migration. We’ll also include a uniqueness index.

class CreateGroups < ActiveRecord::Migration
  def change
    create_table :groups do |t|
      t.string :name, null: false

      t.timestamps
    end

    add_index :groups, :name, unique: true
  end
end
$ rake db:migrate

Now, if you run rails server and go to http://localhost:3000/groups, you should see empty brackets ([]). We actually want to be able to do http://localhost:3000/api/groups instead.

Rails.application.routes.draw do
  scope '/api' do
    resources :groups, except: [:new, :edit]
  end
end

At the risk of being annoying, I wanted to include a realistic level of testing in the tutorial, at least on the server side.

require 'rails_helper'

RSpec.describe Group, :type => :model do
  before do
    @group = Group.new(name: "Ben Franklin Labs")
  end

  subject { @group }

  describe "when name is not present" do
    before { @group.name = " " }
    it { should_not be_valid }
  end
end

To make this spec pass you’ll of course need to add a validation:

class Group < ActiveRecord::Base
  validates :name, presence: true
end

We also have to adjust the controller spec RSpec spit out for us because RSpec’s generators are evidently not yet fully compatible with Rails::API. The generated spec contains an example for the `new` action, even though we don’t have a `new` action. You can remove that example yourself or you can just copy and paste my whole file.

require 'rails_helper'

# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator.  If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails.  There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec.  Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.

RSpec.describe GroupsController, :type => :controller do

  # This should return the minimal set of attributes required to create a valid
  # Group. As you add validations to Group, be sure to
  # adjust the attributes here as well.
  let(:valid_attributes) {
    skip("Add a hash of attributes valid for your model")
  }

  let(:invalid_attributes) {
    skip("Add a hash of attributes invalid for your model")
  }

  # This should return the minimal set of values that should be in the session
  # in order to pass any filters (e.g. authentication) defined in
  # GroupsController. Be sure to keep this updated too.
  let(:valid_session) { {} }

  describe "GET index" do
    it "assigns all groups as @groups" do
      group = Group.create! valid_attributes
      get :index, {}, valid_session
      expect(assigns(:groups)).to eq([group])
    end
  end

  describe "GET show" do
    it "assigns the requested group as @group" do
      group = Group.create! valid_attributes
      get :show, {:id => group.to_param}, valid_session
      expect(assigns(:group)).to eq(group)
    end
  end

  describe "GET edit" do
    it "assigns the requested group as @group" do
      group = Group.create! valid_attributes
      get :edit, {:id => group.to_param}, valid_session
      expect(assigns(:group)).to eq(group)
    end
  end

  describe "POST create" do
    describe "with valid params" do
      it "creates a new Group" do
        expect {
          post :create, {:group => valid_attributes}, valid_session
        }.to change(Group, :count).by(1)
      end

      it "assigns a newly created group as @group" do
        post :create, {:group => valid_attributes}, valid_session
        expect(assigns(:group)).to be_a(Group)
        expect(assigns(:group)).to be_persisted
      end

      it "redirects to the created group" do
        post :create, {:group => valid_attributes}, valid_session
        expect(response).to redirect_to(Group.last)
      end
    end

    describe "with invalid params" do
      it "assigns a newly created but unsaved group as @group" do
        post :create, {:group => invalid_attributes}, valid_session
        expect(assigns(:group)).to be_a_new(Group)
      end

      it "re-renders the 'new' template" do
        post :create, {:group => invalid_attributes}, valid_session
        expect(response).to render_template("new")
      end
    end
  end

  describe "PUT update" do
    describe "with valid params" do
      let(:new_attributes) {
        skip("Add a hash of attributes valid for your model")
      }

      it "updates the requested group" do
        group = Group.create! valid_attributes
        put :update, {:id => group.to_param, :group => new_attributes}, valid_session
        group.reload
        skip("Add assertions for updated state")
      end

      it "assigns the requested group as @group" do
        group = Group.create! valid_attributes
        put :update, {:id => group.to_param, :group => valid_attributes}, valid_session
        expect(assigns(:group)).to eq(group)
      end

      it "redirects to the group" do
        group = Group.create! valid_attributes
        put :update, {:id => group.to_param, :group => valid_attributes}, valid_session
        expect(response).to redirect_to(group)
      end
    end

    describe "with invalid params" do
      it "assigns the group as @group" do
        group = Group.create! valid_attributes
        put :update, {:id => group.to_param, :group => invalid_attributes}, valid_session
        expect(assigns(:group)).to eq(group)
      end

      it "re-renders the 'edit' template" do
        group = Group.create! valid_attributes
        put :update, {:id => group.to_param, :group => invalid_attributes}, valid_session
        expect(response).to render_template("edit")
      end
    end
  end

  describe "DELETE destroy" do
    it "destroys the requested group" do
      group = Group.create! valid_attributes
      expect {
        delete :destroy, {:id => group.to_param}, valid_session
      }.to change(Group, :count).by(-1)
    end

    it "redirects to the groups list" do
      group = Group.create! valid_attributes
      delete :destroy, {:id => group.to_param}, valid_session
      expect(response).to redirect_to(groups_url)
    end
  end

end

Now if you run all specs on the command line ($ rspec), they should all pass. We don’t have anything interesting to look at yet but our Rails API is now good to go.

Adding the client side

On the client side we’ll be using Yeoman, a front-end scaffolding tool. First, install Yeoman itself as well as generator-gulp-angular. (If you don’t already have npm installed, you’ll need to do that. If you’re using Mac OS with Homebrew, run brew install npm.)

$ npm install -g yo
$ npm install -g generator-gulp-angular

We’ll keep our client-side code in a directory called client. (This is an arbitrary naming choice and you could call it anything.)

$ mkdir client && cd $_

Now we’ll generate the Angular app itself. When I ran it, I made the following selections:

  • Angular version: 1.3.x
  • Modules: all
  • jQuery: 2.x
  • REST resource library: ngResource (just because it’s the default and angularjs-rails-resource isn’t an option on the list)
  • Router: UI Router
  • UI framework: Bootstrap
  • Bootstrap component implementation: Angular UI
  • CSS preprocessor: Sass (Node)
  • JS preprocessor: CoffeeScript
  • HTML template engine: Jade
$ yo gulp-angular fake_lunch_hub

If you have a Rails server running on port 3000, stop it for now because Gulp will also run on port 3000 by default. Start Gulp to see if it works:

$ gulp serve

Gulp should now open a new browser tab for you at http://localhost:3000/#/ where you see the “‘Allo, ‘Allo” thing. Our Angular app is now in place. It still doesn’t know how to talk to Rails, so we still have to make that part work.

Setting up a proxy

The only way our front-end app (Angular and friends) will know about our back-end server (Rails) is if we tell our front-end app about our back-end app. The basic idea is that we want to tell our front-end app to send any requests to `http://our-front-end-app/api/whatever` to `http://our-rails-server/api/whatever`. Let’s do that now.

If you look inside `client/gulp`, you’ll notice there’s a file in there called `proxy.js`. I would like to have simply tweaked this file slightly to get our proxy working, but unfortunately I found `proxy.js` very confusing and difficult to work with. So I deleted it and set up the proxy a different way. Let’s delete `proxy.js` so it doesn’t confuse future maintainers.

$ rm gulp/proxy.js

You’ll notice another file inside `client/gulp` called `server.js`. I found that minimal adjustment in this file was necessary in order to get the proxy working. Here’s what my `server.js` looks like after my modifications, which I’ll explain:

'use strict';

var gulp = require('gulp');
var browserSync = require('browser-sync');
var browserSyncSpa = require('browser-sync-spa');
var util = require('util');
var proxyMiddleware = require('http-proxy-middleware');
var exec = require('child_process').exec;

module.exports = function(options) {

  function browserSyncInit(baseDir, browser) {
    browser = browser === undefined ? 'default' : browser;

    var routes = null;
    if(baseDir === options.src || (util.isArray(baseDir) && baseDir.indexOf(options.src) !== -1)) {
      routes = {
        '/bower_components': 'bower_components'
      };
    }

    var server = {
      baseDir: baseDir,
      routes: routes,
      middleware: [
        proxyMiddleware('/api', { target: 'http://localhost:3000' })
      ]
    };

    browserSync.instance = browserSync.init({
      port: 9000,
      startPath: '/',
      server: server,
      browser: browser
    });
  }

  browserSync.use(browserSyncSpa({
    selector: '[ng-app]'// Only needed for angular apps
  }));

  gulp.task('rails', function() {
    exec("rails server");
  });

  gulp.task('serve', ['watch'], function () {
    browserSyncInit([options.tmp + '/serve', options.src]);
  });

  gulp.task('serve:full-stack', ['rails', 'serve']);

  gulp.task('serve:dist', ['build'], function () {
    browserSyncInit(options.dist);
  });

  gulp.task('serve:e2e', ['inject'], function () {
    browserSyncInit([options.tmp + '/serve', options.src], []);
  });

  gulp.task('serve:e2e-dist', ['build'], function () {
    browserSyncInit(options.dist, []);
  });
};

Here are the things I changed, it no particular order:

  1. Configured BrowserSync to run on port 9000 so Rails can run on its default port of 3000 without conflicts
  2. Added middleware that says “send requests to `/api` to `http://localhost:3000`”
  3. Added a `rails` task that simply invokes the `rails server` command
  4. Added a `serve:full-stack` task that runs the regular old `serve` task, but first runs the `rails` task

You’ll have to install `http-proxy-middleware` before continuing:

$ npm install --save-dev http-proxy-middleware

Now we can run our cool new task. Make sure neither Rails nor Gulp is already running somewhere.

$ gulp serve:full-stack

Three things should now happen:

  1. The front-end server should come up on port 9000 instead of 3000.
  2. If you navigate to `http://localhost:9000/api/foo`, you should get a Rails page that says `No route matches [GET] “/api/foo”`, which means
  3. Rails is running on port 3000.

Getting Rails data to show up in our client app

Now we’ll want to get some actual data to show up in the actual HTML of our Angular app. This is a pretty easy step now that we have the plumbing taken care of. First, create some seed data:

# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
#
# Examples:
#
#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
#   Mayor.create(name: 'Emanuel', city: cities.first)

Group.create([
  { name: 'Ben Franklin Labs' },
  { name: 'Snip Salon Software' },
  { name: 'GloboChem' },
  { name: 'TechCorp' },
])

Get the data into the database:

$ rake db:seed

Now let’s modify `src/app/index.coffee` to include a state for groups:

angular.module 'fakeLunchHub', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ngResource', 'ui.router', 'ui.bootstrap']
  .config ($stateProvider, $urlRouterProvider) ->
    $stateProvider
      .state "home",
        url: "/",
        templateUrl: "app/main/main.html",
        controller: "MainCtrl"
      .state "groups",
        url: "/groups",
        templateUrl: "app/views/groups.html",
        controller: "GroupsCtrl"

    $urlRouterProvider.otherwise '/'

Then we add `GroupsCtrl`, which at this point is almost nothing:

angular.module "fakeLunchHub"
  .controller "GroupsCtrl", ($scope) ->

(I manually created a new directory for this, `src/app/controllers`.)

Lastly, let’s create a view at `src/app/views/groups.jade`:

div.container
  div(ng-include="'components/navbar/navbar.html'")
  h1 Groups

If you now navigate to `http://localhost:9000/#/groups`, you should see a big `h1` that says “Groups”. So far we’re not talking to Rails at all yet. That’s the very next step.

A good library for Angular/Rails resources is called, straightforwardly, angularjs-rails-resource. It can be installed thusly:

$ bower install --save angularjs-rails-resource

Now let’s add two things to `src/app/index.coffee`: the `rails` module and a resource called `Group`.

angular.module 'fakeLunchHub', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ngResource', 'ui.router', 'ui.bootstrap', 'rails']
  .config ($stateProvider, $urlRouterProvider) ->
    $stateProvider
      .state "home",
        url: "/",
        templateUrl: "app/main/main.html",
        controller: "MainCtrl"
      .state "groups",
        url: "/groups",
        templateUrl: "app/views/groups.html",
        controller: "GroupsCtrl"

    $urlRouterProvider.otherwise '/'

  .factory "Group", (RailsResource) ->
    class Group extends RailsResource
      @configure url: "/api/groups", name: "group"

Now let’s add a line to our controller to make the HTTP request:

angular.module "fakeLunchHub"
  .controller "GroupsCtrl", ($scope, Group) ->
    Group.query().then (groups) -> $scope.groups = groups

And some code in our template to show the group names:

div.container
  div(ng-include="'components/navbar/navbar.html'")
  h1 Groups

  ul(ng-repeat="group in groups")
    li {{ group.name }}

If you now visit `http://localhost:9000/#/groups`, you should see your group names there. Congratulations! You just wrote a single-page application. It’s a trivial and useless single-page application, but you’re off to a good start. In my experience the plumbing is the hardest part.

That’s all for now

I’ve heard requests for tutorials on basic CRUD operations in Angular/Rails, so keep your eye out for that in the near future. You can subscribe to my posts by leaving your email in the upper right corner.

Also, if you enjoyed this tutorial, you may also like my webinar version of this same tutorial (and then some). Thanks for reading.