Více informací najdete na rubyonrails.cz | Hlavní stránka | Diskusní skupina | IRC

Začínáme s Rails

Tento průvodce vám pomůže začít s Ruby on Rails. Až si jej přečtete, bude vám jasnější:

Tento průvodce popisuje Rails ve verzi 3.0. Některé části kódu zde uvedené nebudou fungovat v předchozích verzích.

1 Předpoklady

Tento průvodce je zamýšlen pro začátečníky, kteří chtějí začít s Rails aplikací od úplného začátku. Nepředpokládá žádnou předchozí zkušenost s Rails. Abyste z něj ale vytěžili co nejvíce, měli byste si nainstalovat následující:

  • Jazyk Ruby ve verzi 1.8.7 nebo 1.9.2
  • Balíčkovací systém RubyGems
  • Funkční instalaci databáze SQLite3
  • Textový editor, který umí ukládat v kódování UTF-8

Rails je framework pro tvorbu webových aplikací, využívající programovací jazyk Ruby. Pokud nemáte žádnou předchozí zkušenost s Ruby, může pro vás být náročné pustit se rovnou do Rails. Na webu je k dispozici mnoho volně dostupných zdrojů v angličtině, mimojiné:

Volně dostupné i placené zdroje v češtině jsou zastaralé nebo neadekvátní. Částečnou výjimku tvoří kniha Ruby – kompendium znalostí pro začátečníky i profesionály od nakladatelství Zoner Press.

2 Co jsou Rails?

Ruby On Rails je framework pro vývoj webových aplikací napsaný v jazyce Ruby. Je navržen tak, aby usnadnil programování webových aplikací na základě obecných předpokladů vývoje pro web. Umožňuje vám psát méně kódu a zároveň dosáhnout více než v mnoha jiných jazycích a platformách. Pokročilejší Rails vývojáři také často říkají, že programovat webové aplikace v Rails je mnohem zábavnější.

Rails jsou docela „umíněný“ software. Mají za to, že existuje něco jako „nejlepší přístup“ a snaží se k tomuto přístupu své uživatele vést — ba je v některých případech od jiných přístupů i odrazovat. Pokud si tento „Rails přístup“ osvojíte, pravděpodobně zjistíte, že jste mnohem produktivnější. Pokud budete trvat na starých návycích získaných v jiných jazycích nebo platformách, a budete se tyto způsoby snažit přenést do vývoje v Rails, programování vás asi tolik bavit nebude.

Filosofie Rails obsahuje několik vůdčích principů:

  • DRY — „Don’t repeat yourself“ aneb „neopakujte se“ — znamená, že psát stejný kód stále dokola je špatné a dobrý kód je „znovupoužitelný“
  • Konvence má přednost před konfigurací — znamená, že Rails předpokládají co asi chcete udělat a jak to chcete udělat, místo aby vás nutily specifikovat každou drobnost v nekonečném množství konfiguračních souborů
  • REST je nejlepší architektonický vzor pro webové aplikace — znamená, že uspořádat webovou aplikaci jako soubor „zdrojů“ (resources_) a standardních HTTP „sloves“ (_verbs) je nejsnazší a nejrychlejší způsob, jak modelovat entity a jejich vztahy

2.1 Architektura MVC

Ruby on Rails obsahují ve svém jádru architekturu Model, View, Controller, obvykle označovanou jako MVC. Mezi výhody MVC patří:

  • oddělení aplikační logiky od uživatelského rozhraní
  • znovupoužitelnost kódu
  • přehledná struktura kódu aplikace, která usnadňuje údržbu
2.1.1 Modely (models)

Model reprezentuje informace (data) ve vaší aplikaci a pravidla pro práci s nimi. V případě Rails jsou modely primárně využívány pro interakci s příslušnou tabulkou v databázi a pro ukládání pravidel této interakce. Ve většině případů odpovídá jedna tabulka v databázi jednomu modelu ve vaší aplikaci. Modely obsahují většinu aplikační logiky.

2.1.2 Pohledy (views)

Pohledy, neboli anglicky views reprezentují uživatelské rozhraní vaší aplikace. V Rails jsou views obvykle HTML soubory s vloženými částmi Ruby kódu, který provádí pouze úkony týkající se prezentace dat. Views mají na starosti poskytování dat webovému prohlížeči nebo jinému nástroji, který zasílá vaší aplikaci požadavky.

2.1.3 Kontrollery (controllers)

Kontrollery fungují jako „lepidlo“ mezi modely a views. V Rails slouží kontrollery k zpracování požadavků které přichází z webového prohlížeče, získávání dat z modelů a k odesílání těchto dat do views, kde budou zobrazeny.

2.2 Součásti Rails

Rails nejsou monolitický framework, ale jsou poskládány z mnoha dílčích součástí:

  • Action Pack
    • Action Controller
    • Action Dispatch
    • Action View
  • Action Mailer
  • Active Model
  • Active Record
  • Active Resource
  • Active Support
  • Railties

Následující kapitoly stručně popisují jejich úlohu.

2.2.1 Action Pack

Action Pack je gem, který obsahuje Action Controller, Action Dispatch a Action View. Je to „VC“ část z MVC.

2.2.2 Action Controller

Action Controller je komponenta, která má na starosti kontrollery v Rails aplikaci. Zpracovává příchozí požadavky, extrahuje z nich parametry a směruje je na příslušnou „akci“ v příslušném kontrolleru. Poskytuje podporu pro sessions, renderování šablon a přesměrování.

2.2.3 Action View

Action View má na starosti views ve vaší aplikaci. Ve výchozím nastavení umí poskytovat výstup v HTML a XML. Řídí renderování šablon, včetně šablon vnořených a parciálních, a obsahuje zabudovanou podporu pro Ajax.

2.2.4 Action Dispatch

Action Dispatch zpracovává směrování příchozích požadavků a posílá je tam, kam chcete: buď do vaší aplikace, nebo jakékoliv jiné Rack aplikace.

2.2.5 Action Mailer

Action Mailer poskytuje podporu pro práci s e-maily. Umožňuje rozesílat e-maily v plaintextu nebo formátované multipart e-maily s přílohami, a to na základě flexibilních šablon. Umožňuje vám ale také e-maily přijímat a zpracovávat.

2.2.6 Active Model

Active Model definuje rozhraní mezi službami součástí Action Pack a vrstvami tzv. ORM (Object Relational Mapping, objektově relační mapování). Jednou z takových vrstev je i Active Record. Active Model umožňuje v Rails používat jiné ORM technologie pro práci s relační databází (např. DataMapper), ale rovněž nerelační databáze jako MongoDB nebo CouchDB.

2.2.7 Active Record

Active Record je základem pro modely v Rails aplikaci. Poskytuje mimo jiné nezávislost na SQL dialektu konkrétní databáze, základní operace pro získávání, vytváření, editaci a mazání záznamů (tzv. CRUD, Create Read Update Delete), pokročilé získávání dat z databáze (operace typu JOIN, GROUP, aj.) a v neposlední řadě i možnost definovat vztahy mezi jednotlivými modely.

2.2.8 Active Resource

Active Resource umožňuje propojit modely ve vaší aplikaci s webovou službou typu REST. Mapuje modely na vzdálené webové služby umožňuje vám s nimi pracovat podobně jako by byly mapovány na databázi.

2.2.9 Active Support

Active Support je rozsáhlý soubor podpůrných funkcí a rozšíření základních tříd Ruby, které kromě kódu samotných Rails může využít i vaše aplikace.

2.2.10 Railties

Railties jsou jádrem kódu Ruby On Rails, který umožňuje vygenerovat novou Rails aplikaci a propojuje jednotlivé frameworky a součásti dohromady.

2.3 REST

REST je zkratka pro Representational State Transfer a je základem tzv. REST architektury. Za její manifest je obecně považována disertační práce Roye Fieldinga, Architectural Styles and the Design of Network-based Software Architectures. Nemusíte ji číst celou — v Rails se REST architektura projevuje v podstatě těmito způsoby:

  • vaši aplikaci tvoří určité entity („zdroje“), které lze jednoznačně identifikovat, typicky prostřednictvím jejich URL
  • vaše aplikace přenáší reprezentace stavu těchto entit mezi jednotlivými součástmi celého systému

Uveďme příklad. V Rails aplikaci bude tento požadavek:

DELETE /photos/17

interpretován jako odkaz na určitou entitu, v tomto případě fotografii, která má ID 17 a jako pokyn k určité akci: smazání této entity, tedy fotografie. Pro webové aplikace je REST zcela přirozená architektura, a Rails ji implementují takovým způsobem, že nemusíte vědět o mnoha komplexních problémech s ní spjatých, nebo s podivnou implementací HTTP v prohlížečích (např. to, že neznají HTTP metodu DELETE).

Pokud by vás zajímalo více podrobností o REST jako architektonickém stylu, následující zdroje jsou stravitelnější než Fieldingova disertace:

