Using Rails & Devise with Ember.js

December 2013: The code has been updated to work with the latest versions of Ember and Ember Data.

Using Devise with Ember.js is pretty easy. Basically, what we need to do is store the current user's data somewhere in HTML to avoid sending an ajax request when our app starts.

Note, this article assumes you've already integrated Ember.js into your Rails app. If not, there is a great series of articles by Brian Cardarella: Building an Ember app with RailsAPI

So, let's start. A meta tag seems the perfect place for putting the current user's data.

<!DOCTYPE html>
<html>
<head>
  <title>Title</title>
  <%= stylesheet_link_tag    "application", media: "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>

  <%- if user_signed_in? %>
    <meta name="current-user" content="<%= UserSerializer.new(current_user).to_json(root: false) %>" />
  <% end %>

</head>
<body>
</body>
</html>

Using UserSerializer right in the view is not a very good practice but in our case (very simple server side layout) it will work.

Next, we need to create CurrentUserController with an isSignedIn property.

App.CurrentUserController = Ember.ObjectController.extend
  isSignedIn: (->
    @get('content') != null
  ).property('content')

Then we need to create an initializer that will actually parse the data from the dom put it into CurrentUserController. We can do it in /initializers/currentUser.js.coffee:

Ember.Application.initializer
  name: 'currentUser'

  initialize: (container) ->
    store = container.lookup('store:main')
    attributes = $('meta[name="current-user"]').attr('content')

    if attributes
      user = store.push('user', store.serializerFor(App.User).normalize(App.User, JSON.parse(attributes)))
      controller = container.lookup('controller:currentUser').set('content', user)

      container.typeInjection('controller', 'currentUser', 'controller:currentUser')

What we do here is create an instance of User and then put it into the content variable of CurrentUserController. We then perform typeInjection on all the controllers in our app to give them currentUser variable so that we don't need to set it manually.

Now, you can access currentUser in any template without needing to set it manually in your controller's needs:

<header>
  <h1>
    {{#if currentUser.isSignedIn}}
      Hi, {{currentUser.name}}
    {{else}}
      Hello, Guest
    {{/if}}
  </h1>
</header>

If you do not want to inject currentUser into every controller out there, here's what you can do:

First, remove this line from the initializer:

container.typeInjection('controller', 'currentUser', 'controller:currentUser')

Then, whenever you need the current user, use needs:

App.ApplicationController = Ember.Controller.extend
  needs: ['currentUser']

It will be available in the template as controllers.currentUser:

{{#if controllers.currentUser.isSignedIn}}
  Hi, {{controllers.currentUser.name}}
{{else}}
  Hi, Guest
{{/if}}

You're the 34537th person to read this article.