Finalist

Finalist Developers Blog

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 logo

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.

Hello World

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.

Automatisch aanvullen van straat en woonplaats

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.

Meer over Sinatra

Getting started met Sinatra

Sinatra op Apache met Passenger (mod_rack/rails)

Sinatra op Google App Engine

Step, Sinatra-achtig web framework voor Scala

Share and Enjoy:
  • email
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Blogosphere News
  • Fleck
  • NuJIJ
  • Slashdot
  • StumbleUpon
  • LinkedIn
  • Twitter

Reageer

RSS feed for comments on this post · TrackBack URI