3 Vytvoření nové Rails aplikace

V průběhu tohoto návodu společně vytvoříme kompletní Rails aplikaci, velmi jednoduchý weblog, nazvaný prostě: blog. Předtím než začneme se samotnou aplikací se ale musíme ujistit, že máte Rails správně nainstalované.

3.1 Instalace Rails

Ve většině je nejjednodušší nainstalovat Ruby On Rails pomocí balíčkovacího systému RubyGems:

# Je možné, že tento příkaz budete muset spustit pomocí <tt>sudo</tt> jako _root_ uživatel
$ gem install rails

Pokud pracujete na Windows, mějte na paměti, že drtivá většina Rails vývojářů používá systémy na bázi Unixu. I když samotné Ruby a Rails lze na Windows snadno nainstalovat například pomocí Ruby Installer, mnoho Ruby gemů předpokládá, že na vaší platformě lze zkompilovat kód v jazyce C a že umíte pracovat v příkazové řádce. Pokud je to pro vás jen trochu schůdné, doporučujeme vám nainstalovat si virtuální stroj s operačním systémem Linux a vyvíjet Rails aplikace v něm, místo na Windows.

3.2 Vytvoření aplikace

Pokud chcete mít z tohoto návodu skutečný užitek, postupujte doslova krok za krokem. Nevynechali jsme žádný krok ani kousek kódu. Pokud si chcete prohlédnout celý kód, můžete si jej stáhnout z Githubu.

Nejprve si otevřete terminál a napište:

$ rails new blog

Tento příkaz vytvoří novou aplikaci nazvanou Blog ve složce s názvem blog.

Různé parametry, které můžete využít při generování Rails aplikací, si můžete vypsat příkazem rails new -h.

Poté aplikaci vygenerujte, přepněte se do jejího adresáře:

$ cd blog

Vidímě, že Rails generátor vytvořil v aktuálním adresáři složku pojmenovanou blog. Otevřete ji a prozkoumejte její obsah. V tomto návodu budeme většinou pracovat ve složce app/, ale zde je základní popis toho, co se skrývá v dalších složkách, které generátor s novou aplikací vytvoří.

Soubor/Složka Účel
Gemfile Specifikace gemů (a případně jejich verzí), které vaše aplikace vyžaduje.
README.rdoc Návod k aplikaci. Zde popište, co vaše aplikace dělá, jak ji nainstalovat, nastavit, apod. Slouží též jako úvodní stránka vygenerované dokumentace (viz příkaz rake doc:app).
Rakefile Natahuje definice konzolových příkazů pro nástroj Rake (definované v Rails a ve vaší aplikaci).
app/ Obsahuje kontrolery (controllers_), modely (models_) a pohledy (views).
config/ Konfigurace aplikace, směrování požadavků (routes.rb), definice přístupových údajů k databázi a podobně.
config.ru Konfigurace pro spuštění aplikace prostřednictvím webových serverů využívajících rozhraní Rack.
db/ Aktuální schéma databáze a databázové migrace.
doc/ Podrobná dokumentace pro vaši aplikaci.
lib/ Rozšíření a doplňkové soubory vyžadované vaší aplikací.
log/ Logy aplikace.
public/ Jediná složka, která je přístupná přes web. Obsahuje obrázky, JavaScript, stylové předpisy (CSS) a další statické soubory.
script/ Obsahuje skript rails, který spouští vaši aplikaci ve vývojovém prostředí, generuje kód, spouští interaktivní konzoli aplikace, a podobně.
test/ Jednotkové a funkční testy, testovací data a ostatní nástroje pro automatizované testování aplikace. Ty jsou popsány v návodu Testing Rails Applications
tmp/ Dočasné soubory
vendor/ Místo pro zdrojové soubory třetích stran. V typické Rails aplikaci jsou to například gemy a pluginy rozšiřující funkce Rails, případně též zdrojový kód Rails.

3.3 Instalování potřebných Gemů

Rails aplikace definuje gemy, které vyžaduje pro svůj běh pomocí nástroje Bundler. Protože si vystačíme s těmi gemy, které jsou specifikovány ve vygenerovaném Gemfile, můžeme přímo spustit příkaz:

bundle install

abychom je nainstalovali.

3.4 Konfigurace databáze

Skoro všechny Rails aplikace komunikují s databází (i když to není žádnou podmínkou). To, jakou databázi Rails použijí, je nastaveno v konfiguračním souboru, config/database.yml. Pokud tento soubor otevřete, uvidíte výchozí konfiguraci používající SQLite3. Důležité je to, že konfigurace je od začátku nastavena pro tři různá prostředí, v nichž může být Rails aplikace spuštěna:

  • Vývojové prostředí (development) používáme na svém počítači, když aplikaci vyvíjíme a pracujeme s ní.
  • Testovací prostředí (test) používají automatizované testy.
  • Produkční prostředí (production) je používáno na serveru, kam svoji aplikaci nainstalujeme, abychom ji zpřístupnili ostatním.
3.4.1 Konfigurace databáze SQLite3

Rails obsahují zabudovanou podporu pro SQLite3, což je jednoduchá databáze, která nepotřebuje server. Je možné (i když nijak nutné), že produkční provoz by ji přetížil, ale na vývoj a testování je ideální. Když Rails vytvářejí novou aplikaci, automaticky vytvářejí konfiguraci pro SQLite3 — to ale můžete kdykoliv změnit.

Takto vypadá konfigurace pro připojení k databázi ve vývojovém prostředí ve výchozím souboru config/database.yml:

development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

V tomto návodu používáme pro ukládání dat SQLite3, jelikož nepotřebuje žádnou konfiguraci a „prostě funguje“. Rails samotné podporují rovněž MySQL and PostgreSQL a existují pluginy pro mnoho ostatních databázových systému. Pokud používáte v produkčním prostředí nějakou konkrétní databázi, je velmi pravděpodobné, že pro ni bude k dispozici ten správný adaptér, abyste ji mohli používat v Rails. Ať již použijete jakoukoliv databázi, nastavte ji tak, aby ukládala data ve formátu UTF-8 (viz konfigurace databáze MySQL níže).

3.4.2 Konfigurace databáze MySQL

Pokud chcete místo výchozí databáze SQLite3 použít MySQL, váš config/database.yml bude vypadat trochu jinak. Zde je konfigurace pro vývojové prostředí:

development:
  adapter: mysql2
  encoding: utf8
  database: blog_development
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

Pokud máte na vašem vývojovém počítači instalaci MySQL s uživatelem root a prázdným heslem, konfigurace bude fungovat. V opačném případě změnte uživatelské jméno a heslo.

3.4.3 Konfigurace databáze PostgreSQL

A konečně, pokud budete chtít použít PostgreSQL, váš config/database.yml bude vypadat nějak takto:

development:
  adapter: postgresql
  encoding: unicode
  database: blog_development
  pool: 5
  username: blog
  password:

Je pravděpodobné, že budete muset změnit jméno a heslo.

Konfiguraci databáze nemusíte zapisovat ručně. Pokud jste se podívali na volby generátoru nové aplikace, jistě jste přišli na to, že obsahuje volbu —database. Dává vám na výběr z adaptérů pro několik nejrozšířenějších relačních databází. Generátor můžete spustit i opakovaně: cd .. && rails new blog —database=mysql. Potvrďte přepsání souboru config/database.yml a aplikace bude nakonfigurována pro databázi MySQL.

3.5 Vytvoření databáze

Když máme konfiguraci databáze hotovou, pojďme ji vytvořit. Toho dosáhnete spuštěním příkazu:

$ rake db:create

Ten vytvoří databáze SQLite3 pro vývojové a testovací prostředí v adresáři db/.

Rake je obecným nástrojem pro spouštění konzolových příkazů, který Rails používají na mnoho různých věcí. Seznam dostupných příkazů si zobrazíte spuštěním příkazu rake -T.

4 „Hello World!“ v Rails

Jedním z tradičních způsobů, ja začít s novým programovacím jazykem, je zobrazení krátkého textu na obrazovce. Abychom toho docílili, musíme spustit server Rails aplikace.

4.1 Spuštění webového serveru

Ve skutečnosti už máte připravenou plně funkční Rails aplikaci! Abyste si ji zobrazili, musíte spustit webový server na vašem vývojovém počítači. Uděláte to následujícím příkazem:

$ rails server

Ve výchozím nastavení se spustí webový server WEBrick (Rails mohou použít řadu jiných webových serverů, jako např. Phusion Passenger, Thin nebo Unicorn). Aplikaci si zobrazíte ve webovém prohlížeči na adrese http://localhost:3000. Uvidíte výchozí informační stránku Rails:

Welcome Aboard screenshot

Server zastavíte stisknutím Ctrl+C v terminálovém okně, kde jste jej spustili. Při vývoji aplikace Rails až na výjimky nevyžadují zastavení nebo restart serveru. Jakékoliv změny v souborech, které provedete, server automaticky načte znovu.

