Sinatra.rb aneb nejsnazší cesta k Ruby

Mohlo by se zdát, že v programovacím jazyce Ruby existuje jediný webový framework: obdivované i proklínané Ruby on Rails. Možností je však více, a na jednu z nich se nyní podíváme blíže: ukážeme si, jak na pár řádcích napsat webovou aplikaci, pracující s JSON daty, v mikro-frameworku Sinatra.

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:

Screenshot Sinatra aplikace - JSON klient

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í RackRails, 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:

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!

Jaký framework v Ruby používáte?

Karel Minařík navrhuje a programuje webové stránky a aplikace, poskytuje konzultace a školení v oblasti vývoje pro web a žije v Praze se svojí ženou a dvěma dcerami.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 28

Přehled komentářů

mirozbiro co je ruby?
Martin Hassman Re: co je ruby?
karmi Re: co je ruby?
Inkvizitor Re: co je ruby?
karmi Re: co je ruby?
darth Re: co je ruby?
skrat sinatra
karmi Re: sinatra
pm Re: sinatra
karmi Re: sinatra
karmi Re: sinatra
zoufalec instalace na Debianu unstable
tmp Re: instalace na Debianu unstable
zoufalec Re: instalace na Debianu unstable
karmi Re: instalace na Debianu unstable
tmp Re: instalace na Debianu unstable
Kamil Re: instalace na Debianu unstable
karmi Re: instalace na Debianu unstable
karmi Re: instalace na Debianu unstable
pm Re: instalace na Debianu unstable
karmi Re: instalace na Debianu unstable
zoufalec Re: instalace na Debianu unstable
Anonymní RE: Sinatra.rb aneb nejsnazší cesta k Ruby
DarkTatka RE: Sinatra.rb aneb nejsnazší cesta k Ruby
Martin Hassman RE: Sinatra.rb aneb nejsnazší cesta k Ruby
Anonymní RE: Sinatra.rb aneb nejsnazší cesta k Ruby
karmi Vydána nová verze Sinatry (0.9.0)
lucastej RE: Sinatra.rb aneb nejsnazší cesta k Ruby
Zdroj: https://www.zdrojak.cz/?p=2908