Při nezasvěceném pohledu zvenčí by se mohlo zdát, že v programovacím jazyce Ruby existuje jediný webový framework: Ruby On Rails. Zasvěcenější vědí, že existuje ještě framework Merb, který z Rails filosoficky a koncepčně vyšel. (Vývojáři obou nedávno oznámili kontroverzní spojení obou frameworků v dalších verzích.) Pro Ruby však existuje množství dalších frameworků, jako např. Mack nebo Webby, které vycházejí z odlišných principů.
Jedním z nich je i mikro-framework Sinatra Blake Mizeranyho , který je zajímavý hned z několika důvodů.
Jednak je skutečně maličký: v aktuální verzi se celý vejde do jednoho jediného souboru (45KB, 1500 řádků kódu), který si můžete celý prostudovat za půl hodiny.
Ruku v ruce s tím jde fakt, že Sinatra nepředpokládá o vaší aplikaci téměř nic: kromě toho, že bude napsaná v Ruby a bude mít URL. Na světě jste jen vy, Ruby a webový prohlížeč.
Díky tomu se velmi hodí na experimenty a zkoušení jazyka Ruby. To je asi nejdůležitější aspekt Sinatry (pozor! neskloňuje se podle vzoru „ondatra“, ale jako „Frank Sinatra“). Zejména Rails jsou pro zkoušení Ruby velmi, velmi nevhodnou platformou, neboť obsahují obrovské množství „magie“ a jsou zaměřené na rychlý návrh a implementaci webových aplikací založených na databázi. (V češtině navíc vyšla jen jedna kniha s takovým divným pánem na obalu.)
„Hello World“
Nejenom Sinatra je maličký, i aplikace v Sinatrovi je maličká. Takhle maličká:
# hello_world.rb
require 'rubygems'
require 'sinatra'
get "/" do
"Hello, it's #{Time.now} at the server!"
end
Spustíme ji jednoduše:
$ ruby hello_world.rb
== Sinatra/0.3.2 has taken the stage on port 4567 for development with backup by Rack::Handler::Mongrel
Principy
Jak vidíme, Sinatra obsahuje opravdu minimální množství infrastruktury: HTTP požadavek GET na kořenové URL vypíše na http://localhost:4567
aktuální čas. Kolem Sinatry je opravdu všechno malé: veškerá důležitá dokumentace se vejde do README souboru.
Však také Sinatra neobsahuje žádnou zabudovanou podporu pro objektově-relační mapování (ORM), pro usnadnění práce s JavaScriptem nebo AJAXem, helpery pro snadnou práci s formuláři ani generátory kódu či vše ostatní, co je na práci v Rails tak poutavé. To vše si musíte dle své vůle a potřeb přilinkovat (nebo napsat?) sami: můžete samozřejmě prostým require "activerecord"
využít obrovskou sílu ORM vrstvy z Rails. Nebo pro ORM využít úplně jinou knihovnu. Vzápětí podobným způsobem využijeme jeden z Ruby gemů pro snadnou práci s JSON.
Sinatra je filosoficky radikální v tom, že zcela vynechává ze hry koncept routování a controllerů, který nalezneme jinak v téměř každém webovém frameworku. Sinatra jednoduše mapuje určité URL (resp. HTTP požadavek) rovnou na kód aplikace. Není tedy typickým (nebo dalším) model-view-controller (MVC) frameworkem. Dle autorů frameworku není URL nic, čeho bychom se měli „bát“.
Nepotřebuje ani views („šablony“). Zobrazí prostě to, co vrátíte v bloku do...end
. Jako webový framework pochopitelně obsahuje rozsáhlou podporu nejen pro views (dynamické šablony), ale i pro helpery, tedy pomocné metody zjednodušující (dle nejlepších zásad MVC) kód ve view.
Sinatra je rovněž RESTful framework, protože kromě GET podporuje také metody POST, DELETE a PUT. Umožňuje též pracovat s podmínečným zpracováním HTTP požadavku a zaslat 304 Not Modified
, pokud má klient nacachovánu aktuální verzi.
A v neposlední řadě podporuje automatizované testování aplikace na úrovni HTTP požadavků a views pomocí jednotkových testů (nebo ostatních testovacích frameworků).
To není špatné na jeden soubor s patnácti sty řádky! Sinatra samotný je navíc jen tenkou slupkou kolem Racku, rozhraní pro webové servery, vykonávajícího Ruby kód (např. Mongrel, WEBRick, Thin, Ebb a Phusion Passenger). Jedním z důsledků je rychlost. Pomocí nástroje Apache benchmark si můžeme změřit výkon (pro zvídavé a kverulanty jsou v balíčku zdrojových kódů k článku připraveny příklady Hello World aplikací v dalších jazycích):
$ ab -n 100 -c 10 http://127.0.0.1:4567/
Requests per second: 704.34 [#/sec] (mean)
Time per request: 14.198 [ms] (mean)
Naše Hello World aplikace je samozřejmě elegantní, ale čas zjišťujeme zpravidla pohledem na mobil (staromódní a bohatí pohledem na hodinky). Měli bychom v Sinatrovi zkusit napsat něco zajímavějšího — např. klienta webové služby poskytující JSON data, typicky Twitteru.
Instalace a jednoduchá aplikace
Nejprve si musíme Sinatru nainstalovat. Pokud máme správně nainstalovaný jazyk Ruby a balíčkovací systém Rubygems, měli bychom si vystačit s jednoduchým:
$ sudo gem install sinatra
Spusťte Hello World aplikaci, jestli vše běží, jak má. Neběží-li, pište do komentářů k článku!
Nainstalujeme si pomocí příkazu $ sudo gem install json
balíček pro snadnou práci s JSON, a můžeme se pustit do klienta, zobrazujícího zprávy z oblíbeného zdroje.
Všechny následující kroky kódu si můžete stáhnout nebo zkopírovat z Git repozitáře.
První krok je jednoduchý:
require 'rubygems'
require 'sinatra'
require 'json'
require 'open-uri'
get '/' do
header 'Content-Type' => 'text/plain; charset=UTF-8'
JSON.parse( open("http://search.twitter.com/search.json?q=from%3Azdrojak").read )['results'].
collect { |r| '* ' + r['text'] }.
join("n")
end
Aplikaci spustíme pomocí $ ruby json_client.rb
a na výchozí adrese bychom měli vidět něco jako:
To nevypadá špatně na pár řádků kódu! Knihovna json
na řádku 8 zajišťuje snadné zpracování JSON dat do Ruby Hashe (asociativního pole). Takto zpracovaná data na řádku 9 projdeme pomocí collect
a vytáhneme pouze položky s klíčem text
. Na řádku 10 oddělíme položky pole koncem řádku a vrátíme jako prostý text.
Právě takováto extrémně jednoduchá webová aplikace či služba je pravou doménou Sinatry. Také jsou jedním z jeho nejznámějších produkčních nasazení webové služby pro post-receive hooky na serveru Github, hostujícím Git repositáře.
Výstup v prostém textu však zajisté není to, co od webové aplikace očekáváme. V dalším kroku do aplikace přidáme HTML kód ve view. Sinatra obsahuje zajímavou funkci právě pro podobné extrémně jednoduché aplikace: kromě views uložených v separátních souborech v adresáři views
můžete jejich kód umístit (odděleně) též do samotné aplikace:
%w{rubygems sinatra json open-uri}.each { |lib| require lib }
get '/:tweeter' do
@tweeter = params[:tweeter] || 'zdrojak'
@results = JSON.parse( open("http://search.twitter.com/search.json?q=from%3A#{@tweeter}").read )['results'].
collect { |r| { :created_at => Time.parse(r['created_at']).strftime('%d/%m %H:%M'), :text => r['text'] } }
erb :index
end
use_in_file_templates!
__END__
@@ layout
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>JSON Client</title>
</head>
<body>
<h1>Novinky od <em><%= @tweeter %></em></h1>
<%= yield %>
</body>
</html>
@@ index
<ul>
<% @results.each do |result| %>
<li><em><%= result[:created_at] %>:</em> <%= result[:text] %></li>
<% end %>
</ul>
V tomto kroku jsme přidali dynamické vypisování vybraného Twitter feedu podle parametru tweeter
, předaného přímo v URL (řádek 3). Na řádku 6 jsme převedli JSON data do jiné struktury a na řádku 7 renderujeme view index
pomocí šablonovacího systému ERB. Díky deklaraci use_in_file_templates!
na řádku 10 Sinatra hledá kód pro views — obecnou „slupku“ layout
a vnitřní šablonu index
— v konstantě DATA
(cokoliv za __END__
). Samozřejmě pro aplikace se složitějšími views je vhodné je umisťovat do jejich vlastního adresáře.
Rozšíření aplikace
Abychom si předvedli, čeho jsou Ruby se Sinatrou schopni, můžeme naši jednoduchou službu v posledním kroku ještě rozšířit o jednoduché cachování. V tomto případě se již vyplatí převést logiku aplikace do objektového modelu. Kód zde vypisujeme zkrácený — v úplnosti si jej prohlédněte v souborujson_client.rb
v repozitáři.
class TweetReader
# ...
def initialize(id)
@id = id
end
# ...
def load
if cached? and not stale?
load_cached
else
load_remote and cache!
end
end
private
# ...
def load_cached
puts "* Loading cached data"
@results = YAML.load_file( cached_file )
end
def load_remote
puts "* Loading data from remote service"
@results = JSON.parse( open("http://search.twitter.com/search.json?q=from%3A#e_13").read )['results'].collect do |r|
{ :created_at => Time.parse(r['created_at']).strftime('%d/%m %H:%M'), :text => r['text'] }
end
end
def cache!
File.open(cached_file, 'w') { |file| YAML.dump(@results, file) }
end
def stale?
File.mtime(cached_file) < Time.now-20 # Expirace po 20 sekundach
end
# ...
end
get '/' do
redirect( "/#{DEFAULT_TWEETER}" )
end
get '/:tweeter' do
@tweeter = TweetReader.new( params[:tweeter] || DEFAULT_TWEETER )
if time = @tweeter.last_modified # 304 Not Modified, pokud se zdroj dat nezmenil
last_modified( time )
end
@tweeter.load
erb :index
end
...
Možná z uvedeného kódu vidíte, proč některé programátory jazyk Ruby tak přitahuje: obsahuje velké množství podpůrných knihoven (jako v našem případě pro serializaci do YAML a pro práci s filesystémem) a umožňuje bez velké námahy psát čitelný, objektový kód: if cached? and not stale? load_cached()
a podobně. Samotná Sinatra aplikace pak díky tomu může mít jedenáct řádků.
Díky nedávné podpoře rozhraní Rack v Rails, můžete dokonce aplikace v Sinatrovi (stejně jiné Rack-kompatibilní aplikace) spouštět přímo jako součást větší Rails aplikace.
Sinatru můžeme snadno použít jako vizuální rozhraní pro zkoušky a cvičení v technikách objektově orientovaného programování, návrhových vzorů nebo tvorbě vlastních domain specific languages. Právě pro takové „průzkumné“ programování je Sinatra jako stvořený. Profese programátora je dnes náročná díky tomu, že celá IT sféra se pohybuje velmi, velmi rychlým tempem — a v každodenní rutině je leckdy obtížné nebo nemožné najít čas a prostor pro nezávazné zkoušení, „skicování“ a programování „zbytečností“. A bez cvičení se žádné řemeslo naučit nedá.
Hosting a další informace
Chcete-li si vyzkoušet aplikaci v Sinatrovi na serveru a nemáte možnost nebo nechcete instalovat Ruby, Gemy a aplikační server, můžete si ji jednoduše nainstalovat na Ruby hosting zadarmo od společnosti Kraxnet. Sinatra je totiž stejně jako Ruby on Rails kompatibilní s Apache modulem mod_rails (Phusion Passenger).
Zdrojové kódy použité v článku si můžete stáhnout: Ukázka frameworku Sinatra (ZIP 2508 bytů).
Další informace hledejte zejména v těchto zdrojích:
- README na stránkách Sinatry
- Informace o deploymentu na serveru hledejte v open-source knize o Sinatrovi
- Server RubyInside před časem publikoval seznam reprezentativních odkazů k Sinatrovi
- Rozsáhlý seznam mnoha aplikací, knihoven a rozšíření naleznete v pastie na serveru Github
- Zhlédněte video z přednášky na konferenci RubyConf 2008 (doprovodné slidy najdete na serveru Slideshare)
Pokud vám cokoliv z uvedených příkladů nebude fungovat správně, napište prosím do komentářů. A dejte vědět, jak vaše experimenty se Sinatrou dopadly!
Přehled komentářů