Stránka „Welcome Aboard“ je jen „kouřový test“ pro novou Rails aplikaci: kontroluje, zda máte Rails nainstalované správně a umí zobrazit jednoduchou stránku. Můžete také kliknout na odkaz About your application’s environment, který zobrazí informace o vašem vývojovém prostředí (verze Rails, Ruby, apod.)

4.2 Rails, řekněte “Ahoj”!

Aby Rails řekly „Ahoj“, musíte vytvořit minimálně kontroler (controller_) a pohled (_view). Naštěstí to můžete provést spuštěním jediného příkazu. V jiném okně nebo záložce terminálu tedy spusťte:

$ rails generate controller home index

Pokud používáte Windows, nebo máte Ruby nestandardně nainstalované, možná budete muset příkaz pustit s celou cestou ke skriptu Rails: ruby \path\to\your\application\script\rails generate controller home index.

Vidíte, že generátor Rails vytvořil několik souborů, mimo jiné app/views/home/index.html.erb. Ten obsahuje šablonu, která bude použita pro zobrazení výsledku akce (action) index v kontroleru home. Otevřete tento soubor v textovém editoru a upravte ho tak, aby obsahoval tuto řádku kódu:

<h1>Ahoj, Rails!</h1>

4.3 Nastavení domovské stránky aplikace

Nyní, když jsme vytvořili kontroler a šablonu, musíme Rails říci, kdy se má text „Ahoj, Rails!“ zobrazit. V našem případě chceme, aby se zobrazil přímo na výchozí URL naší aplikace, tedy na http://localhost:3000, místo zkušební stránky „Welcome Aboard“.

Nejprve musíme smazat výchozí, statický soubor z adresáře public naší aplikace:

$ rm public/index.html

Musíme tak učinit proto, že Rails přednostně renderují statické soubory ze složky public před dynamicky generovaným obsahem z kontrolerů.

Nyní musíme říct Rails, kde se nachází skutečná domovská stránka. Otevřete soubor config/routes.rb v textovém editoru. Ten obsahuje pravidla směrování (routes) v aplikaci. Jsou definovány pomocí specializovaného mini-jazyka, „dialektu“ (DSL, domain specific language). Díky těmto pravidlům Rails vědí, jak propojit příchozí požadavky s kontrolery a jejich akcemi.

Tento soubor obsahuje mnoho příkladů pravidel na zakomentovaných řádcích. Jeden z nich definuje pravidlo pro napojení domovské stránky aplikace na určitý kontroler a jeho akci. Najděte řádek začínající root :to, odkomentujte jej a změňte následovně:

Blog::Application.routes.draw do

  #...
  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  root :to => "home#index"

Deklarace root :to => "home#index" říká Rails, že má požadavky na URL “/” směrovat na kontroler home a jeho akci index.

Pokud si nyní v prohlížeči zobrazíte stránku http://localhost:3000, uvidíte Ahoj, Rails!.

Více informací ohledně směrování požadavků naleznete v návodu Rails Routing from the Outside In.

5 Rychlý start s využitím nástroje generátoru „lešení“ (scaffolding)

Scaffolding je v Rails způsobem, jak rychle vygenerovat hlavní součásti aplikace. Říká se mu „lešení“, protože vytvoří funkční části zdrojového kódu ze šablon, které Rails obsahují. Vygenerované zdrojové kódy pak samozřejmě můžete upravovat. Když chcete najednou vytvořit modely, kontrolery a šablony pro nové entity v aplikaci, generování „lešení“ je pro to ideálním nástrojem.

6 Vytvoření nové entity („zdroje“)

V případě naší aplikace, blogu, můžeme začít vytvořením „lešení“ pro entitu Post (příspěvek): ta bude představovat jednotlivý článek na blogu. Abyste jej vytvořili, zadejte na příkazové řádce následující příkaz:

$ rails generate scaffold Post name:string title:string content:text

I když vám scaffolding pomůže rychle začít, univerzální kód, který vygeneruje, vám nemusí zcela vyhovovat. Zcela jistě budete chtít vygenerovaný kód upravit. Mnoho zkušených Rails vývojářů scaffolding nepoužívá, a raději píše zdrojový kód úplně od začátku. Rails však poskytují ještě zajímavější možnost: upravit přímo šablony pro generované modely, kontrolery, šablony, helpery, či soubory s testy. Další informace najdete v návodu Creating and Customizing Rails Generators & Templates.

Generátor vytvoří ve vaší aplikaci 15 souborů, několik složek, a jeden soubor upraví. Pojďme si výsledek prohlédnout:

Soubor Účel
db/migrate/20100207214725_create_posts.rb.rb Migrace vytvářející tabulku posts v databázi (datum na začátku se ve vašem případě bude lišit)
app/models/post.rb model Post
test/fixtures/posts.yml Testovací data
app/controllers/posts_controller.rb kontroler PostsController
app/views/posts/index.html.erb Šablona zobrazující seznam všech příspěvků
app/views/posts/edit.html.erb Šablona zobrazující formulář pro úpravu příspěvku
app/views/posts/show.html.erb Šablona zobrazující detail příspěvku
app/views/posts/new.html.erb Šablona zobrazující formulář pro přidání nového příspěvku
app/views/posts/_form.html.erb Parciální šablona formuláře použitého v šablonách pro úpravu a přidání příspěvku
app/helpers/posts_helper.rb Pomocné metody pro šablony
test/unit/post_test.rb Jednotkové testy pro model
test/functional/posts_controller_test.rb Funkční testy pro kontroler
test/unit/helpers/posts_helper_test.rb Jednotkové testy pro helper
config/routes.rb Do souboru byly přidány údaje pro směrování URL entity post
public/stylesheets/scaffold.css Výchozí kaskádové styly

6.1 Spuštění migrace

Jedním z výstupů příkazu rails generate scaffold je databázová migrace. Migrace jsou třídy Ruby, jejichž účelem je zjednodušit vytváření nebo úpravy databázových tabulek. Rails používají ke spuštění migrací příkaz rake. Je možné migraci vrátit zpět, tedy zrušit změny, které v databázi provedla. Název souboru migrace začíná datem, kdy byl vytvořen, aby byly migrace provedeny v tom pořadí, v jakém byly vytvořeny.

Když se podíváte do souboru db/migrate/20100207214725_create_posts.rb (pamatujte, že ten váš bude mít odlišný název), uvidíte následující:

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :name
      t.string :title
      t.text :content

      t.timestamps
    end
  end
end

Tato migrace vytváří metodu s názvem change volanou ve chvíli, kdy spustíte tuto migraci. Akce definované touto metodou je možné později vrátit, Rails vědí, jak provedené úpravy vzít zpět. Když nyní migraci spustíme, vytvoří tabulku posts se dvěma sloupci formátu string (krátký text, VARCHAR) a jeden sloupec formátu text. Vytvoří také dva sloupce pro časové značky (timestamp), které udržují informaci o času vytvoření a úpravě záznamů. Více informací o migracích naleznete v návodu Rails Database Migrations.

Nyní můžete pomocí příkazu rake migrace provést:

$ rake db:migrate

Rails provede změny v databázi a oznámí vám, že vytvořily tabulku posts.

==  CreatePosts: migrating ====================================================
-- create_table(:posts)
   -> 0.0019s
==  CreatePosts: migrated (0.0020s) ===========================================

Protože výchozím prostředím je v Rails prostředí vývojové, migrace se provedou v databázi definované v sekci development v konfiguračním souboru config/database.yml. Pokud byste chtěli migrace provést v jiném prostředí, typicky produkčním, musíte je při spouštění příkazu explictině uvést: rake db:migrate RAILS_ENV=production.

6.2 Přidání odkazu

Nyní můžeme přidat odkaz na náš blog na domovskou stránku aplikace, kterou jsme vytvořili dříve. Otevřete soubor app/views/home/index.html.erb a změňte jej takto:

<h1>Ahoj, Rails!</h1> <%= link_to "Můj blog", posts_path %>

Metoda link_to je jednou z pomocných funkcí zabudovaných v Rails, které jsou dostupné v šablonách (views). Vytvoří hypertextový odkaz podle předaných parametrů: zobrazeného textu a URL odkazu — v tomto případě je tímto URL cesta k výpisu příspěvků.

Možná vás zarazí metoda posts_path. Jedná se o pomocnou metodu, kterou vám Rails poskytují proto, abyste mohli udržovat pravidla směrování (routing_) na jediném místě aplikace, v konfiguračním souboru config/routes.rb, a pomocí metod jako posts_path nebo new_postpath je pak předávali v aplikaci jako výsledené URL. Všechny pravidla směrování ve vaší aplikaci si můžete zobrazit příkazem rake routes. Podrobné informace o směrování v Rails naleznete v návodu Rails Routing from the Outside In.

6.3 Práce s příspěvky v prohlížeči

Konečně můžeme začít pracovat s příspěvky našeho blogu! Načtěte v prohlížeči stránku http://localhost:3000 a klikněte na odkaz “Můj blog”:

Posts Index screenshot

Stránka, kterou vidíte je výsledkem vykreslení šablony v souboru app/views/posts/index.html.erb. Zatím v databázi žádné příspěvky nejsou, ale když kliknete na odkaz New Post, můžete nějaký uložit. Rychle zjistíte, že můžete příspěvky upravovat, prohlížet si jejich detaily, nebo je smazat. A všechna aplikační logika i HTML kód vytvořil jediný příkaz rails generate scaffold.

Gratulujeme, právě jste nasedli na Rails! A teď je na čase podívat se, jak to vlastně všechno funguje.

6.4 Model

Soubor obsahující definici modelu, app/models/post.rb, vypadá takto:

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title
end

Povšimněte si, že třída Post dědí od třídy ActiveRecord::Base. Active Record poskytuje modelům v Rails spoustu funkcionality: základní operace se záznamy v databázi (CRUD, Create, Read, Update, Destroy), validační logiku, stejně jako podporu pro složité dotazy do databáze nebo propojení mezi jednotlivými modely.

Další důležitou částí je řádek attr_accessible :content, :name, :title. Ten definuje atributy, které mohou být hromadně upraveny (např. prostřednictvím update_attributes).

6.5 Validační pravidla

Rails obsahují metody, které vám pomohou validovat data, která se modelu posílají (typicky prostřednictvím kontroleru z HTML formuláře, jako když vytváříme nový příspěvek.) Otevřete soubor app/models/post.rb a upravte jej takto:

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title

  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }
end

Tyto deklarace zajistí, že všechny příspěvky budou obsahovat název (name_) a titulek (title_), a že titulek bude mít alespoň 5 znaků. Rails obsahují validace pro mnoho různých podmínek: ověření existence či unikátní hodnoty nějakého atributu (sloupce v databázi), shody textového atributu se zadaným regulárním výrazem, shodu atributu s některým prvkem ze zadaného rozsahu a mnoho dalších. Validace umí ověřovat i validitu asociovaných modelů. Více informací nalezenete v návodu validationscallbacks.html">Active Record Validations and Callbacks.

6.6 Konzole aplikace

Abychom si validace vyzkoušeli v praxi, můžeme použít konzoli aplikace. Konzole je nástroj pro příkazovou řádku, který umožňuje spouštět libovolný Ruby kód v kontextu aplikace. Spusťte konzoli tímto příkazem v terminálu:

$ rails console

Nyní můžeme pracovat kupříkladu s modely v naší aplikaci:

>> p = Post.new(:content => "Nový příspěvek")
=> #<Post id: nil, name: nil, title: nil,
     content: "Nový příspěvek", created_at: nil,
     updated_at: nil>
>> p.save
=> false
>> p.valid?
=> false
>> p.errors
=> #<OrderedHash { :title=>["can't be blank",
                           "is too short (minimum is 5 characters)"],
                   :name=>["can't be blank"] }>

Vidíte, že jsme vytvořili novou instanci třídy Post a pokusili se ji uložit. Obdrželi jsme návratovou hodnotu false, která naznačuje, že uložení se nezdařilo. Vidíte, že metoda valid? vrací rovněž false, a metoda errors vrací asociativní pole (hash) s nesplněnými validačními podmínkami pro jednotlivé atributy.

Zkuste místo p.save zavolat metodu p.save! (s vykřičníkem, v Ruby označovanou jako bang method). Vidíte, že nevrací false, ale vyvolá výjimku ActiveRecord::RecordInvalid.

Až budete hotovi, napište exit a stiskněte Enter pro ukončení konzole.

Na rozdíl od vývojového web serveru nenačítá konzole kód znovu — pokud tedy provedete změnu v modelu a máte otevřenou konzoli aplikace, napište příkaz reload!, abyste změny načetli.

6.7 Výpis příspěvků

Nejjednoduším způsobem, jak začít zkoumat funkcionalitu naší aplikace, je rozebrat si kód, který vypisuje seznam příspěvků (posts_). Otevřete soubor app/controllers/postscontroller.rb a podívejte se na metodu index (v Rails metody kontroleru běžně nazýváme „akce“, action):

def index
  @posts = Post.all

  respond_to do |format|
    format.html # index.html.erb
    format.json { render json: @posts }
  end
end

Post.all říká modelu Post, aby z databáze načetl všechny příspěvky. Výstupem tohoto příkazu je pole s příspěvky, které uložíme do instanční proměnné nazvané @posts.

Metoda all odpovídá SQL dotaz SELECT "posts".* FROM "posts". To si můžete snadno ověřit: po načtení stránky stránky v prohlížeči se podívejte na výpis v okně nebo záložce terminálu, kde jste spustili webový server s aplikací. Active Record podporuje samozřejmě složité vyhledávací, agregační nebo asociační dotazy. Více informací naleznete v návodu Active Record Query Interface.

