I’m not going to talk about setup and configuration cuz it’s trivial! Instead lets see how jekyll is served from heroku.
Jekyll generated site is a static site, so having something serving static files is just fine. There’s Rack::Static middleware shipped with Rack and you can use it for this purpose, ie with sample config.ru:
use ::Rack::Static,
  :root => "public",    # or _site/ where *.html are generated
  :urls => %w[/]        # match all requests
# otherwise 404 NotFound
run lambda { [404, {'Content-Type' => 'text/plain'}, ['whoops! Not Found']]}
starting rackup and pointing browser to http://localhost:9292/index.html should yield a jekyll homepage!
Pretty cool, huh?
But there’s a problem: noone types /index.html these days!
rack/contrib/try_static
TryStatic is part of the rack-contrib-1.1.0 gem since this commit
With rack-contrib gem it’s even more simple! So now my config.ru looks like:
require 'rack'
require 'rack/contrib/try_static'
use Rack::TryStatic, 
    :root => "public",  # static files root dir
    :urls => %w[/],     # match all requests 
    :try => ['.html', 'index.html', '/index.html'] # try these postfixes sequentially
    # otherwise 404 NotFound
    run lambda { [404, {'Content-Type' => 'text/html'}, ['whoops! Not Found']]}
# vi: ft=ruby
And all we need to add a heroku gem manifest: .gems file, with content
rack-contrib
that’s it! Now you can push your app to heroku.
Outdated
Solutions below are obsolete and for reference only! Use rack-contrib gem instead as described above.
rack-try_static gem
Using gem it’s even more simple! So now my config.ru looks like:
require 'rack'
require 'rack/contrib/try_static'
use Rack::TryStatic, 
    :root => "public",  # static files root dir
    :urls => %w[/],     # match all requests 
    :try => ['.html', 'index.html', '/index.html'] # try these postfixes sequentially
# otherwise 404 NotFound
run lambda { [404, {'Content-Type' => 'text/html'}, ['whoops! Not Found']]}
# vi: ft=ruby
And all we need to add a heroku gem manifest: .gems file, with content
rack-try_static
that’s it! Now you can push your app to heroku.
my initial quick solution
module ::Rack
  class TryStatic < Static
    def initialize(app, options)
      super
      @try = ([''] + Array(options.delete(:try)) + [''])
    end
    def call(env)
      @next = 0
      while @next < @try.size && 404 == (resp = super(try_next(env)))[0] 
        @next += 1
      end
      404 == resp[0] ? @app.call : resp
    end
    private
    def try_next(env)
      env.merge('PATH_INFO' => env['PATH_INFO'] + @try[@next])
    end
  end
end
use Rack::TryStatic, 
    :root => "public",                              # static files root dir
    :urls => %w[/],                                 # match all requests 
    :try => ['.html', 'index.html', '/index.html']  # try these postfixes sequentially
# otherwise 404 NotFound
run lambda { [404, {'Content-Type' => 'text/plain'}, ['whoops! Not Found']]}
# vi: ft=ruby
I’ve called it Rack::TryStatic as it sequentially tries to serve a static file according :try option.
TODO
- improve code
 - spec
 - merge to rack-contrib