Hashrocket.com / blog

Setting up an Ember App with a Rails Backend

posted on and written by in

Image 100x100 vic ramon

Update: June 26, 2014

This tutorial is now out of date, and you should instead look at Vic Ramon's Ember Tutorial for a tutorial introducing Ember with Rails.

Update: November 8th, 2013

Some of the instructions below are out of date, particularly the code in the store.js file. Please look at the Ember Data Rails Example that I have posted to Github for correct, updated code. This app can be viewed live on Heroku.


Today I'm going to show you how to setup an Ember app with a Rails backend. The process is relatively straightforward, but . there are some gotchas that I'd like to help you avoid.

The app will use Haml, CoffeeScript, Emblem, and Ember Data. In this tutorial I'll be starting a sample app called Launch Bay. Launch Bay would essentially be an ultra-lightweight version of Pivotal Tracker. In this first post I'm just going to get my app setup to receive data.

Basic App Setup

Let's hit the ground running with a new app

rails new launchbay  -d postgresql

Setup your database and rvm gemset, and make sure to include these in your Gemfile:

gem 'coffee-rails', '~> 4.0.0'
gem 'ember-rails'
gem 'ember-source'
gem 'emblem-rails'
gem 'haml-rails'

Open your configs and add the following:

# environments/production.rb
config.ember.variant = :production
# environments/development.rb
config.ember.variant = :development
# environments/test.rb
config.ember.variant = :development

Backend Setup

Ok, now we've got the basics we need to get our Rails backend up and running. Our app is just going to show a list of stories. For the sake of simplicity these won't be scoped to a particular user or project.

Let's create a stories table.

rails g migration create_stories name:string body:text
rake db:migrate

We'll also need a story model, controller, and serializer.

# app/models/story.rb

class Story < ActiveRecord::Base
end
# app/serializers/story_serializer.rb

class StorySerializer < ActiveModel::Serializer
  attributes :name, :body
end

I'm going to create a versioned api for the controller just in case I want to change things later:

# config/routes.rb

namespace :api do
  namespace :v1 do
      resources :stories, only: :index
  end
end

This controller is going to accept json so that we can interact with our Ember app.

 # app/controllers/api/v1/stories_controller.rb

class Api::V1::StoriesController < ApplicationController
  respond_to :json

  def index
    respond_with Story.all
  end

  private

  def story_params
    params.require(:story).permit(:name, :body)
  end
end

To see if this is all setup properly spin up your server and open a rails console. First let's seed the database:

$ rails console
$ Story.create(name: 'User views a list of stories', body: 'Given I am a user <br /> When I visit the stories index <br /> Then I should see a list of stories')

Now hit the api controller and you should get your one story back as json: http://localhost:3000/api/v1/stories.json

Providing an Outlet for Ember

We need to have a place for our Ember app to actually show up, so let's do that now. I am going to create a generic home controller for now.

# config/routes.rb

root to: 'home#index'
# app/controllers/home_controller.rb

class HomeController < ApplicationController
end

Now give Ember an outlet in the view:

# app/views/home/index.html.haml

%script{ type: 'text/x-handlebars' }
  {{ outlet }}

We also need a Rails layout:

# app/views/application.html.haml

!!!
%html(lang="en-US")
  %head
    %title Launch Bay
    = stylesheet_link_tag "application"
    = javascript_include_tag "application"
    = csrf_meta_tags
  %body
    = yield

Setting up Ember Internals

First, run the generator provided by ember-rails:

rails g ember:bootstrap -g --javascript-engine coffee -n App

Now restart your Rails server and hit localhost:3000. You should see a blank page. Open your developer console and you should see output like this:

DEBUG: ------------------------------- 
DEBUG: Ember.VERSION : 1.0.0
DEBUG: Handlebars.VERSION : 1.0.0 
DEBUG: jQuery.VERSION : 1.10.2 
DEBUG: ------------------------------- 

For more advanced debugging, check out the Ember Inspector for Google Chrome

Setting Up Ember Data

We'll be using Ember Data to handle client-server communication.

The Ember-rails generator created a store.js file. Open it and remove everything, and just add the following:

# app/javascripts/store.js

DS.RESTAdapter.reopen
  namespace: 'api/v1'

Ember Models

Now to create the actual story model:

# app/assets/javascripts/models/story.js.coffee

App.Story = DS.Model.extend
  name: DS.attr('string')
  body: DS.attr('string')

Ember Routes

app/assets/javascripts/router.js contains our top-level routes.

Open that file, change it to Coffeescript, then add this:

# app/assets/javascripts/router.js.coffee

App.Router.map ()->
  @resource 'stories'

Note that resource is singular, in contrast to Rails.

Ember Template

Create the following file:

# app/assets/javascripts/templates/stories.js.emblem

| Hello world.

Save that, now head over to http://localhost:3000/#/stories. You should see your message on the page.

There a few things to note.

  1. Use a pipe to tell emblem that "Hello" is text and not an html tag.

  2. We created a file named stories in the templates folder. This works right now because the stories route has no subroutes. If you added a subroute to the stories resource, then this template would need to be moved to templates/stories/index.emblem.js.

  3. We didn't need to create a stories controller or stories route to make this work. Ember will create those in memory for us if we don't explicitly create them.

Pulling in real data

Ok, we've got text showing up on the page, but we need to actually get data out of our database.To do that we need to bind data to the route, and we do that by creating a stories route.

# app/assets/javascripts/routes/stories.js.coffee

App.StoriesRoute = Ember.Route.extend
  model: ->
    @get('store').findAll('story')

Open the template back up and add the following:

# app/assets/javascripts/templates/stories.js.emblem

h1 Story Listing

= each story in controller
  h2= story.name
  | {{{story.body}}} 

Open up http://localhost:3000/#/stories and you should see the story you put in the database.

I'm doing {{{story.body}}} to prevent the html line breaks from being escaped.

Notice that I am referring to the array of stories in the template as controller. That's because the data is bound do the Stories controller (which is still in memory).

Wrap Up

That's it for app setup with Rails, Ember, and Ember Data. If you were to continue with this project the next step would be to get project models setup, then nest stories under projects in the Ember router. I hope this was helpful.

Posted in Development and tagged with Javascript, Ember