Ember example for Rails developers

In response to http://stackoverflow.com/questions/15913324/what-paradigm-shifts-in-thinking-should-a-rails-or-server-side-developer-make-wh

HTML

<script src="ruby on rails"></script>
<script type="text/x-handlebars" data-template-name="application">
  <h1>Ember example for Rails developers</h1>

  {{outlet}}

  <p class="details">See <a href="http://stackoverflow.com/questions/15913324/what-paradigm-shifts-in-thinking-should-a-rails-or-server-side-developer-make-wh">this StackOverflow post</a> for more details.</p>
</script>

<script type="text/x-handlebars" data-template-name="posts">
  <ul class="posts">
    {{#each controller}}
      <li>{{#linkTo 'post' this}}{{title}}{{/linkTo}}</li>
    {{/each}}
  </ul>
</script>

<script type="text/x-handlebars" data-template-name="post">
  <h2>{{title}}</h2>

  <div class="body">{{body}}</div>

  {{#if lowRatedCommentsShown}}
    <button {{action 'hideLowRatedComments'}}>Hide Low-Rated Comments</button>
  {{else}}
    <button {{action 'showLowRatedComments'}}>Show Low-Rated Comments</button>
  {{/if}}

  {{partial "comments"}}
</script>

<script type="text/x-handlebars" data-template-name="_comments">
  <ul class="commnts">
    {{#each comment in comments}}
      <!-- Messy logic that would be a great example of when to use
           non-singleton controllers, which is an advanced topic. -->
      {{#if comment.isLowRated}}
        {{#if lowRatedCommentsShown}}
          <li>{{comment.body}}</li>
        {{/if}}
      {{else}}
        <li>{{comment.body}}</li>
      {{/if}}
  {{/each}}
</script>

CSS

.body {
    margin-bottom: 16px;
}

.details {
    color: darkgrey;
    font-size: 80%;
}

JavaScript

window.App = Ember.Application.create();

App.Store = DS.Store.extend({
    revision: 12,
    adapter: DS.FixtureAdapter.create()
});

App.Post = DS.Model.extend({
    title: DS.attr("string"),
    body: DS.attr("string"),
    comments: DS.hasMany("App.Comment")
});

App.Comment = DS.Model.extend({
    post: DS.belongsTo("App.Post"),
    body: DS.attr("string"),
    rating: DS.attr("number"),
    
    isLowRated: function () {
        return this.get("rating") < 2;
    }.property("rating")
});

App.Post.FIXTURES = [{
    id: 1,
    title: "Post #1",
    body: "Sample post body.",
    comments: [1, 2]
}, {
    id: 2,
    title: "Post #2",
    body: "Sample post body.",
    comments: []
}];

App.Comment.FIXTURES = [{
    id: 1,
    post: 1,
    body: "First!",
    rating: 1
}, {
    id: 2,
    post: 1,
    body: "Good point, but you need to double-check that claim in paragraph 2.",
    rating: 5
}];

App.Router.map(function () {
  this.route("index", { path: "/" });
  this.route("posts", { path: "posts" });
  this.route("post", { path: "post/:post_id" });
});

App.IndexRoute = Ember.Route.extend({
  redirect: function () {
    this.replaceWith("posts");
  }
});

App.PostsRoute = Ember.Route.extend({
    model: function (params) {
      return App.Post.find();  
    }
});

App.PostRoute = Ember.Route.extend({
    model: function (params) {
      return App.Post.find(params.post_id);  
    }
});

App.PostController = Ember.ArrayController.extend();

App.PostController = Ember.ObjectController.extend({
  // This shared between all posts for as long as the app runs (because
  // this controller is a singleton), but it doesn't get saved in the database
  // (because this is a controller, not a model).
  lowRatedCommentsShown: false,

  // Called by PostView or its template in response to an HTML event.
  showLowRatedComments: function () {
    this.set("lowRatedCommentsShown", true);
  },

  // Called by PostView or its template in response to an HTML event.
 ...