Ruby on Rails a revoluce ve vývoji pro web. Dokončení
Poznámka editora: Autor tohoto blogu neočekával komplikace při dokončování závěrečného článku seriálu. Stihnul se mezitím oženit a stihnul pomoci kamarádovi s administračním rozhraním pro web ve Flashi :) Jak jinak než v Rails. Takže se můžete těšit o tutoriál o tom, jak v Rails zprovoznit podporu pro Flash Remoting. Jako aktuální odčinění naleznete u tohoto článku malou ukázkovou aplikaci, která demonstruje všechny ty techniky, triky a strategie popsané v dosavadních čtyřech článcích.
V předchozích částech seriálu jsme se seznámili se základními principy frameworku Ruby on Rails — s návrhovým vzorem ActiveRecord a s konvencemi, které Rails extrahují ze zkušeností s vývojem webových aplikací (a zpětně je na něj aplikují). Tyto části frameworku silně inspirovaly mnoho dalších klonů (identické pojetí modelů a vztahu mezi controllery a views najdete např. v Cake PHP). V tomto článku náš úvodní seriál dokončíme výkladem o tom, co je v Rails z pohledu vývojáře, jeho produktivity a radosti při vývoji, ojedinělé.
Rozšíření a pomocné metody
Jak jsme viděli v předchozím článku o propojení controllerů a views, principy Rails vycházejí z jednoduchých pravidel a konvencí webového vývoje: předej identifikátor nějakého objektu určitému controlleru a prostřednictvím jeho metody s tímto objektem něco udělej. Název šablony odpovídá názvu metody. Název layoutu odpovídá názvu controlleru. A tak dále.
Podobných opakujících se činností najdeme při vývoji nekonečné množství: neustále musíme formátovat datum nebo čas a počítat rozdíly mezi nimi, formátovat velikost souborů a jiná „ošklivá“ čísla, vypisovat varovná hlášení pro položky formulářů, generovat opakující se části kódu se složitější logikou, … Nepřekvapí nás proto, když v Rails najdeme jak velké množství rozšíření pro nejčastější případy takových úkolů, tak transparentní mechanismus pro integrování rozšíření vlastních. Rails obsahují dvě základní rozšíření: rozšíření jazyka Ruby (Core Extensions) a pomocné metody (helpers) obsažené v modulu Action View.
Rozšíření jazyka Ruby doplňují třídy Array, Date, Hash, Numeric a String o „pohodlné“ metody (convenience methods), které zjednodušují často se opakující operace — typicky např. zjištění, zda je číslo sudé či liché, které využijeme třeba pro střídání barvy pozadí řádků tabulky:
1.even?
=> false
Podobně pro definice velikosti souborů můžeme použít metody pracující s „lidštějšími“ tvary než je neustálé násobení a dělení číslem 1024 a jeho násobky:
10.megabytes
=> 10485760
Důležité je, že tato rozšíření jsou přidána přímo do Ruby, metody even? nebo megabytes lze tedy volat pro všechna čísla.
Většina těchto rozšíření posiluje expresivní charakter Ruby — velmi často chceme kupříkladu vypsat položky pole (seznam kategorií nebo tagů) v čitelném, „neprogramátorském“ formátu: „Zařazeno do kategorií Apple, iPhone a Mac OS X“. Pohodlná metoda to_sentence pro objekt Array zařídí právě to:
['Hynek', 'Vilém', 'Jarmila'].to_sentence
=> "Hynek, Vilém a Jarmila"
Standardně Rails pochopitelně vypíšou „Hynek, Vilém and Jarmila“. Můžeme (jako obvykle) doplnit konfiguraci a napsat ['Hynek', 'Vilém', 'Jarmila'].to_sentence(:connector => 'a'), anebo použít lokalizační plugin, obsahující podporu i pro český jazyk.
Nepřekvapí nás, že nejvíce rozšíření nalezneme pro práci s datem a časem. Datová aritmetika („články za poslední měsíc“) a formátování data a času jsou jedny z nejotravnějších činností při programování. Pokud bychom tedy chtěli vypsat seznam úkolů pro tento měsíc, napsali bychom:
@TodoItems = TodoItem.find( :all,
:conditions => "due_date > '#{Time.now.at_beginning_of_month.to_s(:db)}' AND due_date < '#{Time.now.at_end_of_month.to_s(:db)}'",
:order => 'due_date' )
Pohodlná metoda at_end_of_month nám vrací objekt třídy Time, Tue Jul 31 00:00:00 +0200 2007. Ten si jednoduše převedeme pomocí metody to_s na řetězec v SQL formátu (viz parametr :db), který můžeme využít v porovnávání. K zabudovaným formátům :db, :long, atd. můžeme pochopitelně přidat vlastní a využít je pak podle filosofie Don't repeat yourself v dalším kódu. Pro datovou aritmetiku obsahují Rails celou řadu rozšíření.
Pomocné třídy Rails se od rozšíření tříd Ruby liší — jsou automaticky dostupné pouze ve view, nikoli v controllerech a modelech. (Lze je ale direktivou include zpřístupnit i jim.) Slouží opět ke zjednodušení formátování a generování kódu, typicky formulářových prvků, odkazů (nám již dobře známá metoda link_to nebo výstižně pojmenovaná link_to_unless_current) či pro přístup k chybovým hlášením formulářových prků (error_message_on).
Jedna z nejefektivnějších pomocných metod je ta s výstižným, byť krkolomným názvem distance_of_time_in_words_to_now. Do jisté míry zavedla určitý návrhový vzor pro „polidštění“ výpisu data a času. Byl-li např. článek publikován včera, je většinou mnohem čitelnější říci o něm, že byl publikován včera, než že byl publikován 16. 7. 2007 v 18:15.
Zde je zřetelně vidět inspirační zdroj v mentalitě 37Signals: snaha zbavit se žargonu, péče věnovaná jazyku, kterým aplikace s uživateli „mluví“. Rozdíl mezi publikován 16. 7. 2007 v 18:15 a publikován včera se může zdát nepodstatný, nezasluhující si zvláštní pozornosti, můžeme jej odsoudit jen jako „vychytávku“. Z pohledu uživatele však tento rozdíl může být propastný
Jestliže s ním aplikace mluví o „Úkolech na zítra“ a „Změněno včera“, radikálně roste jeho důvěra v to, že má smysl ji používat, že mu rozumí — neboť s ním mluví „jeho“ jazykem a neplete se mu do cesty. Neschopnost takové empatie na straně tvůrců aplikací je jedním z hlavních důvodů, proč aplikace tak často uživatele stresují. (Empatie se dá přitom samozřejmě karikovat a přehánět do podoby přechytralých aplikací, které se stále ptají „Zdá se, že na ploše jsou nepoužívané ikony, chcete je odstranit?“)
Podobné pobídky k „neprogramátorskému“ výpisu dat nalezneme napříč všemi pomocnými metodami, zvláště s ohledem na čísla:
number_with_delimiter(1234567, " ")
=> "1 234 567"
number_with_precision(123.45678, 2)
=> "123.46"
number_to_human_size 10485760
=> "10 MB"
number_to_human_size 1572864
=> "1.5 MB"
number_to_human_size 125952
=> "123 KB"
number_to_phone(123456789, :delimiter => ' ', :country_code => '420')
=> "+420 12 345 6789"
number_to_currency(12345, :unit => 'Kč ', :separator => ',', :delimiter => ' ')
=> "Kč 12 345,00"
Mnoho dalších tipů pro práci s helpery najdete v loňském Adventním kalendáři pro Rails.
Kromě zabudovaných pomocných metod v Rails nalezneme efektivní a transparentní mechanismus, jak vytvářet helpery vlastní. Najdeme je ve složce app/helpers, a její obsah může vypadat např. takto:

Soubor application_helper.rb obsahuje pomocné metody dostupné všem views (to nás nepřekvapí, protože víme, jaký význam v Rails mají komponenty pojmenované application: layout/application.rhtml, public/javascripts/application.js, atd.) Obsahuje-li naše aplikace controller pojmenovaný Todos (controllers/todos_controller.rb), pro „jeho“ views budou automaticky dostupné pomocné metody ze souboru helpers/todos_helper.rb. Samozřejmě, jako obvykle metody definované v helperech pro konkrétní controllery přetěžují ty definované v application_helper.rb přetěžují. A k čemu můžeme helpery nejlépe využít?
Typicky kupříkladu chceme, aby název stránky (<title>) byl na úvodní stránce webu jiný (Veterinární klinika Jezevčí Skála: Nejlepší péče o vaše miláčky. Očkování, vyšetření, ošetření psů, koček a jiných domácích zvířat) než na stránkách ostatních (Naše vybavení | Veterinární klinika Jezevčí Skála). Skládáme tedy název stránky z různých částí. Mohli bychom v šabloně zkusit něco jako
<title><%= if controller.action_name == 'index' then 'Veteri...' else "@page.title | Veteri..." end -%></title>
ale víme, že takový kód je velmi špatně čitelný a jako takový opět náchylný k chybám. Určitě by bylo efektivnější, kdybychom napsali prostě
<title><%= page_title -%></title>
a metodu page_title definovali právě pomocí helperu. Protože název stránky je (většinou) záležitostí celé aplikace, bude nejlépe, když tuto metodu nadefinujeme v globálním application_helper.rb například nějak takto:
def page_title
if controller.action_name == 'index'
"Veterinární klinika ... domácích zvířat"
else
"#{@page.title} | Veterinární klinika ..."
end
end
Obrovskou zásobů helperů obsahují Rails pro práci s formuláři, kdy usnadňují zejména práci s předvyplňováním hodnot u radio buttonů a u select prvků. To je však rozhodně téma na zvláštní článek, a proto se k nim ještě někdy v budoucnu vrátíme.
Nějaké ty pomocné rutiny, které do HTML vypíšou nějaký ten formulářový prvek má totiž leckterý framework. Rails však mají pro vývojáře přichystány také pomocníky velmi specifické. Na tři z nich se nyní podíváme.
Generátory
Lze říci, že počítačové programy obecně řeší dva typy úloh: buď nějaké výpočetně velmi složité úkoly (a fungují tedy jako „tisíc kalkulaček dohromady“) nebo opakující se a mechanické činnosti — případně, a to nejčastěji, kombinaci obou. Jak jsme viděli, vztahy mezi jednotlivými součástmi aplikace v Rails jsou postaveny na několika málo velmi dobře kodifikovaných principech. Není až takový problém si je zapamatovat — většinou stačí napsat to první, co člověka napadne. Bylo by však dosti neefektivní stále dokola vytvářet nové soubory s modely v příslušných složkách s příslušnými jmény a vepisovat do nich příslušné definice dědění tříd… Z tohoto důvodu obsahují Rails tzv. generátory kódu (generators). Ty nám pomohou při vytváření nových modelů, controllerů a jiných součástí aplikace. Podíváme se podrobněji na to, co nám umožňují.
Chceme-li v Rails vytvořit novou třídu objektů, tedy nový model, např. pro úkolovník, postačí, když vytvoříme ve složce app/model soubor todo_item.rb. Museli bychom ale jednak deklarovat jeho odvození z třídy Active Record, jednak bychom nějak a někde museli deklarovat jeho datovou strukturu („po staru“ řečeno sloupečky v tabulce), která je s ním těsně spjatá. V Rails nám totéž — a ještě více — vykoná jeden příkaz:
script/generate model TodoItem
Jeho zkrácený výstup vidíte níže:
create app/models/todo_item.rb
create db/migrate/001_create_todo_items.rb
create test/unit/todo_item_test.rb
create test/fixtures/todo_items.yml
Generátor nám nejenom vytvořil soubor s prázdným modelem Active Record, ale vygeneroval i soubory pro testování (které prozatím necháme stranou) a soubor se zajímavým názvem 001_create_todo_items. Ten obsahuje silnou zbraň Rails jménem migrace.
Migrace odstiňují vývojáře od definování datových struktur modelů (sloupečků v tabulce) pomocí SQL příkazů (CREATE TABLE ...) a mají dvě základní výhody: jsou čitelnější než SQL a tudíž méně náchylné k chybám v kódu a jsou přírůstkové a odvolatelné. Obsahují tak historii definic datové struktury aplikace, kterou lze vracet zpět, upravovat, jednoduše přidávat objektům nové atributy („sloupečky“).
Definice datové struktury tudíž může probíhat skutečně agilně, z hlediska znalosti věcí v daném okamžiku a vývojář se nemusí stresovat tím, jestli náhodou nepojmenoval nějaký sloupeček špatně nebo jestli mu (bože!) v tom hezkém obrázku propojených tabulek náhodou některý nechybí a „zákazník to pak bude chtít…“
Typická migrace pro náš model s úkoly může pak vypadat např. takto:
class CreateTodoItems < ActiveRecord::Migration
def self.up
create_table :todo_items do |t|
t.column :title, :string
t.column :due_date, :date
t.column :completed, :boolean
t.column :position, :integer
t.column :created_at, :datetime
end
end
def self.down
drop_table :todo_items
end
end
Vidíme, že se jedná o standardní třídu Ruby, obsahující metody up pro provedení migrace a down pro její „odvolání“. Číslo 001 v názvu souboru slouží pro identifikaci dané migrace. Migraci provedeme příkazem rake db:migrate pro změnu datové struktury aplikace na poslední definici v pořadí, příkazem rake db:migrate VERSION=1 se vrátíme na datovou strukturu odpovídající souboru 001_....
Vidíme také, že definujeme objekt s atributy pro název, datum plánovaného dokončení a informací o tom, zda byl úkol splněn, a to ve velmi srozumitelných datových typech string, date a boolean. (Rails obsahují abstraktní datový typ boolean velmi dobře popsaný v knize Agile Web Development With Ruby On Rails.)
Řekněme, že v náhlém hnutí mysli se nám velmi znelíbí pojmenování pro atribut názvu úkolu, title. Chtěli bychom, aby se jmenoval (logičtěji) name a tento nedostatek nám v pokročilé hodině téměř znemožňuje programovat dále. Je třeba to napravit, a to co nejdříve. A nepřijít přitom o všechna data, která již databáze obsahuje — takže na nějaké to DROP TABLE... můžeme zapomenout. Generátor migrace obsažený v Rails nám pomůže právě v takové situaci. Jediné, co musíme udělat, je vymyslet srozumitelný název pro migraci a vyplnit údaje o tom, jak se má příslušný sloupeček příslušné tabulky přejmenovat:
script/generate migration ChangeAtrTitleOfTodoItemToName
=> create db/migrate/002_change_atr_title_of_todo_item_to_name.rb
Vygenerovaný soubor pak obsahuje prázdnou třídu migrace, do níž doplníme jen těla metod up a down.
class ChangeAtrTitleOfTodoItemToName < ActiveRecord::Migration
def self.up
# rename_column :table_name, :column_name, :new_column_name
rename_column :todo_items, :title, :name
end
def self.down
rename_column :todo_items, :name, :title
end
end
Příkazem rake db:migrate a restartem aplikace pak provedeme migraci na tuto definici. Z tohoto důvodu nečekejte u Rails aplikací žádné SQL dumpy apod. Ty případně využijete jen pro přenášení dat — nikoliv datových struktur — mezi databázemi.
Podobně snadno můžeme Rails nechat vytvořit soubory pro určitý controller (např. Article) a jeho metody (např. index, list a show):
script/generate controller Article index list show
create app/controllers/article_controller.rb
create test/functional/article_controller_test.rb
create app/helpers/article_helper.rb
create app/views/article/index.rhtml
create app/views/article/list.rhtml
create app/views/article/show.rhtml
Vidíme, že generátor vytvořil soubory pro samotný controller, jeho helper a test, stejně jako views pro metody, které jsme uvedli při jeho volání. V postupně vznikajících IDE pro Rails nalezneme tyto generátory obalené grafickým rozhraním, jejich funkce je ale totožná a možnosti stejné.
Konzole
Podobná specialita Rails, která se též odehrává v příkazové řádce, je konzole aplikace, která se spouští příkazem script/console. Umožňuje nám pracovat s živou, běžící aplikací v prostředí terminálu. Ve fázi psaní kódu i pro rychlý pohled na data aplikace je její pomoc neocenitelná: umí číst data z modelů, upravovat je a zase zapisovat, má přístup k helper metodám a v omezené míře i ke controllerům. Můžeme si tak vyzkoušet různá formátování výstupu za pomoci helperů, složitější operace s modely „nanečisto“, bez použití prohlížeče, neustálého znovunačítání stránky a vypisování různých „pomocných“ dumpů. Mnoho triviálních operací můžeme navíc provádět pouze přes konzoli a vůbec k nim nevytvářet grafické rozhraní v HTML a odpovídající controllery. Typickou takovou úlohou je např. změna hesla u nějaké velmi jednoduché aplikace bez složité struktury uživatelských práv:
script/console production
u = User.find_by_login('admin')
=> #<User:0x35a0ed0 @attributes={"name"=>"Administrator", ...
u.password = 'simsalabim'
=> "simsalabim"
u.save
=> true
A je to. Podobně neocenitelnou pomoc nám konzole poskytuje pro rychlý administrátorský zásah nebo rychlou kontrolu:
User.find_by_email('persona_non_grata@botmail.com').destroy!
=> true
User.find_all_by_is_logged_in(true).size
=> 21
Z pohledu vývojáře je zkrátka konzole jedna z nejužitečnějších věcí v Rails; nástroj, který šetří obrovské množství času a psaní různých podpůrných pseudo-metod controllerů a pseudo-views pro ladění výstupu aplikace. Všechny příklady Rails kódu v našich článcích jsou prováděny právě v konzoli. Implicitně je konzole spuštěna v development režimu aplikace — pokud bychom ji chtěli spustit nad „ostrými“ daty (což chceme dost často), musíme režim uvést při jejím spuštění: script/console production. Rozsáhlý seznam tipů a triků pro práci v konzoli naleznete na blogu Amy Hoy, Err the Blog a v příslušné epizodě Railscasts. Zde z nich uvedeme pouze dva nejužitečnější.
Při práci v konzoli zároveň často měníte kód modelu. Konzole však o změněném kódu neví. Abyste ji nemuseli stále ukončovat příkazem exit a znovu startovat, postačí znovu načíst aplikaci příkazem reload!. Druhým nedocenitelným tipem je zkratka pro zformátování výstupu do velmi čitelného YAML formátu — postačí příkaz uvést jediným písmenem y:
y TodoItem.find(:all)
---
- !ruby/object:TodoItem
attributes:
name: "Dopsat článek"
completed: f
id: "1"
...
Ba co víc. Znak podtržítko slouží jako zkratka po vyvolání výstupu předchozího příkazu. Můžeme tedy psát příkazy „jak jsme zvyklí“, a jen výstup, který nás zajímá, rychle převést na YAML, aniž bychom museli daný příkaz psát znova.
TodoItem.find(:all)
=> [#<TodoItem:0x362c91c @attributes={"name"=>"Dopsat článek"...]
y _
---
- !ruby/object:TodoItem
attributes:
name: "Dopsat \xC4\x8Dl\xC3\xA1nek"
...
Konzole dokonce obsahuje všechny vlastnosti terminálu, na které jste zvyklí: šipkami nahoru a dolů se pohybujete v historii příkazů, pomocí ctrl+R můžete v historii i vyhledávat, klávesa tab doplňuje příkazy — těm, kteří se terminálu nebojí, tedy poskytuje maximální komfort a efektivitu při budování a ladění aplikace.
Pluginy a komunita
Protože nejste sněhová vločka, je velká pravděpodobnost, že funkcionalitu, kterou potřebujete, potřeboval už dříve někdo před vámi. A ten někdo mohl být dokonce chytřejší než vy. Pro Rails existuje obrovské množství rozšíření formou pluginů, od těch určených pro specifické případy, po pluginy, které řeší obecné problémy téměř každé aplikace. Podívejme se na dva takové případy.
V aplikaci můžeme mít určitý uspořádaný seznam položek: stránek v menu, souborů ke stažení, úkolů v úkolovníku. Položky se mohou vypisovat v určitém pořadí (podle data, názvu, apod.) nebo podle manuálně zadaného pořadového čísla. Je standardním požadavkem ze strany klienta, aby řazení položek bylo možné „manuálně“ ovlivnit i u takových seznamů, které jsou řazeny dle data — např. v případě výpisu tiskových zpráv. Tuto funkčnost je třeba implementovat snad v každé webové aplikaci. Lze říci, že kde je nějaký podobný seznam, dříve či později se objeví požadavek na to, aby bylo možné jeho položky manuálně řadit. Nepřekvapí nás proto, že Rails obsahují standardně plugin s příznačným názvem acts_as_list — TodoItem acts_as_list („Úkol se chová jako seznam“).
Každému modelu můžeme jednoduše nastavit, že se chová jako seznam:
class TodoItem < ActiveRecord::Base
acts_as_list
end
Jediné, co musíme navíc udělat, je definovat v dané tabulce sloupeček s názvem position. Pro instance třídy TodoItem pak máme automaticky k dispozici metody k pohodlné manipulaci s položkami seznamu: move_higher, move_lower, move_to_top, higher_item a další. V controlleru si pak nadefinujeme jen jednoduché obslužné metody:
def move_higher
if TodoItem.find(params[:id]).move_higher
flash[:message] = "Položka byla posunuta výše v seznamu"
else
flash[:message] = "Bohužel, položku se nepodařilo posunout…"
end
redirect_to :action => 'list'
end
(Pro kompletní kód si stáhněte ukázkovou aplikaci z tohoto článku.)
Psát podobnou funkcionalitu „vlastnoručně“ by bylo naprostým plýtváním času. Právě v těchto případech Rails oceníte nejvíce: vždy, když si uvědomíte, kolika vlastností dosáhnete prostými deklaracemi typu acts_as_list.
Obsluha uspořádaného seznamu je ale pořád ještě slabý čaj proti práci se soubory nahrávanými do aplikace pomocí formulářů: ošetřením práv, zmenšováním obrázků, hlídáním správných datových typů, … Právě pro práci s uploady obsahují Rails plugin actsas_attachment, resp. jeho novější inkarnaci attachmentfu. Tento plugin si musíme do aplikace nainstalovat:
script/plugin install acts_as_attachment
Pro uploadovaná data si vytvoříme model a deklarujeme jeho chování:
class Photo < ActiveRecord::Base
acts_as_attachment :storage => :file_system,
:max_size => 3.megabytes,
:content_type => :image,
:resize_to => '800x>',
:thumbnails => { :small => '125>', :tiny => '25>' }
validates_as_attachment
end
Vidíme, že definujeme způsob ukládání dat (na disku), maximální velikost (3 MB), povolený typ souborů (pouze obrázky) a necháváme je zmenšit na 800 pixelů na šířku. Zároveň vytváříme dva thumbnaily (small a tiny) o velikostech 125 a 25 pixelů na šířku. Na posledním řádku deklarujeme, že se má před uložením objektu provést validace — zda vůbec něco bylo nahráno, zda není nahrávaný dokument větší, než povolené tři megabyty a zda má správný mime-type.
Jediné, co musíme zajistit ve view, je to, aby se příslušný file input jmenoval korektně, tedy:
file_field :photo, :uploaded_data
=> <input id="photo_uploaded_data" name="photo[uploaded_data]" type="file" />
V controlleru pak jednoduše zavoláme:
@photo = Photo.create(params[:photo])
Což je jen zkratka pro případný zápis Photo.create(:uploaded_data => params['nahrany_soubor']). V případě, že data z formuláře odesíláme „zabalená” v kontejneru photo a nazvaná uploaded_data, vystačíme si s kratším zápisem. V každém případě pozorně sledujte log soubory (log/development.log a log/production.log), jestliže vám něco nefunguje – dozvíte se přesně, s jakými parametry voláte metody a pod jakým názvem vám uploadovaný soubor přichází.
Soubor pak máme uložen jak na disku, tak v databázi a můžeme k němu a k jeho atributům snadno přistupovat:
photo = Photo.find(:first)
=> #<Photo:0x3576c84 @attributes={"content_type"=>"image/jpeg", "size"=>"132716", ...
number_to_human_size photo.size
=> "129.6 KB"
photo.content_type
=> "image/jpeg"
photo.width
=> 500
photo.public_filename
=> "/photos/1/sample-image-01.jpg
Pokud si budete chtít výše uvedený Ruby kód vyzkoušet v Rails konzoli, nezapomeňte nejprve nalinkovat helpery pro čísla: include ActionView::Helpers::NumberHelper. Konzole je sama od sebe připojené nemá.
Na disku ukládá plugin actsasattachment dokumenty do podsložky pojmenované stejně jako tabulka pro daný model, v našem případě tedy photos, a pro každý dokument vytváří dále podsložky pojmenované podle jejich ID:

Ve view, kde chceme daný soubor zobrazit, pak jen jednoduše napíšeme:
<%= image_tag photo.public_filename(:small) # Malý thumbnail %>
<%= image_tag photo.public_filename # Plná velikost %>
Jak vidíme, práce s uploady je díky pluginu actsas_attachment zcela jednoduchá. Rails však obsahují ve svých útrobách skrytou ještě jednu _vychytávku: třídu TestUploadedFile. Ta je primárně určena především k testování uploadů, jak její název napovídá. Můžeme ji ale skvěle využít k něčemu jinému: k automatizovanému importu dat z disku do naší aplikace.
Představme si, že programujeme fotogalerii, a fotografie máme na disku. Chceme využít plugin acts_as_attachment a mít všechny fotografie přístupné jako objekty v Rails. Jistě nebudeme stovku fotografií uploadovat jednu po druhé pomocí formuláře v HTML stránce. Napíšeme si importní metodu:
# class Photo
def self.import_photo file
raise "PhotoNotFound (#{file})" unless File.exists? file
self.create( :uploaded_data => ActionController::TestUploadedFile.new(file, 'image/jpeg') )
print "."
end
Nyní nám jen stačí projít celý adresář a fotografie naimportovat:
# class Photo
def self.import_directory dir
t = Time.now
count ||= 0
Dir.foreach(dir) do |file|
# Trivialni kontrola vyhovujicich souboru
if file.to_s.downcase.include? "jpg"
Photo.import_file "#{dir}/#{file}"
count += 1
end
end
puts "\n-- Nahrál jsem #{@count} fotografií z adresáře #{dir} za #{Time.now-t} sekund\n\n"
end
Tyto metody nyní můžeme spustit pomocí Rails konzole, nebo je můžeme pouštět třeba periodicky pomocí cronu a script/runner. Můžeme tak pomocí dvou metod o sedmnácti řádcích naplnit databázi stovkami megabytů dat, která pak máme ve webové aplikaci přístupná jako objekty v databázi. Co to přesně znamená? Stačí jen trochu rozpálit fantazii a představit si, že pomocí callbacku before_save a funkce get_exif_by_entry RMagicku přečteme data třebas o fotoaparátu a můžeme si dělat podobně krásné statistky jako má Flickr… a s takovými hezkými grafy…
Pluginy pro Rails a knihovny (standard libraries) nebo balíčky (rubygems) pro Ruby jsou totiž možná to úplně nejlepší na psaní aplikací v Rails. Máloco vás potěší tak jako to, když zjistíte, že funkčnost, kterou potřebujete, napsal už někdo před vámi, a možná dokonce i líp, než byste to udělali vy. Pluginy a gemy jsou takový svět Rails/Ruby v malém — základní infrastrukturu máte hotovou, nemusíte o ní přemýšlet a nemusíte ji programovat. Můžete se zabývat „detaily“, které celý svět tak obdivuje na Basecampu, Flickru, Joyent Connectoru. Můžete zkrátka řešit věci, které jsou z pohledu uživatelů vaší aplikace daleko důležitější než „prostá funkcionalita“. Tu očekávají automaticky — neohromíte je tím, že jim ukážete pár text fieldů ve formuláři, pomocí nichž mohou zadat novou fakturu do účetní aplikace. Pluginy, gemy a ostatní rozšíření vám šetří čas, který můžete věnovat úplně jiným věcem.
Například podobně standardní potřebou jako manipulace s položkami uspořádaného seznamu nebo upload souborů je i to, aby vám aplikace poslala e-mailem notifikaci, když dojde k nějaké chybě či výjimce. Pro Rails existuje výborný plugin Exception Notifier, který vám zašle vyčerpávající informaci o tom, jaká chyba v aplikaci nastala, při jakém requestu, jaký řádek kódu chybu způsobil. A vy se můžete zabývat sortováním takových mailů, nebo třeba jejich automatickým parsováním do ticket systému...
Co když ale potřebujete něco méně obvyklého? Chtěli byste například exportovat ActiveRecord recordsety do CSV formátu. A ejhle, on existuje pěkně napsaný plugin convertibleto_csv. Potřebujete provést request na nějaké URL na internetu a zpracovat je? Využijete už nám dobře známou knihovnu Net::HTTP_. Chcete použít jako datové úložiště třeba Google Spreadsheet? Stáhněte si knihovnu GData Ruby a polovinu práce máte hotovou.
Samozřejmě, ne všechno v Ruby a pro Ruby je ukázkou elegantního kódu. (I když ty nejhorší výstřelky bývají dost často portem z jiného jazyka.) Díky úspornosti a čitelnosti kódu v Ruby vám ale trvá neporovnatelně méně času přepsat části kódu podle „svého“.
Okolo Ruby a Rails však především existuje na celém světě přátelská, energická komunita programátorů, vývojářů a web designérů, takže pokud vládnete angličtinou, najdete na blozích a diskusních fórech spřízněných s Rails jedny z nejzajímavějších a nejtalentovanějších tvůrců dneška.
Ruby on Rails je jedním z nejpoutavějších duchovních výkonů v oblasti informačních technologií za poslední dekádu: zcela změnil zavedené postupy ve vývoji pro web, a za jeho vlak se dále připojují vagony Standardizace deploymentu, Integrace version-control, Výstup v různých formátech pomocí respond_to, Sdílené aplikační rozhraní aplikací — REST, Škálování aplikací, … Právem za Rails David Heinemeier Hansson obdržel cenu 2005 Open Source Best Hacker.
V současné době je již samotný kód Rails dílem mnoha a mnoha přispěvatelů, nikoliv solitérským geniálním činem. Právě účast na tomto „společném díle“, objevování netušených možností Ruby a Rails, integrování funkcí a vlastností, které byste si v jiných jazycích a frameworcích dvakrát rozmysleli, je to co přináší vývojářům v Rails takovou radost z práce.
Tento „nakažlivý optimismus“ lidí okolo Rails je možná jedním ze zdrojů nedůvěry a obav, které se v souvislosti s Rails stále dokola objevují. Práce má být přece otrava. Pro vývoj v Rails to ale tak úplně neplatí. Existuje-li něco srovnatelného s radostí ze hry (běžně chápané jako protiklad práce, neboť nevytváří žádnou hodnotu), je to psaní webové aplikace v Rails… A začnou-li vás Rails bavit doopravdy, můžete nejen začít opravovat a vylepšovat Rails samotné, ale také si nabrousit ostruhy v utkání nejlepších Rails hackerů.
O Rails už každém případě víte dost na to, abyste se mohli rozhodnout, zda-li vás zajímají a mohou vám přinést nějaký užitek. Tímto článkem jsme skončili náš úvod o nejdůležitějších principech Rails. V dalších článcích se podíváme podrobněji na syntaxi jazyka Ruby, abychom porozuměli všem jejím zákoutím, a budeme se věnovat konkrétním návodům a postupům v Rails. Mezi připravená témata patří práce s formuláři a Ajax v Rails, nasazení aplikací na serveru (deployment), implementace Flash Remoting a další. A samozřejmě též slíbený článek o Rails z pohledu projektových manažerů a vůbec „neprogramátorů“. Máte-li návrh na nějakou problematiku, o níž byste se chtěli dozvědět více, využijte komentáře k článku!
Obrovský dík tentokrát patří Jiřímu Kubíčkovi za chladnokrevnou asistenci při zprovozňování Rails, SQLite a RMagicku na Windows XP, aby si autor sám prošel trnitou cestu, již budou muset projít ti z čtenářů, kteří si budou chtít ukázkovou aplikaci rozběhat v tomto operačním systému.
Výukovou mini-aplikaci doprovázející tento seriál článků si můžete stáhnout, abyste si vše mohli vyzkoušet v pohodlí svého http://127.0.0.1:
Je vhodná i pro naprosté začátečníky v Rails, obsahuje celou řadu doporučených postupů při vývoji v Rails, a na 120 řádcích kódu pokrývá tato témata:
- Použití metod
find,update_attributesatoggleActiveRecordu, definování vlastních metod pro třídu i instanci modelu - Práci s layouty, kombinaci scaffoldu v Rails a vlastních metod controllerů a views
- Metody controllerů pro zobrazování i manipulaci s objekty, včetně ošetření chyb, přesměrování
- Definování vlastních helperů
- Ukázky embedded Ruby (ERB) v šablonách, ukázky volání parciálních šablon
- Použití pluginu acts_as_list a využití jeho metod pro manipulaci s objekty v controllerech
- Použití pluginu acts_as_attachment pro upload obrázků do aplikace a jejich zmenšování

doc/README_FOR_APP.
Pokud se dostanete do nesnází, využijte komentářů k článku,
diskusního fóra na Rubyonrails.cz nebo
IRC kanálu #rubyonrails na irc.felk.cvut.cz.
~
23 komentářů