Rails and Backbone Working Together

rails and backbone working together

Before anything else I'd suggest taking a look at thoughtbot's Backbone.js on Rails book, which is a great starting point, although aimed at an intermediate to advanced audience. I bought this book having already worked with rails but as a total backbone.js beginner and it has served me very well.

Beyond that, there are some fundamental issues with combining these frameworks which go beyond the details covered in this book and other books. Below are some things I'd suggest you think about, from my own experiences pairing RoR and backbone.js. This is a long answer and strays a bit from the specifics of your question, but I hope it might help you out in the "big picture" sense of understanding the problem you're facing.

Rails: Web Framework vs API

The first thing you confront when using backbone.js on top of a rails application is what to do about views, but this is really just the surface of a much deeper issue. The problem goes to the very heart of what it means to create a RESTful web service.

Rails is set up out of the box to encourage its users to create RESTful services, by structuring routing in terms of a set of resources accessed at uniform URIs (defined in your routes.rb file) through standard HTTP actions. So if you have a Post model, you can:

  • Get all posts by sending GET request to /posts
  • Create a new post by sending a GET request to /posts/new, filling out the form and sending it (a POST request) to /posts
  • Update a post with id 123 by sending a GET request to /posts/123/edit, filling out the form and sending it (a PUT request) to posts/123
  • Destroy a post with id 123 by sending a DELETE request to /posts/123

The key thing to remember about this aspect of Rails is that it is fundamentally stateless: regardless of what I was doing previously, I can create a new Post simply by sending a POST request with a valid form data to the correct URI, say /posts. Of course there are caveats: I may need to be logged in (have a session cookie identifying me), but in essence Rails doesn't really care what I was doing before I sent that request. I could follow it up by updating another post, or by sending a valid action to whatever other resources are made available to me.

This aspect of how Rails is designed makes it relatively easy to turn a (Javascript-light) Rails web application into an API: the resources will be similar or the same, the web framework returning HTML pages while the API (typically) returns data in JSON or XML format.

Backbone.js: A new stateful layer

Backbone is also based on RESTful resources. Whenever you create, update or destroy a backbone.js model, you do so via the standard HTTP actions sent to URIs which assume a RESTful architecture of the kind described above. This makes it ideal for integrating with RESTful services like RoR.

But there is a subtle point to be stressed here: backbone.js integrates seamlessly with Rails as an API. That is to say, if you strip away the HTML views and just use Rails for serving RESTful resources, integrating with the database, performing session management, etc., then it integrates very nicely with the structure that backbone.js provides for client-side code. Many people argue that there's nothing wrong with using rails this way, and I think in many ways they are right.

The complications arise though from the issue of what to do with that other part of Rails that we've just thrown away: the views and what they represent.

Stateful humans, stateless machines

This is actually more important than it may initially seem. HTML views represent the stateless interface that humans use for accessing the RESTful resources your service provides. Doing away with them leaves you with two access points:

  1. For humans: a rich, client-side interface provided by the backbone.js layer (stateful)
  2. For machines: a resource-oriented RESTful API provided by the rails layer (stateless)

Notice that there is no longer a stateless (RESTful) interface for humans. In contrast, in a traditional rails app with an API, we had something closer to this:

  1. HTML resources for humans (stateless)
  2. JSON/XML resources (API) for machines (stateless)

The latter two interfaces for accessing resources are much closer in nature to each other than the previous two. Just think for example of rails' respond_with, which takes advantage of the similarities to wrap various RESTful responders in a unified method.

Working together

This might all seem very abstract and beside the point, I know. To try to make it more concrete, consider the following problem, which gets back to your question about getting rails and backbone.js to work together. In this problem, you want to:

  • Create a web service with a rich client-side experience using backbone.js, with rails as the back end serving resources in JSON format.
  • Use pushState to give each page in the app a URL (e.g. /posts/123) which can be accessed directly (by entering it into the browser bar).
  • For each of these URLs, also serve an HTML page for clients without javascript.

These are not unusual demands for a modern web service, but they create a complex challenge. To make a long story short, you now have to create two "human-oriented" layers:

  1. Stateful client-side interface (backbone.js templates and views)
  2. Stateless HTML resources (Rails HTML views)

The complexity of actually doing this leads many nowadays to abandon the latter of these two and just offer a rich client-side interface. What you decide to do depends on your goals and what you want to achieve, but it's worth thinking about this problem carefully.

