After many complex calculations, grueling hours of gathering evidence, and some experience running Ember in production, I have determined that Ember is not Rails.

Many concepts in Ember have identically named concepts in Rails, even after learning Ember basics it took me far longer to rid myself of design assumptions from my time spent in Rails. This post will describe a few problems when switching from Rails to Ember development, and discuss a few patterns that are emergent from distinguishing things conceptually. Take a moment, call up Brad your drug dealer, tell him you're gonna need the heavy stuff because you're about to free your mind from Rails.

Routing

Routes exist in both Ember and Rails. Ember routes are used to decide what information to display to the user. Rails routes are generally used RESTfully. In Rails a user may have 6 different routes for corresponding CRUD. When you crack open your first Ember app you may be tempted to write something like this

Router.map(function() {  
 this.route('user', {
   this.route('show')
   this.route('index')
   this.route('edit')
   this.route('update')
   this.route('destroy') 
 });
});

This routing is excessive in Ember. If we want to delete a user, we don't need to make a new route. We only need to somehow trigger an action that causes the record to be deleted. The entire CRUD workflow outlined above may all be contained in a single route in Ember.

Router.map(function() {  
 this.route(‘users’);
});

Your main concern with routing in Ember should be how to make the URL correspond most intuitively with the UI that needs to be displayed. The user of our application shouldn't need to know about the implementation of our API. A new user route which in Rails might be user/new should in Ember be create_an_account or new_user, or more likely some much smarter name that you come up with. The point is, the decision about how to create your routes in Ember should be centered around what is most understandable to the user of your application, not necessarily what is RESTful.

Design

When I build features in Rails I try to pull out abstractions in the form of classes and modules. The idea being that the correct abstraction will make things more readable, composable, and testable. I ran into trouble when trying to apply my ideas about design in Rails to design in Ember. I wrongly assumed that abstraction through classes would continue to be my primary tool for avoiding complexity.

In the front, we also want behavior to be encapsulated in small chunks that are easy to test. Ember provides you an awesome tool for doing just that, a component. If you find a particularly messy and hard to understand bit of UI, you are often served well by breaking that behavior into smaller components. This realization allowed me repurpose my design instincts in Rails and greatly reduced the number of pejorative haikus I receive during pull request.

Models

Both Rails and Ember have the concept of models. In Rails, ActiveRecord models are used for persisting database records, and each ActiveRecord model instance corresponds to a single record.

class User < ActiveRecord::Base  
end  

When I first began working with Ember, I mentally mapped ember-data models onto ActiveRecord models.

// models/user.js
​
import DS from ‘ember-data’;  
​
export default DS.Model.extend({});  

This mode of thinking breaks down when your API exposes endpoints designed for tasks other than persistence. For example, let's say we have an API endpoint for persisting users, but we also have an endpoint for looking up "account issues" for a given user.

POST /users # creates a new user  
GET /users/1/account_issues # get a list of user account issues  

We'll never persist account issues (they're purely informational) and they are not backed by database records (they're dynamically calculated using, let's say, a user's Facebook social graph). We can model the API like this:

// models/user.js
import DS from ‘ember-data’;  
​
export default DS.Model.extend({  
 issues: DS.hasMany('issues', { async: true })
});
​
// models/issue.js
import DS from ‘ember-data’;  
​
export default DS.Model.extend({  
 user:        DS.belongsTo('user', { async: true }),
 description: DS.attr('string')
});

We can then retrieve a user's issues like this:

model: function(){  
  let user = this.store.find('user', 1);
  return user.get(‘issues’);
}

Using the ember-data store to retrieve issues allows us to limit boilerplate, create strong domain concepts in our Ember application, and break up expensive DB queries into async fetches.

Understanding that Ember and Rails are significantly different is an important step to becoming an effective Ember dev. You can find more information on how to move from Rails to Ember at the fromrailstoember website.

Tags: