CommonJS for Ruby on Rails

by Evgeny OkhrimenkoMarch 1, 2013
Learn how to structure your JavaScript code in a Ruby-on-Rails app using Sprockets.

This blog post is for those who feel that Sprockets is not enough to structure the JavaScript code in a Ruby-on-Rails app. If you’re not satisfied with the #= require and window.Global mess, come under the cut.

 

Alternatives at hand

As you might suggest, the JavaScript community knows a lot about structuring large apps. Here’s a great overview of what the community can offer. In two words, the main practical alternatives are CommonJS and asynchronous module definition (AMD). Instead of considering the pros and cons of each approach, we’ll focus on CommonJS. It’s very simple and a solution of choice for Node.js, by the way.

If you’d like to know other people’s opinion, explore the limitations of the AMD approach and learn about AMD advantages over CommonJS modules.

 

CommonJS by Alex MacCaw

As CommonJS is simply a specification, we have a bunch of different implementations. There is also a number of Ruby-based solutions, but I’ll promote one of them—sprockets-commonjs. Yep, it’s based on Sprockets, so don’t forget to install it unless you are riding Ruby on Rails v3.1 or higher.

The rest of this article is the CommonJS quick-start guide. It may be helpful if you’re not satisfied with the official documentation.

 

Starting with CommonJS

Go to Gemfile and add the following command.

   gem 'sprockets-commonjs'

If you are not using Ruby on Rails v3.1 or higher, you may also need this command.

   gem 'sprockets-rails'

After that, you’ll be able to create CommonJS modules instead of ordinary JavaScript files. To do that, simply prefix a file extension with module. For example, widget.js.coffee
should become widget.module.js.coffee. Now, you get two new keywords.

  1. require() loads modules necessary for the current keyword.
  2. module.exports specifies what should be returned if another module will require this keyword.

Let’s assume we have our widget.module.js.coffee file inside of app/assets/javascripts/modules/.

class Widget
  draw: ->
    ...

module.exports = Widget

To use it in another module (for example, /modules/specific/window.module.js.coffee),
we just need to use the following command.

Widget = require('../widget')

class Window extends Widget
  draw: ->
    super
    ...

module.exports = Window

We can also use a dot.

require('./../widget')

An absolute path can be used, as well.

require('modules/widget')

The module extension wraps your code with this command.

require.define({'path/to/module': function(exports, require, module){ /* your code */ }});

This callback is executed only if the module is required somewhere. That’s why you can no longer worry about the correct #= require chain. A single #= require_tree ./modules in application.js would be enough.

require is the only global variable exposed by sprockets-commonjs. Except the module definition, it also allows for requiring modules from non-modules. In this case, don’t forget about the #= require precedence.

 

Further reading