As another possible reference for doing that, I'd suggest having a look at O'Reilly's RESTful Web Services. It might seem odd to be recommending a book on REST in a question about Rails and Backbone.js, but actually I think this is the key piece that fits these very different frameworks together, and understanding it more fully will help you take advantage of the strengths of both.

Backbone With Rails

I think perhaps you are confused about how web applications work. Backbone is a client-side framework; it uses Javascript code that is run in your users' browsers. Rails is a server-side framework; it uses Ruby code that runs on your server.

Given all that, your Backbone code and your Rails code by definition have to be completely separate. The two can communicate in only two ways:

1) Your Rails code can write <script> tags to the page (inside a .html.erb file) and put variable data there; for instance:

<script>
var myVarFromRails = '<%= someRailsVariable %>';
</script>

When that comes back from the server (ie. when you view source the page) that will get converted to:

<script>
var myVarFromRails = 'foo';
</script>

(assuming 'foo' was the value of someRailsVariable).

2) Your Javacript code can make AJAX requests to Rails URLs, and whatever the Rails code spits out there will come back as the response to your AJAX request. In other words you can do:

 $.ajax({url: someRailsUrl, complete: function(response) {
// whatever the server sent back will be inside the "response" variable
}});

Other than that the two are pretty much entirely separate, and if you want to do the same thing in both of them (eg. validate a form) you essentially have to write the code twice, once for Ruby and once for Javascript.

I say "essentially" because there are Rails plug-ins which do #1 and #2 for you in different ways. I'm not a Rails expert, and even if I was there are so many of these plug-ins that you really need to look for yourself to find out what exists and what makes sense for your codebase.

Hope that helps.

* EDIT *

I know I just said I wouldn't list libraries, but then I realized it'd be more helpful if I at least provided a few to get you started. Just don't take these as canon; they're simply some popular libraries at the moment, but they may or may not be right for you.

  1. https://github.com/codebrew/backbone-rails
  2. https://github.com/meleyal/backbone-on-rails
  3. https://github.com/aflatter/backbone-rails
  4. https://learn.thoughtbot.com/products/1-backbone-js-on-rails
  5. http://kiranb.scripts.mit.edu/backbone-slides/

That last two aren't actually libraries, they're a book/presentation, but I thought they might be useful.

Why use Backbone.js with Rails?

Here are a couple reasons:

1) Backbone was built specifically with Rails in mind, and integrates easily with the help of backbone-on-rails. While the Model-View-View Model (MVVM) patterns of Knockout and Angular could easily be incorporated into a Rails app rather unobtrusively, Backbone's MVC architecture provides a level of organization that really seems necessary if your app has a lot of asynchronous page updates. Take this Stack Overflow page for a quick example:

If you were building this question view in Rails, you'd have your question show.html.erb, question_show.js, show.js.erb, and all other js.erb files that pertain to asynchronously updating the content on this page (actions such as up/down-voting, favoriting, commenting, etc.).

In Backbone, a view is not a markup template like show.html.erb, instead, it contains all relevant code to that markup resource in one spot. So, instead of defining all your event listeners in a remote, question_show.js file and handling all your AJAX updates in various js.erb files, all event listening and publishing relevant to the questions show resource is contained in one place, the Backbone question show view. Granted, comment could have its own view and comments their own collection, as well as other MVC elements I'm not mentioning. But point being, Backbone helps you define front-end resources.

2) Choosing a JavaScript framework like Backbone helps take some of the load off your server for code that really doesn't need to be executed server-side. Why render all your markup elements in html.erb templates on the server when it could be done in the client's browser. In response to questions of security, you have the ability to whitelist/blacklist database object attributes when formatting your database object as JSON and shipping it to the client.

3) Backbone (specifically) seems to give a good amount of freedom. It provides a set of conventions to help organize your application, but at the end of the day it's your framework you're developing. The MVC framework of Backbone is less one-way than Rails, yet the solid conventions persist.

4) With Backbone (not speaking for or against other frameworks), pushState is easily implemented into a framework that expects its use cases. However, pushState has its downsides in terms of crawlers accessing your content, and requires some server-side rendering to be incorporated in a crawler-friendly way. What's great though, is that you can attain the same history/degradability in using Backbone out-of-box; their url fragments allow for the same functionality, they just have an extra # in there.

There are plenty of other reasons to use a framework like Backbone, and it really seems like there are a lot of alternatives because one framework does not fit all. But for what I can attest to, Backbone seems to be a great framework if you're building an app from scratch. And it also seems very doable if you want to incorporate it into an existing application.

Source: Backbone.js on Rails

Backbone & Rails. Why do you set routes in backbone?

Backbone is designed to be used in single-page applications, therefore it has a little to do with Rails routing. All Rails has to do is land a .html page, and everything else is client's concern.
If you intend to use Rails as an API, then its routing does not matter at all. You can even keep Rails application and frontend on different servers. In this case all what Rails has to do is process requests from the client.

For example, you can build your client-side without any hard ties to a backend, they can be absolutely separated. You just make AJAX calls from a client, then server processes them and responds with JSON.

Also, Backbone does not "route the javascript files to that html pages". It just executes functions according to a hash "route: action". These are just plain JavaScript functions which already can "see" JavaScript working within the document.

Backbone.js with Rails

Why not make use of the routes feature Backbone provides to decide which method to call? The activities controller would contain only routes use for activities, the user controller only for the user handling, and so forth.
Like this you can instantiate the controller just as you do and the routing will decide what happens based on the current location's hash.

If you can't use links with hashes (or there are no such links on your page), I'd simply name my view containers specific enough to attach events only for the current view when needed.

jQuery plugins etc. belong into views IMO. Same goes for your tabs and input hint toggle.

Update

On a general level (and I would not necessarily recommend doing it this way): If you have two methods:

// should be only called for the 'Foo' controller
function foo() {
alert("FOO");
};

// should be only called for the 'Bar' controller
function bar() {
alert("BAR");
};

and want to call only one of them depending on the current Rails controller, create a small helper:

e.g. in you *helpers/application_helper.rb*

def body_class
controller.controller_name
end

then call this method in your layout file (or header partial):

<body class="<%= body_class %>">

and use e.g. jQuery to "split" your JS execution:

if ($('body').hasClass('foo')) {
foo();
} else if ($('body').hasClass('bar')) {
bar();
}

Rails vs Backbone: Who should be responsible for templates and views?

Since it seems that nobody is going to post an answer to this I'll offer my two cents as just one point of view (I've written elsewhere about the issue of how to get rails and backbone.js to work well together: rails and backbone working together).

Most people in this situation will tend to drop the rails views altogether and migrate everything to backbone.js.

This is what I've done in a project that I'm working on right now.

This is the natural course of events, and particularly once you start getting used to all the complex interesting things you can do with backbone.js and structured javascript, it becomes difficult to turn back and implement standard stateless HTML pages.

As I mentioned in my other answer linked to above; however, there are costs to completely abandoning HTML views and the stateless layer. In my case, I intend to add back pure HTML pages for non-js-enabled browsers for GET requests only once the app has reached a certain level of functionality. We won't support POST or PUT requests unless the user has javascript enabled (or unless they want to go through the JSON API).

That's the balance I've struck: stateless HTML (no-JS) for accessing data, but JS required for posting/changing data. Your choice will vary depending on your use case and target user.

The other thing that I would mention is that if you have been working with rails HTML views on a project for some time, you might be unprepared for the initial overhead required to switch to backbone.js. This is not simply swapping HAML for ERB: you are migrating from a stateless front-end to a stateful one, and that is a potentially huge change (depending on the complexity of the app). I myself was a bit unprepared for the depth of that change, and had to do a lot of catching up before getting our app back on track after making the switch. And we made the switch very early, with minimal functionality already in-place.

Anyway just a few thoughts, hope they help.

How does Backbone.js save data in my Rails model?

In modern Javascript in general all communication between the server (Rails) and the client (Backbone) happens via AJAX (although sometimes form submissions are also used in addition). In Backbone specifically, AJAX requests revolve mainly around two methods: fetch and save (other methods such as remove can also trigger AJAX). fetch takes data from Rails and brings it in to Backbone, while save does the reverse and brings data from Backbone in to Rails.

In order for that to work with your Movie model you need:

  1. a Rails endpoint (eg. example.com/api/movie) on your server that expects to be given JSON for a Movie and saves it as a Rails record
  2. a Backbone.Model with a url property that points to the Rails endpoint

If you have these two and you call save on your Movie Backbone.Model the model will POST its attributes, in JSON form, to the model's URL, at which point your Rails endpoint can receive that JSON and store it in your database.

Similarly when you call fetch on that Movie model, Backbone will make an AJAX request to that same URL, only instead of a POST it will be a GET request. The Rails endpoint in this case should return the JSON representation of the Movie, which Backbone will then convert to model attributes.

Hope that helps.

P.S. Since User is just another model like Movie, everything I said about Movie applies equally to User.



Related Topics



Leave a reply



Submit