Kód mezi do ... end se v Ruby nazývá „blok“ a často se používá podobně jako např. anonymní funkce v jQuery ($.get('/ajax', function(data) { ... }), které možná znáte lépe.

Blok předaný metodě respond_to zpracovává HTML a JSON výstup akce index. Pokud načtete v prohlížeči stránku http://localhost:3000/posts.json, uvidíte výpis příspěvků ve formátu JSON.

Formát HTML automaticky hledá šablonu ve složce app/views/posts/ nazvanou stejně jako metoda, tedy index. Všechny instanční proměnné z kontroleru jsou v šabloně k dipozici. Pojďme se podívat na obsah šablony, tedy souboru app/views/posts/index.html.erb:

<h1>Listing posts</h1>

<table>
  <tr>
    <th>Name</th>
    <th>Title</th>
    <th>Content</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @posts.each do |post| %>
  <tr>
    <td><%= post.name %></td>
    <td><%= post.title %></td>
    <td><%= post.content %></td>
    <td><%= link_to 'Show', post %></td>
    <td><%= link_to 'Edit', edit_post_path(post) %></td>
    <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New post', new_post_path %>

Ruby kód v šabloně prochází pole @posts a zobrazuje obsah jednotlivých položek (název, atd) a odkazy na stránky detailu, provedení úprav, smazání záznamu. Upozorníme vás na několik věcí:

  • šablony jsou zapsány v šablonovacím jazyce ERB (Embedded Ruby)
  • výkonný Ruby kód je uvozen znakem procenta (<% ... >), kód, který vypisuje data do stránky, je navíc uvozen znakem rovná se (<%= ... >)
  • pomocná metoda link_to vytvoří hypertextový odkaz
  • pomocné metody edit_post_path a new_post_path, poskytují Rails jako součást REST směrování podle pravidel v config/routes.rb. V dalších příkladech uvidíme ještě mnoho dalších variací těchto helperů

V předchozích verzích Rails jste museli použít metodu h, aby byl případný HTML kód, který by dynamický obsah vypisovaný do stránky mohl obsahovat, ošetřen a převeden na entity (tzv. escapován_). V Rails 3 a vyšších verzích je toto ošetření implicitní, a jakýkoliv HTML kód je na entity převáděn automaticky. Pokud chcete získat „surové“ HTML, použijte pomocnou metodu raw: <%= raw post.name %> nebo rozšíření třídy String#html_safe: <%= post.name.htmlsafe %>

Podrobnosti o šablonách a jejich zobrazování najdete v návodu Layouts and Rendering in Rails.

6.8 Úprava layoutu

Konkrétní šablona pro akci kontroleru (tedy view) je jen část toho, jak Rails zobrazují HTML ve vašem prohlížeči. V Rails najdeme také tzv. layouty_, což jsou kontejnery pro jednotlivé šablony (_view). Když Rails vykreslují tuto konkrétní šablonu, vloží ji do této „obalující šablony“, layoutu. V předchozích verzích Rails generátor vytvářel zvláštní layout pro každý kontroler. To se v Rails 3 změnilo a generátor vytvoří jen jeden layout, v souboru app/views/layouts/application.html.erb, společný pro celou aplikaci. (Můžete samozřejmě přidávat layouty vlastní a používat v každém kontroleru, nebo i v každé z jejich akcí layout jiný.)

Otevřete nyní společný layout v editoru, a upravte například tag <body>:

<!DOCTYPE html>
<html>
<head>
  <title>Blog</title>
  <%= stylesheet_link_tag :all %>
  <%= javascript_include_tag :defaults %>
  <%= csrf_meta_tag %>
</head>
<body style="background: #EEEEEE;">

<%= yield %>

</body>
</html>

Když v prohlížeči znovu načtete stránku /posts, uvidíte, že má šedé pozadí. Stejný layout bude použit i pro vykreslení ostatních views aplikace.

6.9 Vytvoření nového příspěvku

Abychom vytvořili nový příspěvek, musíme projít dvěma akcemi kontroleru. První z nich je akce new, která vytvoří objekt @post, tedy novou instanci třídy Post:

def new
  @post = Post.new

  respond_to do |format|
    format.html # new.html.erb
    format.json { render json: @post }
  end
end

Šablona new.html.erb zobrazí uživateli formulář pro vyplnění atributů tohoto objektu:

<h1>New post</h1>

<%= render 'form' %>

<%= link_to 'Back', posts_path %>

Řádek obsahující kód <%= render 'form' %> nám dává nahlédnout do problematiky parciálních šablon v Rails (anglicky partials nebo také partial templates). Parciální šablona je izolovaný kousek HTML a Ruby kódu, který můžeme používat na více místech aplikace. V našem případě je formulář pro vytvoření nového příspěvku téměr shodný s formulářem pro úpravy: oba mají dvě políčka pro vložení názvu a titulku, textové pole pro vložení obsahu, a tlačítko pro vytvoření nového příspěvku nebo aktualizaci existujícího.

Když se podíváte na soubor obsahující parciální šablonu pro formulář, app/views/posts/_form.html.erb, uvidíte následující:

<%= form_for(@post) do |f| %>
  <% if @post.errors.any? %>
  <div id="errorExplanation">
    <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
    <ul>
    <% @post.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :content %><br />
    <%= f.text_area :content %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Parciální šablona má stejně jako hlavní šablona k dispozici instanční proměnné nastavené v kontroleru, v tomto případě @post.

Více informací o parciálních šablonách najdete v návodu Layouts and Rendering in Rails.

Blok předaný metodě form_for zobrazí HTML formulář. V rámci tohoto bloku máte k dispozici pomocné metody pro vykreslení formulářových prvků. Deklarace f.text_field :name, například, říká Rails, že mají zobrazit textové políčko, a propojit je s atributem name příspěvku @post. Tyto pomocné metody můžete využít jen ve spojení s atributy modelu (v našem případě je to name, title, apod.) Samozřejmě byste mohli celý formulář zapsat v přímo v HTML – ale v Rails využíváme pro zobrazení formuláře metodu form_for a ostatní pomocné metody neboť výsledný kód je kratší, přehlednější a formulář je provázán s konkrétní instancí modelu.

Metoda form_for je také natolik inteligentní, že pozná, zda chcete zobrazit formulář pro přidání nového příspěvku nebo úpravu příspěvku existujícího. Nastaví správné URL pro atribut action formuláře i název tlačítka pro odeslání formuláře.

Pro vykreslování formulářových prvků můžete tam, kde to má smysl, využít i pomocné metody, které nejsou vázány na model, např. text_field_tag, místo f.text_field. Pro vykreslení formuláře, který není vázán na konkrétní model, můžete využít metodu form_tag místo form_for. Mějte však na paměti, že mnoho logiky, např. správné pojmenování formulářových prvků nebo uvedení URL, kam bude formulář odeslán, pak musíte napsat sami.

Když kliknete na tlačítko Create Post, prohlížeč odešle data z formuláře aplikaci, kde je převezme metoda create. Rails vědí, že mají použít metodu create kontroleru PostsController právě proto, že respektujeme konvence, o nichž jsme hovořili v úvodu tohoto průvodce. Pojďme se podívat na zdrojový kód metody create:

def create
  @post = Post.new(params[:post])

  respond_to do |format|
    if @post.save
      format.html { redirect_to @post, notice: 'Post was successfully created.' }
      format.json { render json: @post, status: :created, location: @post }
    else
      format.html { render action: "new" }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

Metoda create vytvoří novou instanci třídy Post a předá jejímu konstruktoru data z formuláře, která Rails předává z view pomocí metody params (ta vrací asociativní pole, hash). Pokud se podíváte na výpis v okně terminálu, kde jste příkazem rails server spustili webový server, uvidíte všechny podrobnosti o tom, jak data přicházejí z formuláře do Rails:

Started POST "/posts" for 127.0.0.1 at ...
  Processing by PostsController#create as HTML
  Parameters: {"commit"=>"Create Post",
               "post"=>{"name"=>"Hello, World!",
                        "title"=> ...},
                "authenticity_token"=>"...",
                "utf8"=>"✓"}
  AREL (1.3ms)  INSERT INTO "posts" ...
Redirected to http://localhost:3000/posts/1
Completed 302 Found in 17ms

Pokud byl záznam úspěšně uložen, metoda create vykoná kód odpovídající formátu požadavku: v našem případě HTML. Ten přesměruje prohlížeč na stránku s detailem záznamu (metoda show) a nastaví notifikaci pro tzv. flash zprávu. Tyto notifikace se v Rails používají pro pro předávání zpráv mezi jednotlivými HTTP požadavky. V případě akce create totiž uživateli nezobrazujeme žádnou stránku, ale po jejím provedení jej rovnou přesměrujeme na stránku detailu příspěvku. Flash notifikace uchová zprávu pro následující HTTP požadavek, takže při vykreslení šablony pro akci show zobrazíme uživateli zprávu „Post was successfully created“.

Pokud se záznam nepodařilo uložit — nejspíše díky nesplnění validačních pravidel, když jste například některé povinné pole nechali prázdné — v prohlížeči se zobrazí view pro předchozí akci, new, které bude obsahovat formulář se znovu-vyplněnými údaji a informace o nesprávně vyplněných položkách.

6.10 Zobrazení detailu

Pokud kliknete na odkaz „Show“ ve výpisu příspěvků, ve vašem prohlížeči se načte stránka s URL typu http://localhost:3000/posts/1. Rails si požadavek na toto URL vyloží tak, že mají provést akci show a předat jí 1 jako parametr id. Ve výpisu webového serveru opět vidíme podrobnosti tohoto procesu:

Started GET "/posts/1" for 127.0.0.1 at ...
  Processing by PostsController#show as HTML
  Parameters: {"id"=>"1"}

Pojďme se podívat na zdrojový kód metody show:

def show
  @post = Post.find(params[:id])

  respond_to do |format|
    format.html # show.html.erb
    format.json { render json: @post }
  end
end

Vidíme, že pomocí metody find — tu modelům poskytuje Active Record —, vyhledá jednotlivý záznam podle předaného id a předá ho do view v instanční proměnné @post. View se pak vykresluje pomocí šablony app/views/posts/show.html.erb, která zobrazí jednotlivé atributy příspěvku:

<p class="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>


<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

6.11 Úprava příspěvku

Stejně jako vytvoření nového příspěvku je jeho úprava záležitost dvou kroků, a dvou akcí Rails kontroleru.

Prvním krokem je HTTP požadavek na URL /posts/1/edit, které získáme pomocnou metodou edit_post_path(@post). Na rozdíl od metody new_post_path jí musíme předat konkrétní příspěvek (nebo jeho id). Tento požadavek vyvolá v kontroleru akci edit:

def edit
  @post = Post.find(params[:id])
end

Kontroler načte příspěvek z databáze podle předaného id, a opět jej předá do view v instanční proměnné @post, přičemž view samotné bude vykresleno za pomoci šablony app/views/posts/edit.html.erb:

<h1>Editing post</h1>

<%= render 'form' %>

<%= link_to 'Show', @post %> |
<%= link_to 'Back', posts_path %>
<% end %>

Stejně jako akce new využívá akce edit parciální šablonu form. V tomto případě však bude formulář do aplikace odeslán pomocí (simulovaného) PUT požadavku a tlačítko na odeslání bude označené jako “Update Post”. Tento požadavek v kontroleru zpracuje metoda update. Vypadá takto:

def update
  @post = Post.find(params[:id])

  respond_to do |format|
    if @post.update_attributes(params[:post])
      format.html { redirect_to @post, notice: 'Post was successfully updated.' }
      format.json { head :no_content }
    else
      format.html { render action: "edit" }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
    end
  end

Kontroler nejprve opět pomocí metody find vyhledá v databázi příspěvek podle předaného id. Metodě update_attributes — tu poskytuje modelům rovněž Active Record — předá všechny parametry týkající se příspevku, tedy params[:post]. Pokud vše dopadne dobře, příspěvek je v databázi aktualizován a prohlížeč je přesměrován na stránku s detailem příspěvku. Pokud došlo k chybám — typicky kvůli porušení validačních pravidel —, v prohlížeči je znovu zobrazena stránka s editačním formulářem.

6.12 Smazání příspevku

A konečně, pokud kliknete na odkaz „Destroy“, váš prohlížeč provede (simulovaný) DELETE HTTP požadavek na URL /posts/1. Rails takový požadavek směřují na metodu destroy kontroleru:

def destroy
  @post = Post.find(params[:id])
  @post.destroy

  respond_to do |format|
    format.html { redirect_to posts_url }
    format.json { head :no_content }
  end
end

Tato metoda vyhledá — stejně jako předchozí — příspěvek v databázi a pomocí metody destroy poskytované modelům Active Record jej odstraní z databáze. Pak přesměruje prohlížeč na stránku s výpisem všech příspěvků.

Již dvakrát jsme v tomto návodu zmínili „simulovaný“ HTTP požadavek. Jak jsme uvedli výše, současné verze prohlížečů neumějí — na rozdíl od jiných HTTP klientů, jako je např. curl — odeslat PUT nebo DELETE požadavek. Rails však potřebují pracovat i s těmito HTTP metodami, aby mohly využít celou paletu RESTful architektury. Výše jste si mohli povšimnout například tohoto kódu: link_to ‘Destroy’, … :method => :delete. Když se podíváte na výsledné HTML, uvidíte, že Rails k odkazu přidaly atribut data-method=“delete”. Pro takové odkazy Rails dynamicky vytvoří formulář, který odešle POST požadavek s důležitým parametrem: _method, který je nastaven na hodnotu DELETE. Rails tento parametr berou v úvahu a „přetíží“ jím skutečnou HTTP metodu požadavku (v tomto případě POST). Na tomto příkladě vidíte, jak vás Rails odstiňují od implementačních detailů REST architektury, jak jsme o tom mluvili na začátku.

7 Komentáře pro příspěvky

Když jsme si vyzkoušeli, jak pomocí generátoru přidat do aplikace kompletní entitu, příspěvek, můžeme si zkusit přidat druhou: ta bude sloužit ke správě komentářů k příspěvkům na blogu.

7.1 Vygenerování modelu

Už jste si asi všimli, že modely v Rails používají název v jednotném čísle (a začínají velkým písmenem, neboť jsou to Ruby třídy), zatímco tabulky v databázi jsou nazvány číslem množným. Nový model vytvoříme podobě jako celou entitu „příspěvek“:

$ rails generate model Comment commenter:string body:text post:references

Místo generátoru scaffold jsme využili generátor model. Tento příkaz vygeneruje čtyři soubory:

  • app/models/comment.rb – Model
  • db/migrate/20100207235629_create_comments.rb – Migrace
  • test/unit/comment_test.rb and test/fixtures/comments.yml – Jednotkový test a testovací data

Nejdřív se podívejme na soubor comment.rb:

class Comment < ActiveRecord::Base
  belongs_to :post
  attr_accessible :body, :commenter
end

Vidíte, že je hodně podobný modelu Post v souboru post.rb z dřívějška. Obsahuje navíc řádek belongs_to :post, který deklaruje vazbu (anglicky association) mezi Active Record modely. O vazbách si ještě povíme více v dalších kapitolách tohoto návodu.

Generátor vytvořil k modelu i migraci, která vytvoří tabulku comments v databázi:

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.string :commenter
      t.text :body
      t.references :post

      t.timestamps
    end
    add_index :comments, :post_id
  end
end

Migrace obsahuje deklarace pro textové sloupce commenter a body, podobně jako v případě modelu Post. Řádek t.references deklaruje cizí klíč pro vazbu mezi modelem Post a Comment. Nyní opět spusťte migraci:

$ rake db:migrate

Rails jsou natolik chytré, že spustí pouze migrace, které ještě nejsou v databázi. Proto ve výstupu uvidíte pouze jednu migraci:

==  CreateComments: migrating =================================================
-- create_table(:comments)
   -> 0.0017s
==  CreateComments: migrated (0.0018s) ========================================

7.2 Provázání modelů

Vazby (nebo též asociace či relace) v Active Record vám umožňují snadno definovat vazby mezi modely. V případě příspěvků a komentářů se jedná o tyto vazby:

  • Každý komentář patří k jednomu příspěvku
  • Jeden příspěvek může mít více komentářů

V Rails zapíšete deklarace těchto vazeb velmi podobně:

class Comment < ActiveRecord::Base
  belongs_to :post
  attr_accessible :body, :commenter
end

Tedy: komentář patří k příspěvku. Druhou stranu vazby, v modelu Post, budete muset nastavit ručně:

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title
  
  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }

  has_many :comments
