Mar
28

Sprockets & Bower in my Padrino please.

One of the things that Padrino doesn't come out of the box with is Sprockets, a rack middleware for asset packaging & management. This decision makes sense as not every app needs something like that, or should even be forced to use it. There are other alternatives like jammit and sinatra-assetpack that you can use as well. But should you need Sprockets specifically, it's pretty easy getting it setup. First up, you'll need to include a few libraries. Add the sprockets gem and sprockets-helpers gem to your Gemfile. If you want compression as well, we can add it here, but let's put that for our development group only so we don't load it during production.


  # Gemfile
  # ...
  gem 'sprockets'
  gem 'sprockets-helpers'

  group :development do
    gem 'yui-compressor'
    gem 'uglifier'
  end

We'll create a configuration setup for our Sprockets to include all the asset directories we want to have it consume. Here we can also append our Bower components/ directory as well for Sprockets to manage. Also notice that I'm placing my assets/ folder on the root of the application. I like keeping my assets part of the entire app, so if I do include subapps, they can just as easily leverage these directories as well. That's not to say they can't invoke it from it being nested in something like app/assets/, it feels much clearer to see top level directories as something shared amongst all the applications.


  # config/sprockets.rb

  SPROCKETS = Sprockets::Environment.new(File.expand_path('../../',         __FILE__))
  SPROCKETS.append_path File.expand_path('../../assets/stylesheets',        __FILE__)
  SPROCKETS.append_path File.expand_path('../../assets/javascripts',        __FILE__)
  SPROCKETS.append_path File.expand_path('../../assets/images',             __FILE__)
  SPROCKETS.append_path File.expand_path('../../assets/vendor/stylesheets', __FILE__)
  SPROCKETS.append_path File.expand_path('../../assets/vendor/javascripts', __FILE__)
  SPROCKETS.append_path File.expand_path('../../assets/vendor/images',      __FILE__)
  SPROCKETS.append_path File.expand_path('../../components/',               __FILE__)

  if PADRINO_ENV == 'development'
    SPROCKETS.css_compressor = YUI::CssCompressor.new
    SPROCKETS.js_compressor  = Uglifier.new(mangle: true)
  end

The sprockets-helpers gem gives us some nice helpers to use to retrieve the paths of our sprocket-powered assets. We can include these helpers a few different ways but well just put it into our app.rb via the #helpers. Also, if you generated the app with a stylesheet library like sass or less, go ahead and removed the registered initializer from your application. Sprockets will be doing all that work now.


  # app/app.rb
  require File.expand_path('../../config/sprockets', __FILE__)

  module Web
    class App < Padrino::Application
      register Padrino::Rendering
      register Padrino::Mailer
      register Padrino::Helpers
      helpers Sprockets::Helpers

      configure do
        set :sprockets, SPROCKETS

        # We can configure `sprockets-helpers` to find the correct assets for us.
        Sprockets::Helpers.configure do |config|
          manifest_path      = Padrino.root('public/assets/manifest.json')
          config.environment = sprockets
          config.prefix      = '/assets'
          config.manifest    = Sprockets::Manifest.new(sprockets, manifest_path)
          config.digest      = true
          config.public_path = public_folder
        end
      end

    end
  end

We will want to be able to precompile are assets before we push our code to production. However, we will also need a way to also view our changes in development as well. Let's add to our config.ru:


  # config.ru

  #!/usr/bin/env rackup
  # encoding: utf-8

  # This file can be used to start Padrino,
  # just execute it from the command line.

  require File.expand_path("../config/boot.rb", __FILE__)

  if PADRINO_ENV == 'development'
    map '/assets' do
      run Web::App.sprockets
    end
  end

  run Padrino.application

Let's go ahead and add the final piece for our pre-compilation in Rakefile:


  # Rakefile

  PADRINO_ENV  = ENV['PADRINO_ENV'] ||= ENV['RACK_ENV'] ||= 'development'  unless defined?(PADRINO_ENV)
  require 'bundler/setup'
  require 'padrino-core/cli/rake'
  require 'sprockets/../rake/sprocketstask'

  Bundler.require(PADRINO_ENV)
  require File.expand_path('../config/sprockets', __FILE__)
  PadrinoTasks.use(:database)
  PadrinoTasks.use(:sequel)
  PadrinoTasks.init

  Rake::SprocketsTask.new do |t|
   manifest_path = File.expand_path('../public/assets/manifest.json', __FILE__)
   t.environment = SPROCKETS
   t.output      = File.expand_path('../public/assets', __FILE__)
   t.manifest    = Sprockets::Manifest.new(SPROCKETS, manifest_path)
   t.assets      = %w(application.js application.css)
  end

Alright! With all of this setup we can go ahead and create our sprocket powered assets and include them into our views with something like:


  # app/views/layouts/application.slim
  doctype html
  html
    head
      # ...
      link href=stylesheet_path('application') media="screen" type="text/css" rel="stylesheet"

Since we have Sprockets being loaded via config.ru, we need to use it via the file directly or something like rackup:


  web $ rackup config.ru -p 3000
  [2013-03-29 05:15:29] INFO  WEBrick 1.3.1
  [2013-03-29 05:15:29] INFO  ruby 2.0.0 (2013-02-24) [x86_64-darwin12.2.0]
  [2013-03-29 05:15:29] INFO  WEBrick::HTTPServer#start: pid=13597 port=3000

Okay, we've made it this far. When we are ready to do a push to production, we can run our Sprockets rake tasks to help doing the pre-compilation. You should see something like this:


  web $ rake assets
  I, [2013-03-29T04:57:30.185976 #13086]  INFO -- : Writing /web/public/assets/application-e08d1625e051d63293e26ce3eb4100de.css

And there we have it. We should have our assets pre-compiled and app ready to go. That wasn't so hard was it? Thanks for reading!