Building Single Page Web Applications With Ruby On Rails

Building Single Page Web Applications With Ruby On Rails

BUILDING SINGLE-PAGE WEB APPLICATIONS WITH RUBY ON RAILS

What are single-page apps?

The pursuit of better user experience changes the way we think about browsing the web. As the Internet matures, we abandon the classic approach whereby the user is presented with a handful of URLs which reload the page when clicked; the new approach aims to make the web application work seamlessly. This is achieved by retrieving all the code needed with a single-page load and, subsequently, changing only some elements on the page, depending on the actions taken by the user. Such an approach has a few benefits. It ensures a fine-grained control over the user experience as it eliminates network latencies and disruptions caused by page redraws. It can also save resources like bandwidth and CPU time on the server side.

There are various techniques which allow to retain a single-page during the client-server communication process. On a higher level of abstraction, the main difference between these techniques boils down to the opposition: “thick server architecture vs. thin server architecture” or “fat client vs. thin client”. A “fat client” is able to perform many tasks without accessing the server. In contrast, a thin client, generally, does as little processing as possible and depends on connecting to the server each time user input needs to be processed or validated.

There are certain advantages to making the client thin and delegating all the data processing, such as, for example, rendering appropriate HTML and/or JavaScript, to the server. For instance, it is easier to develop and deploy web applications where almost the entire code runs on the server. The disadvantage of frequent server access is that your thin-client single-page application is as fast as the network connection between the client and the server. So, just as the user experience was affected by page redraws, it is now limited by the request/response scheme. Having taken an action in the system, the user is presented with spinners or flash messages like “work in progress…”, rather than immediate results. Some say that single-page apps could take another step forward and make the interfaces completely non-blocking. The interactions should be resolved instantly, without spinners or “please wait…” messages affecting user experience. And decoupling network requests from the application interface is the only way to escape the “”jail” where “the web application is as fast as the network connection”.

Moving the application logic to the client would not be possible without the help of Javascript. Fortunately, there are a number of Javascript frameworks that let you pull some business logic from your Ruby on Rails application to the client side. But before I dive into reviewing Javascript frameworks that let you build great single-page applications with Ruby on Rails, I feel obliged to answer an important question:

Why Rails?

Rich and smart single-page applications require a backend to provide data validation, access control support, authentication and session management, data calculations and all the other support you would expect from a webservice. Rails has it all. ActiveRecord as an orm and validation module, Ruby gems like devise and cancan for authentication/authorization respectively, and you name it. Some of these solutions are baked-in in vanilla Rails, some are distributed as gems and plugins (just check the Ruby ToolBox for the available solutions, e.g. authentication, access control and orms ). Of course, a rich single-page application does not rely on a Ruby on Rails backend for rendering views / templates and sending back HTML documents. Instead it requires JSON objects which a Ruby on Rails application can deliver without sweat – let’s just mention Jbuilder and the other nifty ways of passing data to Javascript. I could dwell on the topic for long, showing how easy it is to use the Ruby on Rails framework to create applications meant to respond with JSON. Rather than do that, I would prefer you to use the links to access specific ready-made solutions which speak louder than words.

On the whole, with the approach, we move the business logic to the client. One might wonder if we really need all the backend functionalities. Unfortunately, we do. Since you do not have complete control over what your client application does, you cannot trust the client blindly. Hence, you will often find yourself duplicating the code on both the client and the server side (for example, validations).

The Javascript frameworks for building single-page web applications are usually back-end agnostic. In fact, you can leave the ins and outs of the data to the API provided by the application build with Ruby on Rails, Django, Node.js (the Express framework) or any other platform that suits you. Yet RoR with its flexibility and robustness does stand out among the other competitors. Still not convinced? Then let me add that a lot of MVC / MVVM Javascript frameworks for building single-page apps are inspired by the Ruby on Rails framework itself. To give an example, Yehuda Katz, a member of the Ruby on Rails core team, is championing one of the frameworks – Ember.js. Batman.js developers are advertising their framework as: “APIs are heavily inspired by Rails and designed to make Rails devs feel right at home”.

In the following sections of the article, I will focus on the Javascript frameworks for building single-page web applications and I will discuss them from a Ruby on Rails developer’s perspective. Javascript MVC/MVVM frameworks for building single-page web application with Ruby on Rails. There are a number of Javascript frameworks suitable for building single-page web applications. The ones that make Ruby on Rails developers feel at home might be:

  • Batman.js
  • Backbone.js
  • Ember.js

What about Knockout.js or Spine.js or yet another solution? I will comment on the other contenders that did not make it into my list of the prefered choices as well as try to explain why I chose to omit them.

To start with, what do we expect from a good Javascript MV* framework? Before I get to the point, the asteriks after “MV” is there to capture the two major philosophies behind Javascript frameworks: the Model-View-Controller and the Model View ViewModel paradigms. The best definition of the MVC pattern I have read is the one introduced by the Smalltalk’s inventors: Model-View-Controller “is the concept of encapsulating some data together with its processing (the model) and isolate it from the manipulation (the controller) and presentation (the view) part that has to be done on a UserInterface”.

The MVVM paradigm originates from MVC. The model and view layers are similar to the layers defined in MVC. Basically, the model layer holds the data while the view layer refers to the elements such as buttons, labels, and other controls displayed by the GUI.The view model can be described as a layer that fills the gap between the ‘real’ state of data in the model and the concept of displaying it in the view.

To clarify, let me furnish an example: there is an application which has a user profile page which displays a picture uploaded by the user. The avatar image information is stored in the User model. Your job as a developer is to write the ‘displaying’ code. Everything works well if the user has uploaded the avatar picture before. If he did not, you should display an image placeholder instead. Where do you place the code that determines whether to display an image or a placeholder? Such code does not fit into the model since it is view-related. You should not place it in the view as it is not an appropriate place for the logic. A helper method? Could work. But helper methods are just simple methods in a global namespace and you feel that something more specific to the model would be a better idea.

