Rails, Metal en Sinatra
15 June 2009 9:44 · Arie Meeldijk · Ruby
Rails is een prachtig framework, maar wanneer je simpele pagina’s hebt die vele honderden requests per seconde aan moeten kunnen en/of wanneer je iets heel simpels wilt maken, kan Rails teveel overhead hebben. Om deze overhead te omzeilen is er Rails Metal.
Een Metal applicatie is een op zichzelf staande Ruby (eigenlijk Rack) applicatie die je in een Rails project kunt plaatsen. Bij Rails word je geholpen door een overdaad aan handige defaults en hulpjes, maar bij een Metal applicatie ben je bijna helemaal zelf verantwoordelijk voor het afhandelen van een request.
Hello World in Rails Metal
class Poller def self.call(env) if env["PATH_INFO"] =~ /^\/poller/ [200, {"Content-Type" => "text/html"}, "Hello, World!"] else [404, {"Content-Type" => "text/html"}, "Not Found"] end end end
Metal applicaties nestelen zich voor de Rails applicatie. Wanneer in dit voorbeeld /poller/ aangeroepen wordt zal de Metal applicatie het request afhandelen. Als er geen match is (je roept bijv /iets_anders/ aan) genereert de Metal applicatie een 404 error. Rails vangt deze 404 error op, en als Rails wel een actie heeft voor de aangeroepen URL zal Rails het request verder afhandelen en een pagina tonen. Als Rails ook niets met de URL kan zal een 404 pagina getoond worden.
Nu is het schrijven van regexen, het definieren van content-types en het uitlezen van de PATH_INFO bij elk request niet heel plezierig.
Gelukkig zijn er vriendelijke manieren om een Metal applicatie te maken.
Enter stage, mr. Sinatra
Sinatra is volgens zijn makers een DSL voor het maken van web-applicaties in Ruby met minimale inspanning. Begonnen als een standalone Ruby applicatie, maar dankzij Rails Metal nu ook beschikbaar als onderdeel van een Rails project.
De Hello World code is alvast erg simpel en een stuk leesbaarder dan het pure Metal voorbeeld.
Hello world
require 'rubygems' require 'sinatra' get '/' do 'Hello world!' end
Een HTTP GET request naar de root moet de tekst ‘Hello world!’ teruggeven
Deze code kunnen we uitproberen door Sinatra te installeren en vervolgens de applicatie te draaien:
Stop de voorbeeldcode in een bestand genaamd ‘voorbeeld.rb’
sudo gem install sinatra
ruby voorbeeld.rb
Vervolgens doet Sinatra op http://localhost:4567 z’n ding.

Een Hello World pagina is natuurlijk leuk, maar een wat praktischer voorbeeld van een Sinatra mini-app geeft beter weer hoe bruikbaar het kan zijn.
Postcodecheck
Bij het invoeren van een Nederlands adres is het handig dat na het invoeren van de postcode en huisnummer, automatisch de straat en woonplaats ingevuld worden.
Met een recent project kwam deze vraag vanuit de klant, dus was dit een goede gelegenheid om dit op een herbruikbare manier te bouwen.
Allereerst laden we de Rails configuratiebestanden, Sinatra, ActiveRecord voor databasetoegang en de configfile waarin de databasegegevens staan.
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails) require 'sinatra' require 'activerecord' $config = YAML.load_file(File.join(File.dirname(__FILE__), "/../../config/database.yml"))
Vervolgens definieren we een postcode klasse en geven we aan dat dit een ActiveRecord klasse is (en dus in de database gevonden kan worden). Ook maken we een aantal vriendelijke ‘named scopes’ aan waarmee we makkelijk naar postcodes kunnen zoeken.
De named scope ‘code’ maakt het mogelijk om met Zip.code(’1016AC’) de matchende postcode-records terug te krijgen.
class ZipcodeCheck < Sinatra::Base class Zip < ActiveRecord::Base named_scope :house_number, lambda {|house_number| { :conditions => ["zips.from_number <= ? AND zips.to_number >= ? AND zips.even = ?", house_number, house_number, house_number.even?]}} named_scope :code, lambda {|code| { :conditions => ["zips.code = ?", code]}}
Een vriendelijke lookup methode zorgt ervoor dat we met Zip.lookup(’1016AC’, 270) een enkel postcode-record terugkrijgen.
def self.lookup(code, house_number) Zip.code(code).house_number(house_number).first end
Als laatste definieren we hoe we deze miniapplicatie kunnen aanroepen en wat er dan moet worden teruggegeven. In dit geval zal /vind_postcode aangeroepen kunnen worden waarbij een aantal parameters meegegeven kunnen worden. Deze parameters worden gebruikt om een postcode-record te zoeken en de inhoud van dit record als JSON weer te geven.
get "/vind_postcode" do Zip.lookup(params[:zip], params[:house_number]).to_json end
Bij het aanroepen van /vind_postcode?zip=1016AC&house_number=270 komt het volgende terug:
{"zip": {"city": "AMSTERDAM", "code": "1016AC", "street": "Singel"}}
Dit kan een brokje Javascript aan de voorkant vervolgens gebruiken om straat en woonplaats in te vullen.

Dankzij Metal is het heel eenvoudig om pagina’s die niet goed binnen het Rails framework passen toch in een Rails applicatie te integreren. De simpele Metal applicaties zijn sneller en kosten minder programmeerregels dan vergelijkbare Rails pagina’s.
Sinatra levert met z’n vriendelijke DSL een prettige manier om deze simpele Metal applicaties te maken, maar leent zich ook prima voor het schrijven van kleine stand alone webpagina’s.
Dus heb je een stuk data dat je snel beschikbaar wilt maken op het web? Gebruik dan eens geen Perl script, maar kijk eens naar Sinatra.

Reageer
RSS feed for comments on this post · TrackBack URI