end

Tedy: příspěvek má mnoho komentářů.

Tyto dvě jednoduché deklarace přidají do modelů obrovský kus funkcionality. Kupříkladu, pokud máme v proměnné @post uložen příspěvek, všechny komentáře pro tento příspěvek získáme — jako pole (Array) — zápisem @post.comments.

Více informací o Active Record vazbách najdete v návodu Active Record Associations.

7.3 Přidání pravidla směrování pro komentáře

Stejně jako v případě kontroleru home musíme přidat pravidlo směrování, aby Rails věděly, jaká URL budeme používat pro práci s komentáři. Když otevřete soubor config/routes.rb, v záhlaví uvidíte řádek resources :post, který přidal generátor při vytváření „lešení“ pro příspěvky. Upravte ho takto:

resources :posts do
  resources :comments
end

Tato deklarace vytvoří tzv. vnořenou entitu, tedy vnořenou do entity posts. Pravidla směrování jsou dalším místem, kde se projevuje hierarchie ve vztahu příspěvků a komentářů: komentáře vždy patří k nějakému příspěvku. Když nyní spustíte v terminálu příkaz rake routes, uvidíte řádek:

post_comments GET /posts/:post_id/comments(.:format) ...

URL /posts/:post_id/comments výstižně ukazuje, jak vnořená entita v rámci směrovacích pravidel funguje: komentáře jsou doslova „zanořeny“ do entity posts.

7.4 Vytvoření kontroleru

Jakmile máme hotový model, můžeme obrátit svou pozornost ke kontroleru. Využijeme opět generátor:

$ rails generate controller Comments

Ten vytvoří čtyři soubory a jeden prázdný adresář:

  • app/controllers/comments_controller.rb — Kontroler
  • app/helpers/comments_helper.rb — Soubor s pomocnými metodami pro views
  • test/functional/comments_controller_test.rb — Funkční testy pro kontroler
  • test/unit/helpers/comments_helper_test.rb — Jednotkový test pro helper
  • app/views/comments/ — Adresář pro soubory se šablonami

Jako u každého blogu, čtenáři budou přidávat komentáře k článkům hned po jejich přečtení, a jakmile svůj komentář přidají, budeme je chtít přesměrovat zpět na stránku příspěvku, která bude obsahovat jejich nově přidaný komentář. Kontroler CommentsController proto bude obsahovat metodu pro přidání komentáře a pro smazání komentářů (například proto, že obsahují spam).

Nejprve tedy musíme upravit šablonu pro zobrazení příspěvku (/app/views/posts/show.html.erb) tak, aby umožňovala přidat komentář:

<p class="notice"><%= notice %></p>

<p>
  <b>Název:</b>
  <%= @post.name %>
</p>

<p>
  <b>Titulek:</b>
  <%= @post.title %>
</p>

<p>
  <b>Obsah:</b>
  <%= @post.content %>
</p>

<h2>Přidat komentář:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<%= link_to 'Upravit příspěvek', edit_post_path(@post) %> |
<%= link_to 'Zpátky na příspěvky', posts_path %> |

Jak vidíte, přidali jsme kód s formulářem pro přidání komentáře, která bude směřovat na akci create kontroleru CommentsController. Pojďme ji tedy vytvořit:

class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.create(params[:comment])
    redirect_to post_path(@post)
  end
end

Kód je malinko složitější než analogický kód pro vytvoření příspěvku: to je dáno právě vazbou mezi příspěvkem a jeho komentáři. Když vytváříme nový komentář, musíme vědět, ke kterému příspěvku se vztahuje.

Nejprve tedy z databáze získáme samotný příspěvek, metodou Post.find. Metodu create pak voláme přímo na poli @post.comments, abychom provázali komentář s příspěvkem. Tuto funkcionalitu pro naše modely opět poskytuje Active Record. Jakmile komentář uložíme, přesměrujeme prohlížeč na detail příspěvku, tedy metodu show kontroleru PostsController. Pojďme tedy přidat vypisování komentářů do souboru app/views/posts/show.html.erb:

<p class="notice"><%= notice %></p>

<p>
  <b>Název:</b>
  <%= @post.name %>
</p>

<p>
  <b>Titulek:</b>
  <%= @post.title %>
</p>

<p>
  <b>Obsah:</b>
  <%= @post.content %>
</p>

<hr>

<h2>Komentáře</h2>
<% @post.comments.each do |comment| %>
  <p>
    <%= comment.created_at.to_s(:short) %> napsal <strong><%= comment.commenter %></strong>:
    <q><%= comment.body %></q>
  </p>
<% end %>

<hr>

<h2>Přidejte komentář:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<br />

<%= link_to 'Úprava příspěvku', edit_post_path(@post) %> |
<%= link_to 'Zpátky na příspěvky', posts_path %> |

