Thin 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!









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.
yay, thanks for catching this, now I can hope my birthday to come again next year!
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).
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.
well done, guy
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.