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!
Recent Comments