Nyní můžete na svůj „blog“ přidávat příspěvky i komentáře!

8 Refactoring

Když nám příspěvky a komentáře fungují, měli bychom aplikaci trochu pročistit. Šablona app/views/posts/show.html.erb nevypadá příliš elegantně — vylepšit ji můžeme právě pomocí parciálních šablon.

8.1 Použití parciální šablony pro zobrazení kolekce komentářů

Nejprve musíme z šablony vyjmout HTML kód pro zobrazení příspěvku a uložit ho do parciální šablony v souboru app/views/comments/_comment.html.erb:

<p>
  <b>Commenter:</b>
  <%= comment.commenter %>
</p>

<p>
  <b>Comment:</b>
  <%= comment.body %>
</p>

Pak můžeme v šabloně app/views/posts/show.html.erb zobrazit celou kolekci komentářů speciálním parametrem pro metodu render, <%= render @post.comments %>:

<p class="notice"><%= notice %></p>

<p>
  <b>Jméno:</b>
  <%= @post.name %>
</p>

<p>
  <b>Titulek:</b>
  <%= @post.title %>
</p>

<p>
  <b>Obsah:</b>
  <%= @post.content %>
</p>

<h2>Komentáře</h2>
<%= render @post.comments %>

<h2>Přidat komentář:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

<br />

<%= link_to 'Upravit příspěvek', edit_post_path(@post) %> |
<%= link_to 'Zpět na příspěvky', posts_path %> |

Pro každý komentář z kolekce @post.comments pak Rails vykreslí parciální šablonu app/views/comments/_comment.html.erb, kterou jsme právě vytvořili. Metoda render prochází pole @post.comments a každý komentář vloží do lokální proměnné s názvem comment.

8.2 Použití parciální šablony pro zobrazení formuláře

Pojďme také přesunout sekci „nový komentář“ do vlastního partialu. Vytvořte soubor app/views/comments/_form.html.erb a do něj vložte:

Formulář pro přidání nového formuláře můžeme také přesunout do zvláštní parciální šablony. Vytvořte soubor app/views/comments/_form.html.erb a do něj přesuňte HTML kód:

<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Pak tuto parciální šablonu můžeme vložit do app/views/posts/show.html.erb:

<p class="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @post.name %>
</p>

<p>
  <b>Title:</b>
  <%= @post.title %>
</p>

<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

<h2>Comments</h2>
<%= render @post.comments %>

<h2>Add a comment:</h2>
<%= render "comments/form" %>

<br />

<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %> |

Řádek render "comments/form" udává, že chceme zobrazit parciální šablonu _form.html.erb z adresáře app/views/comments.

Povšimněte si, že zatímco název fyzického souboru parciální šablony na disku začíná podtržítkem, když jej předáváme metodě render, podtržítko vynecháváme.

Objekt @post je dostupný ve všech šablonách, které ve view renderujeme, neboť je deklarován jako instanční proměnná.

9 Mazání komentářů

Důležitou funkcí našeho blogu je mazání komentářů, které obsahují spam. Abychom mohli komentáře mazat, musíme do view přidat odkaz a do kontroleru CommentsController akci destroy.

Začneme přidáním odkazu v šabloně app/views/comments/_comment.html.erb:

<p>
  <b>Commenter:</b>
  <%= comment.commenter %>
</p>

<p>
  <b>Comment:</b>
  <%= comment.body %>
</p>

<p>
  <%= link_to 'Destroy Comment', [comment.post, comment],
               :confirm => 'Are you sure?',
               :method => :delete %>
</p>

Pokud klikneme na tento odkaz, Rails vyvolají pravidlo směrování DELETE /posts/:post_id/comments/:id, které směřuje na metodu destroy kontroleru CommentsController. Pojďme ji tedy přidat:

class CommentsController < ApplicationController

  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.create(params[:comment])
    redirect_to post_path(@post)
  end

  def destroy
    @post = Post.find(params[:post_id])
    @comment = @post.comments.find(params[:id])
    @comment.destroy
    redirect_to post_path(@post), :notice => 'Comment was deleted'
  end

end

Tato metoda vyhledá v databázi příspěvek podle post_id, a vyhledá připojený komentář podle id. Smaže jej z databáze, a přesměruje zpět na stránku příspěvku. Zároveň nastaví notifikaci Comment was deleted, která zobrazena bude na cílové stránce.

9.1 Mazání asociovaných objektů

Pokud vymažeme z databáze příspěvek, komentáře, které jsou k němu připojeny je třeba vymazat rovněž. V opačném případě by jen zabíraly místo v databázi, a v případě složitější aplikace by takové „osiřelé“ komentáře mohly způsobit nesprávné chování.

Rails nám umožňují nastavit mazání asociovaných objektů přímo při deklaraci asociace, bez toho, aniž bychom se o mazání komentářů museli starat sami (např. v metodě PostsController#destroy). Do modelu Post v souboru app/models/post.rb prostě přidáme:

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title

  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }
  has_many :comments, :dependent => :destroy
end

10 Zabezpečení

Pokud bychom nyní spustili svůj blog veřejně, kdokoliv by mohl přidávat, upravovat nebo mazat příspěvky a komentáře. To určitě není to, co chceme.

Rails obsahují podporu pro jednoduchou autentizaci pomocí tzv. HTTP Basic Authentication. Vyzkoušíme si to.

Nejprve přidáme metodu pro HTTP autentizaci do hlavního kontroleru, ApplicationController, v souboru app/controllers/application_controller.rb:

class ApplicationController < ActionController::Base
  protect_from_forgery

  private

  def authenticate
    authenticate_or_request_with_http_basic do |user_name, password|
      user_name == 'admin' && password == 'password'
    end
  end

end

Uživatelské jméno a heslo můžete samozřejmě změnit na jakékoliv jiné. Ve skutečné aplikaci by navíc bylo uloženo spíše v konfiguraci aplikace, než přímo v kódu. Metoda authenticate je dostupná všem kontrolerům, neboť z ApplicationController dědí.

V kontroleru PostsController teď musíme nějak zařídit, aby určité akce byly dostupné každému uživateli aplikace a určité akce pouze přihlášeným uživatelům. K tomu můžeme velmi dobře využít tzv. before_filter, který nám umožnuje specifikovat, že před všemi (anebo jen některými) akcemi kontroleru musí Rails zavolat určitou metodu; v našem případě metodu authenticate:

class PostsController < ApplicationController

  before_filter :authenticate, :except => [:index, :show]

  # Zbytek kódu kontroleru

Díky čitelnosti Ruby je deklarace poměrně jasná: před každou akcí kontroleru zavolej metodu authenticate, pokud se nejedná o metody index a show (ty zobrazují obsah našeho blogu návětěvníkům).

Rovnež mazání komentářů chceme povolit pouze přihlášeným uživatelům, proto do CommentsController doplníme:

class CommentsController < ApplicationController

  before_filter :authenticate, :only => :destroy

  # Zbytek kódu kontroleru

Pokud nyní kliknete např. na odkaz „New Post“, zobrazí se vám dialogové okno s výzvou pro přihlášení:

Basic HTTP Authentication Challenge

Zkuste do něj vyplnit správné a nesprávné přihlašovací údaje a podívejte se, co se stane.

Využití HTTP Basic Authentication slouží pouze jako výukový příklad. Ve skutečné aplikaci, kde chcete zabezpečit přístup k určitým částem vaší aplikace, využijete spíše některé z hotových řešení, jako je např. gem Clearance. Ten vám poskytuje nejenom infrastrukturu pro autentizaci uživatele pomocí session, ale též infrastrukturu a HTML šablony pro registraci nového uživatele, zaslání zapomenutého hesla, a další. Jeho použití je velmi jednoduché, podívejte se do jeho README.

11 Formuláře pro úpravu více modelů najednou

Poměrně běžnou funkcí každého blogu je označování příspěvků štítky (angl. tag). Abychom mohli tuto funkci přidat do našeho blogu, musíme být schopni v jednom formuláři upravit více modelů. A právě takové zanořené formuláře Rails podporují.

Vyzkoušíme si to tak, že ve formuláři, kde přidáváme nový příspěvek, jej budeme moci označit několika štítky. Nejprve vytvoříme pomocí generátoru model Tag, který bude reprezentovat štítek:

$ rails generate model tag name:string post:references

Opět musíme následně spustit migrace pro vytvoření tabulky tags v databázi:

$ rake db:migrate

Nyní musíme — stejně jako v případě komentářů — upravit i model Post v souboru app/models/post.rb, abychom deklarovali vztah mezi příspěvky a tagy i „z druhé strany“ a abychom dali pomocí deklarace accepts_nested_attributes_for Rails vědět, že chceme štítky editovat zároveň s příspěvky:

