A CurrentUser Mixin for Ember and Rails

A pretty common usecase in web apps is showing details about the currently logged in user. This kind of functionality is something that should be easily accessible in your Ember controllers if you need it. Here's a way to do just that using a Mixin.

LS.CurrentUserMixin = Ember.Mixin.create({  
  needs: ['application'],

  init: function () {
    this._super();

    // we only need to fetch and parse the current
    // user once, after that we store it on the
    // application controller
    if (!this._application().get('currentUser')) {
      this.store.pushPayload('user', { user: this_.payload().user });
      this._application().set('currentUser',
                              this.store.getById('user', this_.payload().user.id));
    }
  },

  currentUser: function () {
    return this._application().get('currentUser');
  }.property(),

  // private

  _application: function () {
    return this.get('controllers.application');
  },

  _payload: function () {
    return JSON.parse($('meta[itemprop="current-user"]').attr('content'));
    // you could also fetch this from and endpoint like users/me
  }
});

You can now use this in your controllers like so:

LS.UserIndexController = Ember.ObjectController.extend(  
  LS.CurrentUserMixin, {

  isCurrentUser: function () {
    return this.get('currentUser') === this.get('model');
  }.property('currentUser', 'model'),
});

And then in your handlebars templates like this:

<span class="avatar">  
  <img {{bind-attr src="currentUser.avatar_url"}}>
</span>  

As you might be able to tell, we're preloading the JSON representing our current user in our app by setting a meta tag in our application.html.erb. Here's how you can do just that:

<!DOCTYPE html>  
<html>  
  <head>
    <meta charset="utf-8">
    <!-- include javascripts etc --/>
    <% if current_user.authenticated? %>
      <meta itemprop="current-user" content="<%= UserSerializer.new(current_user).to_json %>">
    <% end %>
  </head>
</html>  

A note on testing

To be able to set any user you need in your tests we can create a little helper function:

function setCurrentUser (user) {  
  LS.CurrentUserMixin.reopen({
    init: function () {},

    currentUser: function () {
      return this.store.pushPayload('user', user);
    }.property()
  });
}

And now you can use this in your tests without needing any meta-tags containing JSON:

moduleFor('controller:chapter_index', 'Chapter Index Controller', {  
  needs: ["controller:application"],

  setup: function () {
    setCurrentUser(someUserObjectYouNeed);
  }
});