The Flexible Thin Anorexic Gymnast that Democratized Deployment

ImageThin is fast, I don’t think I need to prove that again. But what I’d like to showcase now is Thin extensibility. Most of it is due to Thin being based on Rack. It’s also why lots of framework are supporting Thin already.

Can Thin replace all images with LOLCAT pics on my site when it’s my birthday of a leap year, plz, plz, plz? Cause it should!

Yeah, you’re not the first one to ask.

config.ru file

require 'open-uri'
require 'hpricot'

class LolCater
  def initialize(app)
    @app = app
  end
  
  def call(env)
    status, headers, body = @app.call(env)
    
    if iz_ma_burdae? && leap_year?
      doc = Hpricot(body)
      doc.search('img').set('src', lolcat_pic_url)
      body = doc.to_html
    end
    
    [status, headers, body]
  end
  
  private
    def iz_ma_burdae?
      Date.today.month == 11 && Date.today.day == 7
    end
    
    def leap_year?
      Date.today.leap?
    end
  
    def lolcat_pic_url
      doc = Hpricot(open('http://icanhascheezburger.com/?random#top'))
      pic = (doc/'div.snap_preview img').first.attributes['src']
    end
end

use LolCater
run Rack::Adapter::Rails.new(:root => '/path/to/my/app')

That’s called a Rack middleware. It must have a call method and receive a Rack app as the first argument of new. You then tell Thin to use this middleware when running your Rails application. Save the file as config.ru. .ru is for Rackup config file.

You can now launch your application with the thin script:

thin start --rackup config.ru

Wai-wai-wait that means I can built my own framework in like 30 LOC and use all of Thin goodness?

Right! You can start a cluster of your lolcat image replacer app like you would for a Rails app, but specify the --rackup option, which tell Thin to load your application from this file instead to go with the default Rails adapter.

thin start --rackup config.ru --servers 3

In fact, here’s a lil’ framework I built in 35 LOC:

# Start w/ thin start -r invisible.rb
require 'tenjin'

module ::Invisible
  class Adapter
    def initialize
      @template = Tenjin::Engine.new(:postfix=>'.rbhtml', :layout=>'../layout.rbhtml', :path=>'home')
      @file = Rack::File.new('public')
    end  
    def call(env)
      path = env['PATH_INFO']
      if path.include?('.')
        @file.call(env)
      else
        _, controller, action = env['PATH_INFO'].split('/')
        Invisible.const_get("#{(controller || 'home').capitalize}Controller").new(@template, env).call(action || 'index')
      end
    end
  end
  class Controller
    def initialize(template, env)
      @template, @status, @env, @headers = template, 200, env, {'Content-Type' => 'text/html'}
    end
    def call(action)
      send(action)
      render(action) unless @body
      [@status, @headers.merge('Content-Length'=>@body.size.to_s), [@body]]
    end
    protected
      def render(action=nil)
        @body = @template.render(action.to_sym, instance_variables.inject({}) {|h,v| h[v[1..-1]] = instance_variable_get(v); h})
      end
  end
end

require 'app/controllers'
run Invisible::Adapter.new

Full code on my github repo: http://github.com/macournoyer/invisible/.

And you know I can’t help myself but benchmark it:
merb-core: 1865.19 req/sec
invisible: 2428.17 req/sec

(Disclamer: it’s just for fun, don’t use that framework ever or I will come in your house when you sleep and steel all your left foot socks).

Democratizing deployment

I don’t know if you get what this means? It means, deploying a Rails or Merb, Ramaze, etc, app is just a matter of writing a simple Rack config file and playing w/ the –rackup option. It’s all the same script and tools now!

For example, there’s a Ramaze Rack config file in example/ramaze.ru, so to deploy your Ramaze application you would.

thin start --servers 3 --rackup ramaze.ru

Or create a config file and install it as a runlevel script:

thin config --rackup ramaze.ru -C myapp.yml
edit myapp.yml
sudo thin install # Installs the runlevel script
mv myapp.yml /etc/thin/myapp.yml

Or if you prefer to monitor your clusters with God, check out the sample God config file that is a drop-in replacement for Thin’s runlevel script.

Happy hacking & deploying!

12 Comments

Filed under ruby, thin, tutorial

12 responses to “The Flexible Thin Anorexic Gymnast that Democratized Deployment

  1. LolBugger

    You have a bug in LolCater#is_ma_burdae?

    def iz_ma_burdae?
    Date.today == Date.new(1981, 11, 7)
    end

    That would only return true on the day you were born (in 1981!), which will never happen again.

  2. yay, thanks for catching this, now I can hope my birthday to come again next year!

  3. Pingback: links for 2008-02-11 « Bloggitation

  4. Pingback: Head On » Blog Archive » links for 2008-02-11

  5. Pingback: Starting to Get Thin, v0.6.3

  6. I’m a bit late to comment on this post, but here I am anyways.

    When I started developing Halcyon with Rack, I didn’t really comprehend the implications of serving Halcyon; I thought I’d have to write the server, et al. But with Rack and now Thin supporting Rackup config files, I have decided to remove all of the “server” code from Halcyon and just depend primarily on Thin (though with the rackup runner the deployer can use whichever inferior server the deployer chooses to run with).

    This makes my framework a great deal simpler, thankfully. Now I can focus on more important issues rather than serving and performance (well, on the performance of serving Halcyon apps, anyways; performance is always a concern).

  7. hey matt,

    That’s very cool. Rack is changing a lot of stuff in the web ruby world these days and it’s all for the best. We can now all focus on what makes our app special.

  8. so you’re saying that being thin is an advantage on being a better gymnast? I’m a gymnast and im not thin but im not fat and i do fairly well for a starter. im doing a report on why gymnasts like me go anorexic and this popped up. it could be helpful to my paper for school.thanks much.

  9. Pingback: decodeURI » Creating a Rack Middleware for Minifying Your Javascript files

  10. Pingback: Creating a Rack Middleware for Minifying Your Javascript files « decodeURI

  11. I’m not sure exactly why but this site is loading extremely slow for me. Is anyone else having this issue or is it a problem on my end? I’ll check back later
    on and see if the problem still exists.

Leave a comment