Skip to content

Instantly share code, notes, and snippets.

@bsaf
Last active August 29, 2015 14:05
Show Gist options
  • Save bsaf/08e1ab835c87d3fd03dc to your computer and use it in GitHub Desktop.
Save bsaf/08e1ab835c87d3fd03dc to your computer and use it in GitHub Desktop.
Ember app with Rails backend

Ember app with Rails backend

Rails app setup

This will create the Rails app and set up an API that the Ember app can consume.

Create the Rails app

$ rails new myapp --skip-bundle

Add some things to the Gemfile

# Specific to this task
gem 'ember-rails'
gem 'active_model_serializers'
gem 'coffee-rails'
gem 'pg'

Generate the Ember app structure (using CoffeeScript)

$ rails g ember:bootstrap -g --javascript-engine coffee

Setup the database

in config/database.yml

development:
  adapter: postgresql
  encoding: unicode
  database: myapp_development
  username: USERNAME
  password: 
  host: localhost

test:
  adapter: postgresql
  encoding: unicode
  database: myapp_test
  pool: 5
  username: USERNAME
  password: 
  host: localhost

Then run:

$ rake db:create

Add an API

in config/routes.rb

namespace :api, path: '/api' do
  resources :tasks
end

in initializers/inflections.rb

ActiveSupport::Inflector.inflections(:en) do |inflect|
   inflect.acronym 'API'
end

add controllers/api/tasks_controller.rb

module API
  class TasksController < ApplicationController
  
    def index
      tasks = Task.all
      render json: tasks, status: 200
    end
    
  end
end

create the Task model

$ rails g model Task title:string completed:boolean
$ rake db:migrate

Check it works so far

Add a couple of tasks using rails console:

Task.create!(title: "Task 1"); Task.create!(title: "Task 2")

Start the server with rails s, you should be able to visit http://localhost:3000/api/tasks and see:

{"tasks":[{"tasks":{"id":5,"title":"Task 1","completed":null,"created_at":"2014-08-10T08:58:02.901Z","updated_at":"2014-08-10T08:58:02.901Z"}},{"tasks":{"id":6,"title":"Task 2","completed":null,"created_at":"2014-08-10T08:58:02.905Z","updated_at":"2014-08-10T08:58:02.905Z"}}]}

Create the site index page

in config/routes.rb

root 'site#index'

add controllers/site_controller.rb

class SiteController < ApplicationController

  def index
  end

end

add views/site/index.html.erb

<h1>Tasks app</h1>

Generate a serializer for Task

$ rails g serializer task

in serializers/task_serializer.rb

class TaskSerializer < ActiveModel::Serializer
  attributes :id, :title, :completed
end

Restart the Rails server to see the effect.

Ember

Setup

delete javascripts/application.js

$ rm app/assets/javascripts/application.js

add correct namespace to javascripts/store.js.coffee

Myapp.ApplicationAdapter = DS.ActiveModelAdapter.extend({
  namespace: 'api'
})

Add an index route

in javascripts/router.js.coffee

Myapp.Router.map ()->
  @resource 'tasks', path: '/'

add javascripts/routes/tasks_route.coffee

Myapp.TasksRoute = Ember.Route.extend
  model: ->
    return this.store.find('task')

add javascripts/models/task_model.coffee

Myapp.Task = DS.Model.extend
  title:     DS.attr 'string'
  completed: DS.attr 'boolean'

add javascripts/templates/tasks.handlebars

<ul>
{{#each}}
  <li>
    {{title}}
  </li>
{{/each}}
</ul>

Check it works

Restart your server and visit http://localhost:3000/ and you should see the tasks.

Add a nested route

update javascripts/router.js.coffee

Myapp.Router.map ()->
  @resource 'tasks', path: '/', ->
    @resource 'task', path: '/task/:task_id'

update javascripts/templates/tasks.handlebars to add a link-to and an outlet

<ul>
{{#each}}
  <li>
    {{#link-to 'task' this}}{{title}}{{/link-to}}
  </li>
{{/each}}
</ul>

{{outlet}}

add javascripts/templates/task.handlebars

<h2>{{title}}</h2>

Note on the outlet

If you click a task link, it should show up below, where the outlet is. If you want the task to show up on its own page, just un-nest the resource and the entire template will be replaced when you click the link.

Testing

Add rspec and spring

in Gemfile

group :development do
  gem 'spring'
  gem 'spring-commands-rspec'
end

group :development, :test do
  gem 'rspec-rails', '~> 3.0.0'
  gem 'capybara'
  gem 'poltergeist'
end

install the gems and spring binstubs

$ bundle install && bundle exec spring binstub --all && spring stop

install rspec

$ rails generate rspec:install

check spring is running

$ bin/rspec
$ spring status

Tests should run faster now.

API tests

in spec/spec_helper.rb

def json(body)
  JSON.parse(body, symbolize_names: true)
end

in spec/api/tasks_spec.rb

require "rails_helper"

RSpec.describe "the tasks API", :type => :request do

  describe "listing tasks" do

    it "responds successfully with an HTTP 200 status code" do
      get '/api/tasks'
      expect(response).to be_success
      expect(response).to have_http_status(200)
    end

    it "returns the tasks" do
      Task.create!(title: "Task 1", description: "Task 1 desc")
      Task.create!(title: "Task 2", description: "Task 2 desc")

      get '/api/tasks'

      tasks = json(response.body)[:tasks]
      task_titles = tasks.collect { |task| task[:name] }

      expect(task_titles).to include("Task 1")
      expect(task_titles).to include("Task 2")
    end
  end

end

Integration tests

in spec/spec_helper.rb

require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist

add spec/integration/tasks_index_spec.rb

require "rails_helper"

RSpec.describe "the list of tasks", :type => :feature do

  describe "viewing the list", js: true do

    it "shows the tasks" do
      Task.create!(title: "Task 1", description: "Task 1 desc")
      Task.create!(title: "Task 2", description: "Task 2 desc")

      visit '/'

      expect(page).to have_text("Task 1")
      expect(page).to have_text("Task 2")
    end
  end

end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment