How I learnt about jQuery Deffered thanks to Rails

If you are not a regular JS developer but Ruby one or whatever, you probably don’t know every feature of JavaScript or jQuery, even if you use them every day. But some of them are worth spending some time to get to know – for me one of such features was jQuery.Deffered which I would like to introduce in this post.

The context

I love the idea behind unobtrusive javascript, silently promoted with the release of Rails 3. It helps to keep the code clean, separate concerns of data and logic like controllers and views.

In one of our projects I had to replace the standard browser confirmation prompt with a  fancy one and I was wondering if I can just re-use the Rails unobtrusive solution with data-confirm param. Then, the problem occured.

The problem

Rails wisely assigns built-in confirm method to the $.rails.confirm, which allows developer to simply replace it with anything (s)he wants. The problem is, that you cannot provide any method which would open up a prompt, wait for click and return the boolean value as confirm does. No wait here, sorry. The problem is well known, but there is no simple solution yet.

O’rly?

Eight months ago (sic!) Mr akzhan proposed the solution – he rewrote jquery_ujs confirm to work with $.Deferred. After polishing, his work landed couple of days ago in assync-confirm branch of jquery-ujs maintainer repo. Unfortunatelly, Steve will not merge it with master untill it is used by more people at production. However, if you like, you can just download the fork, put it somewhere into your assets and require the standard jquery_ujs instead.

Here’s a simple code which works for me (with jConfirm and coffee-script):

$.rails.confirm = (message) ->
  answer = $.Deferred()
  jConfirm message, 'Please confirm', (result) ->
    if result
      answer.resolve()
    else
      answer.reject()
  return answer.promise()

And that’s all! Dead simple, isn’t it? It will work with standard data-confirm, e.g.:

link_to some_path,
  { :method => 'delete', :remote => true, :confirm => "o'rly?" }

How it works?

So, you can ask – what is this magic $.Deffered? In the simplest words it’s an object with stack of callbacks inside. You can add a new one from the outside, and resolve/reject the object which would end up with specific callbacks run. There are three stacks – one is called when an object receives resolve() call, another one when it receives reject() call, and lastly a one when it receives any of the two calls.

In the example above we’re creating a deffered object which would get resolve/reject call from the jConfirm callback, depending on user decision. Without waiting for response, the $.rails.confirm will return the misterious promise object – it’s a deffered object without resolve/reject methods. You can still add new callbacks, but you cannot decide when to run them. We left that decision to jConfirm callback.

It’s also worth knowing that you cannot set a state of deffered object twice. However if you add a callback after resolving/rejecting, it would be fired up instantly, depending on the state of course.

Conclusion

Read the whole doc at jquery.com and just use it when you need the ability to postpone adding your callbacks. There are some objects already deffered – like $.ajax. You don’t need to specify callbacks inside constructor, you can move the object around and add callback anytime you need with then, done, fail or always.

Disclaimer

If you decide to use assync-confirm version of jquery-ujs, make sure that a little bug is fixed, or fix it yourself.

Backbone.js, Rails 3 and asynchronous interfaces


Backbone.js and RailsCurrently at Monterail we’re building a frontend-heavy app. With a lot of cool stuff. Like preloading data for daily views so that you won’t have to wait for every surrounding day to load. Many, many interactive elements that should be really interactive – not in an oldschool, ajax “spinner” way. And a lot of other stuff we shouldn’t talk about yet.

We decided to put more emphasis on Backbone.js with Rails and build most of the app with this combination.

Below you can find some of our findings on the topic of making those two speak to each other in a nice manner.

Asset pipeline

Really makes things easier. Organizing your JS code before it meant a lot of things: handling dependencies, minifying & compressing the code, organizing it into a lot of different files and building connections between them. With asset pipeline and it’s awesome “require” directive, you can forget about handling it manually. You just create the structure in app/assets/javascripts. Or app/assets/javascripts/backbone. Your choice. You can have many different files in whatever directories you like – in the end you’ll get one (or more, if you decide to) JS file that has everything your webapp need.

Don’t forget to:

  • gzip your JS/css assets
  • minify them!

Rails-backbone gem

Also makes things easier. It puts backbone.js (& underscore.js) library code into your asset pipeline. It gives you some great generators to get started if you don’t know where to start. Definitely worth including in your Gemfile.

http://rubygems.org/gems/rails-backbone

Coffeescript & underscore.js

Obviously. It makes Javascript code feel fresh. You can fold, iterate and do a lot of stuff. Be sure to check out underscore.js docs. And coffescript docs, of course, but I bet you know it already.

“V” in MVC done right

Views are classes. You handle logic inside them using the hardcore code, which is receiving so much hate in Rails views. They can have many methods, they do not have to render a single “action”. And templates are templates – and nothing else. There is a lot of discussion recently on the matter of Views/Templates in Rails, but you can start implementing it right away. Go and try it. You’ll fall in love.

You have many options for templates – eco, Handlebars.js, {{ mustache }}, but we recommend haml_coffee_assets. It gives you the HAML you (probably) already know with the ability to use coffeescript within it.

Single-page app

Yes. You will use Rails to render only the core view of the application. Of course, you will have to handle all things around the core of your app.

For example, you should probably leave out:

  • authentication logic: sign up / in, reset password
  • steps necessary to use the app: billing pages
But, other than that, you can use the views rendered in JS. What does that mean?
  • if they do not include any dynamic data, they are lightning fast
  • if they include dynamic data, you can preload it and they still will be lightning fast
Backbone comes with it’s own router. You can utilise pushState without any effort. How cool is that?

Asynchronous interface

Since you’ll be doing most of the association/validation work in background, you don’t have to worry too much about server responses. It’s the same with rendering elements: you’ll be doing it in JS, not Rails. You don’t have to wait for the request to return the rendered element to actually display them. If you create something, the UI should respond right away and the ajax request can persist the data with it’s own pace.

You should, however, prepare a global “something went wrong, reload the page” request rescuer – in case of network outages, backend app failures and so on. But it’s way easier to manage.

API

You get it for free. It doesn’t have to be public, but if you decide to, it’s a matter of adding OAuth as the way of authenticating requests and it’s done. (Or you could go with http basic auth, which is obviously bad for a lot of reasons).

With smart caching (give redis-store a try), you’ll save tons of server power. You know, relating to the old ruby saying, “views are most resource-consuming”.

Conclusion

There are, obviously, downsides, which I’ll cover in future (I silently hope that there won’t be any reason to do that). And topics I haven’t covered. I’m curious about other stacks and ways of doing things. Share in the comments or ping me on Twitter!