class Post < ActiveRecord::Base
  attr_accessible :content, :name, :title, :tags_attributes

  validates :name,  :presence => true
  validates :title, :presence => true,
                    :length => { :minimum => 5 }

  has_many :comments, :dependent => :destroy
  has_many :tags

  accepts_nested_attributes_for :tags, :allow_destroy => :true,
    :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
end

Volba allow_destroy v deklaraci pro vnořené atributy říká Rails, že mají v šabloně, kterou za chvíli vytvoříme, zobrazit checkbox pro odstranění štítku. Volba reject_if zabrání uložení štítku, který má všechny atributy prázdné.

Všimněte si také, že nesmíme zapomenout přidat :tags_attributes do seznamu povolených attr_accessible. Bez toho by při pokusu o úpravu štítků z modelu Post docházelo k výjimce MassAssignmentSecurity.

Nyní upravíme šablonu views/posts/_form.html.erb tak, abychom mohli k příspěvku přidat štítky:

<% @post.tags.build %>
<%= form_for(@post) do |post_form| %>
  <% if @post.errors.any? %>
  <div id="errorExplanation">
    <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
    <ul>
    <% @post.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
  <% end %>

  <div class="field">
    <%= post_form.label :name %><br />
    <%= post_form.text_field :name %>
  </div>
  <div class="field">
    <%= post_form.label :title %><br />
    <%= post_form.text_field :title %>
  </div>
  <div class="field">
    <%= post_form.label :content %><br />
    <%= post_form.text_area :content %>
  </div>
  <h2>Tags</h2>
  <%= render :partial => 'tags/form',
             :locals => {:form => post_form} %>
  <div class="actions">
    <%= post_form.submit %>
  </div>
<% end %>

Na prvním řádku pomocí @post.tags.build inicializujeme kolekci štítků pro příspěvek, který vytváříme nebo upravujeme.

Na dalším řádku si povšimněte jiného názvu proměnné obalující formulář, post_form_ místo pouhého f. Tuto změnu jsme provedli, aby bylo pochopitelnější, co se v celém procesu děje.

Také jsme využili schopnosti metody render předávat do parciálních šablon proměnné pod jinými názvy: v našem případě post_form jako form (viz řádek :locals => {:form => post_form}).

Nyní vytvořte adresář app/views/tags a v něm soubor _form.html.erb, který bude obsahovat formulář pro štítek:

<%= form.fields_for :tags do |tag_form| %>
  <div class="field">
    <%= tag_form.label :name, 'Tag:' %>
    <%= tag_form.text_field :name %>
  </div>
  <% unless tag_form.object.nil? || tag_form.object.new_record? %>
    <div class="field">
      <%= tag_form.label :_destroy, 'Remove:' %>
      <%= tag_form.check_box :_destroy %>
    </div>
  <% end %>
<% end %>

Pro zobrazení formuláře zde využíváme pomocné metody Rails fields_for.

Nakonec musíme upravit šablonu pro zobrazení detailu příspěvku (views/posts/show.html.erb) tak, aby vypisovala příslušné štítky:

<p class="notice"><%= notice %></p>

<p>
  <b>Název:</b>
  <%= @post.name %>
</p>

<p>
  <b>Titulek:</b>
  <%= @post.title %>
</p>

<p>
  <b>Obsah:</b>
  <%= @post.content %>
</p>

<p>
  <b>Štítky:</b>
  <%= @post.tags.map { |t| t.name }.join(", ") %>
</p>

<h2>Komentáře</h2>
<%= render @post.comments %>

<h2>Přidejte komentář:</h2>
<%= render "comments/form" %>


<%= link_to 'Upravit příspěvek', edit_post_path(@post) %> |
<%= link_to 'Zpět na příspěvky', posts_path %> |

Když si nyní aplikaci proklikáte, zjistíte, že můžete editovat příspěvky i štítky, tedy dva odlišné modely, v rámci jediného formuláře v jediném view.

Řádek @post.tags.map { |t| t.name }.join(", ") ale v šabloně vypadá docela nepatřičně. Views by neměly obsahovat žádnou aplikační logiku, protože to ztěžuje znovupoužitelnost, testovatelnost a především to ztěžuje čitelnost kódu.

Ukážeme si, jak pro zpřehlednění podobného kódu využít pomocné metody pro šablony (angl. view helpers).

12 Pomocné metody pro šablony

Soubory s pomocnými metodami se nacházejí ve složce app/helpers. Umožňují nám definovat metody, které jsou dostupné v šablonách a lze je používat opakovaně. V našem případě chceme spojit dohromady název (atribut name_) několika objektů čárkami, tedy něco jako jedna, dvě, tři. Jelikož chceme tuto metodu využít při zobrazování příspěvků, vložíme ji do modulu PostsHelper v souboru app/helpers/postshelper.rb:

module PostsHelper
  def join_tags(post)
    post.tags.map { |t| t.name }.join(", ")
  end
end

Nyní tuto metodu využijeme pro zobrazení štítků v šabloně app/views/posts/show.html.erb:

<p class="notice"><%= notice %></p>

<p>
  <b>Název:</b>
  <%= @post.name %>
</p>

<p>
  <b>Titulek:</b>
  <%= @post.title %>
</p>

<p>
  <b>Obsah:</b>
  <%= @post.content %>
</p>

<p>
  <b>Štítky:</b>
  <%= join_tags(@post) %>
</p>

<h2>Komentáře</h2>
<%= render @post.comments %>

<h2>Přidat komentář:</h2>
<%= render "comments/form" %>


<%= link_to 'Upravit příspěvek', edit_post_path(@post) %> |
<%= link_to 'Zpět na příspěvky', posts_path %> |

Vidíte, že výsledný kód je mnohem přehlednější, nemluvě o tom, že správné chování této metody můžeme velmi snadno automatizovaně testovat (viz třída PostsHelperTest v souboru test/unit/helpers/posts_helper_test.rb)

13 Kam dál?

Za pomoci tohoto průvodce jste vytvořili webovou aplikaci, jednoduchý blog, který návštěvníkům umožňuje prohlížení příspěvků a přidávání komentářů, zatímco přihlášeným uživatelům správu příspěvků a komentářů. Začali jsme s prázdnou Rails aplikací a postupně se dozvěděli, jak lze využít zabudované generátory pro rychlejší vytvoření součástí aplikace, jak mezi sebou propojit Active Record modely, jak provázat Active Record modely a HTML formuláře nebo jak zpřehlednit kód aplikace využitím parciálních šablon a pomocných metod.

Mnoho věcí jsme samozřejmě museli vynechat — ať již se jedná o složitější dotazy do databáze nebo vztahy mezi modely, pokročilejší konfigurace pravidel směrování, apod. Připomínáme proto, že mnoho dalších návodů v angličtině naleznete na adrese http://guides.rubyonrails.org. Dokumentaci k jednotlivým součástem Rails naleznete na adrese http://railsapi.com.

Mezi další zdroje informací v češtině, které můžete využít, patří zejména:

14 O tomto dokumentu

Tento návod je českým překladem anglického originálu, který naleznete na adrese http://guides.rubyonrails.org/getting_started.html. Zdrojový text anglického originálu naleznete v repositáři Rails na serveru Github.

Zdrojový text tohoto překladu naleznete v repositáři rubyonrails-cz/docrails (větev czech) na serveru Github.

Historii tohoto dokumentu a přehled autorů naleznete ve výpisu revizí na serveru Github.

Budeme vám vděční za připomínky k překladu. Využijte pro ně prosím Issues připojených k repositáři na serveru Github.

Opravy, doplňky nebo vylepšení překladu nám můžete posílat prostřednictvím Fork Queue připojené k repositáři na serveru Github.

Postup je jednoduchý:

  • Klikněte na odkaz “Fork” u repositáře rubyonrails-cz/docrails, abyste vytvořili kopii repositáře provázanou s původním.
  • Pracujte ve větvi czech, na rozsáhlejší úpravy vytvořte tematickou větev (např. adding_ajax_chapter).
  • Dodržujte prosím zvyklosti předchozích autorů, ať již se týkají samotného překladu, slovotvorby, používání anglických výrazů nebo formátu commit message.
  • Zašlete správcům repositáře požadavek na vložení vaší práce pomocí tzv. Pull Request

Uvítáme též překlady “ostatních návodů”:http://edgeguides.rubyonrails.org/ z angličtiny. Nejdříve si prosím v diskusní skupině ověřte, že na daném překladu již nepracuje někdo jiný. Chceme vás upozornit, abyste překladu věnovali dostatečnou péči a pozornost. Překlady provedené nedbale, připomínající automatický překlad, převádějící otrocky anglický originál do češtiny nemají velké šance na přijetí.

Pro překlad nového návodu platí stejná pravidla jako pro úpravy návodu uvedené výše — v každém případě vytvořte pro vaši práci novou větev, např. active_record_basics. Pravidelně synchronizujte zdrojový anglický překlad s mateřským repositářem.

Pro diskusi překladů prosím využijte diskusní fórum nebo kanál.