The example above pictures one of the problems an MVVM architectural design is trying to solve by introducing a ViewModel layer. The MVVM concept is barely present in in Ruby on Rails. The existing decorator / presenter solutions are often quite tightly coupled with the model layer. On the contrary, as defined by the MVVM pattern underlying conditions, the ViewModel should not necessarily be aware of where its data comes from.

Thus the considerations of MVC vs. MVVM were a factor behind the selection of specific solutions for the list compiled. Ruby on Rails developers are far more familiar with MVC rather than MVVM frameworks. It should be noted though, that the MVC implementation in JS frameworks is a bit different from its counterpart in Rails. For the purpose of this article, I will make use of TodoMVC – “a project which offers the same Todo application implemented using MV concepts in most of the popular JavaScript MV frameworks of today”. There are quite a few demos listed on the project page and you can find some relevant examples at the labs directory of the project on github.

Batman.js According to the creators, Ruby on Rails developers should feel at home with Batman.js. The reason for this might be the fact that Batman.js was extracted and shaped into a framework from an existing Ruby on Rails application: Shopify. Batman.js is a framework for building rich single-page browser applications. It is written in CoffeeScript and its API is developed with CoffeeScript in mind. Batman.js is suitable for running both in a browser and in Node.js ( v0.4.x and v0.6.x). As for the browser compatibility, Batman.js developers are targeting Chrome, Safari 4+, Firefox 3+, and IE 7+. The code samples from the project site should illustrate why a Ruby on Rails developer may easily fall in love with Batman.js.

I do feel at home. The feeling may very well be aroused by Coffeescript leveraging, which was inspired by the Ruby language. As regards the implementation of the Batman.js sample application in TodoMVC project, it is hard to decide what to choose: the original coffeescript file or the javascript conversion. The former reflects the spirit of the framework better and lends itself better to present the features. The js-converted version ensures consistency – all other examples are written in Javascript. However, the automatically converted code looks just ugly and is very hard to read. Since I consider using Coffeescript an advantage, I eventually decided to use the Coffeescript version.

The Todo app code is very well organized and the structure resembles the architecture of an RoR application: there are routes, model and controller sections. Below, you will find a Batman.js model example from the TodoMVC project. It is a Todo application class with a Rails-style looking definitions concerning encoding, persisting, validations and storageKey, as well as accessors definitions:

In the example above, we add a class name to a list element if the todo.completed binding evaluates to true. Other examples of handling complex behavior are data-mixin and data-event-* attributes. In this case, the data-event-change will attach respective event handlers to the specified node:

As you can see the computed properties allow you to treat a function definition as a regular property. What is more, the computed properties can have dependencies on other properties and if you list the dependencies properly, Ember.js makes all the binding updates when necessary. Neat!

If you would like to see how Ember.js can integrate with Rails, read the case-based articles by Zack Hubert :

Ember.js pros and cons

Pros:

  • Support for organizing views in a hierarchical order. Ember.View also allows you to encapsulate HTML templates, which enables you to use views composition techniques in your application (reusing templates).
  • A big part of Ember’s appeal comes from leveraging Handlebars.js declarative templates. After setting up the DOM (using helper methods like {{view}}, {{action}} and {{bindAttr}}), you no longer have to worry about keeping the DOM up to date.
  • Ember.js offers a lot of support for integration with Ruby on Rails and boosts developers productivity.
  • Very powerful implementation of bindings and computed properties.
  • Due to Ember.js design, it is easy to mix Ember.js with other Javascript libraries.

Cons:

  • Ember.js still exhibits limited adoption. Yet the project website features some respectable brand names, such as Zendesk or Square.
  • The Ember.js framework is in a state of flux as the framework has yet to hit 1.0. A lot of framework elements are changing and some of its parts are definitely not production ready, like the Ember Data module.
  • If I had written this article a few months ago, I would, without doubt, have added “Poor documentation” in the cons section. But Ember.js maintainers have recently refreshed the project website with some nice docs and guides. For all the effort, you can still find some undocumented, quirky parts in the Ember.js framework.
  • Ember’s view binding system implementation can sometimes be the cause of poor performance of a single page application.
  • Ember.js loses against batman.js and Backbone.js when it comes to size: 42k min+gzip.

Summary

The selection of a technology stack for single-page web applications can be executed in two steps:Choosing the right tool/framework for the back-end service / API,

Choosing the right library / framework for the client part of the application.

As for the first step, I presented the arguments why Ruby on Rails with its RESTful approach, decent JSON objects manipulation support and backend features is a decent choice. What about the client side of the single-page application? I have made an attempt to answer this question from the perspective of a Ruby on Rails developer. If your choice is Ruby on Rails, it is optimal to choose the Javascript framework that integrates well with REST services and follows the MVC pattern. And vice versa, if you have chosen a specific Javascript framework make sure the back-end technology is suitable for the framework. There are many other great JS frameworks for building rich client-side applications. I have, however, found them to be less suitable for Ruby on Rails. For example, it seems that Cappuccino and SproutCore 1.x force native-like paradigms, which lack a good presentation layer. Knockout.js does not provide the abstractions for async communications with the server nor does it play well with out-of-the-box REST services. Similarly, Spine.js is optimized more for local storage scenarios rather than for the integration with REST services. If you would like to share your experience concerning the integration of Ruby on Rails with the frameworks featured in this article (or those I chose to omit), feel free to post them in the comments section. I will be happy to hear from you.