______        ___________
_______________________      ____  /_______ ___  /__  /
__  ___/_  __ \  __ \_ | /| / /_  __ \  __ `/_  /__  / 
_(__  )_  / / / /_/ /_ |/ |/ /_  /_/ / /_/ /_  / _  /  
/____/ /_/ /_/\____/____/|__/ /_.___/\__,_/ /_/  /_/  

  Makes your front-end code roll

Build Status

What?

Snowball enables you to:

  • Use npm for dependency management
  • Run your front-end javascript on a server with ease (i.e. running tests on a CI server)
  • Serve pre-defined bundles through Sinatra
  • Compile and minifiy all your JavaScript in a pre-deploy step
  • Write your front-end code in CoffeeScript
  • Serve pre-compiled Jade templates for your front-end

Why?

Because:

  • Sprockets is kinda cumbersome when you have a large number of dependencies.
  • npm is really really good at managing dependencies for you.

How?

  • It uses browserify magic to search your code for require() statements and figure out which dependencies to include in the bundle.

FAQ

Oh, but I depend on a javascript library that is not in the npm repository!

No problem, really! You can still require reqular files in your bundle files like this:

  require("./path/to/my-esoteric-lib.js")

Oh, but I have a lot of javascript code that is not written as Node modules!

Really? Then you should start converting right away.

The only thing you need to make sure is that your esoteric library follows the CommonJS / Modules spec and adds itself to the exports object. This is how underscore.js does that:

if (typeof exports !== 'undefined') {
  if (typeof module !== 'undefined' && module.exports) {
    exports = module.exports = _;
  }
  exports._ = _;
} else {
  root['_'] = _;
}

Installation

Add this line to your application's Gemfile:

gem 'snowball'

And then execute:

$ bundle

Usage

Define a bundle

Defining a bundle is as easy as creating a javascript file and require() your dependencies. Then you just add the containing folder to the Snowball search path, configure the endpoint you'd like to serve bundles from, and you are good to go.

I.e. given the follwing project layout:

myapp
  |- js
      |- all.js
          var $ = require("jquery");
          var Backbone = require("backbone");
          var myJsApp = require("myjsapp").App;
          myJsApp.init()
      |- minimal.js
          var $ = require("jquery");
          var myTinyVersion = require("tinyapp").TinyApp;
          myTinyVersion.init();
  |- my_app.rb
    class MyApp extends Sinatra::Base
      register Sinatra::Snowball
      snowball do
        set_serve_path "/bundles"
        add_load_path "js"
      end
      # (...)
    end

Now your bundles are available from /bundles/all.js and /bundles/minimal.js and all dependencies are automatically resolved and concatenated into that file

Precompiling bundles pre-deploy

Example rake task that takes a an entry file, concatenates and compresses it to a target file.

namespace :snowball do
  target = './public/all.js'
  entryfile = './js/all.coffee'

  desc "Roll a new javascript bundle"
  task :roll do
    require "uglifier"
    require "snowball/roller"
    puts "Rolling..."
    File.open(target, 'w') do |f|
      f.write(Uglifier.compile(Snowball::Roller.roll(entryfile, Snowball::Config.new)))
    end
    puts "Done!"
  end
end

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request