Ruby on Rails (12-14) 2008/2009
Z JASR Wiki
<<< Powrót do Tworzenie aplikacji wielowarstowych 2008/2009
Spis treści
|
Wprowadzenie
Ruby on Rails (RoR) jest to framework przystosowany do tworzenia aplikacji webowych w schemacie MVC w jezyku Ruby. Główne założenia projektu to:
- szybkość pisania kodu (nie powtarzmy raz podanej informacji)
- konwencja nad konfiguracją - jak najmniej konfiguracji - promujemy konwencje i wzorce domyślne
Ruby
Ruby to interpretowany, w pełni obiektowy i dynamicznie typowany język programowania stworzony w 1995 roku przez Yukihiro Matsumoto (pseudonim Matz).
Cechy szczególne
Ruby posiada:
- automatyczne odśmiecanie pamięci
- iteratory
- przeciążanie operatorów
- obsługa wyjątków
- wyrażenia regularne wbudowane w składnię
- liczby całkowite o dowolnych rozmiarach
- dodawanie metod do instancji klasy - możliwa jest zmiana lub dodanie metody do instancji danej klasy
- przekazywanie funkcji jako parametrów
- rozpoznawanie typów na podstawie ich zachowania, a nie deklaracji
- moduły - rodzaj wielodziedziczenia pozwalający włączyć gotową implementację zbioru metod do danej klasy
- możliwość zmiany praktycznie wszystkiego (w wyniku możliwości dokonywania zmian w już zadeklarowanych klasach)
Instalacja
- Linux - zainstalowny w większości dystrubycji, również na komputerach w labolatorium
- Windows - instalator
Interpretery
- Ruby - oryginalna w C
- JRuby - ruby na JVM
- IronRuby - ruby na .NET
Przydatne narzędzia
- ri - pomoc, np. "ri String" opisze klasę String
- irb - ruby jako interpreter - przydatny do testowania przykładów poniżej
- RubyGems - menadżer pakietów dla Ruby obsługujący zależności
Wprowadzenie do języka
Przykłady poniżej pochodzą z podręcznika.
Podstawy
# komentarz 1-linijkowy
=begin
komentarz wielolinijkowy
znak '=' musi być w pierwszej kolumnnie!
=end
#wypisz na wyjście
puts "ala ma kota"
#wszystko jest obiektem (zamień na system binarny - w postaci stringa)
345.to_s(2)
#liczby mogą być dowolnego rozmiaru
c = 1000**100
puts c
Definicja funkcji
Możliwości:
- definiowanie funkcji z dowolną ilością argumentów
- przekazywanie bloków kodu do funkcji
- specyfikowanie domyślnych wartości
def sayGoodnight(name = "John")
result = "Goodnight, " + name
return result
end
# Time for bed...
puts sayGoodnight
puts sayGoodnight("Mary")
Tablice
a = [ 1, 'cat', 3.14 ]
puts a[1]
#pusta tablica
empty1 = []
empty2 = Array.new #konstruktor z klasy Array
#notacja skrótowa dla słów
a = %w{ ant bee cat dog elk }
#tablica asocjacyjna
instSection = {
'cello' => 'string',
'clarinet' => 'woodwind',
'drum' => 'percussion',
'oboe' => 'woodwind',
'trumpet' => 'brass',
'violin' => 'string'
}
puts instSection['oboe']
Instrukcje warunkowe, pętle
i = 0
while i < 10
if i % 2 == 0
puts '2'
elsif i % 3 == 0
puts '3'
else
puts 'n'
end
i+=1
end
Można też używać notacji odwrotnej:
puts "test" if true
i = 0
begin
i+=1
puts "test"
end while i < 10
Symbole
Ruby zawiera obiekt nazwany symbolem. Symbol jest zawsze poprzedzony dwukropkiem, po utworzeniu symbolu używana jest jego jedna początkowa kopia. Symbol można konwertować do stringa i na odwrót.
Bloki
W ruby istnieje pojęcie bloku kodu który w rzeczywistości jest funkcją nienazwaną. Bloki mogą przyjmować argumenty, być przekazywane do funkcji. Istnieje specjalna funkcja yield która wykonuje przekazany do funkcji blok.
Blok definiuje się elementami do kod end lub też przez nawiasy klamrowe. Jeśli metoda przyjmuje blok najpierw należy przekazać w nawiasie () argumenty zwykłe a potem blok, tablice asocjacyjne trzeba przekazywać jako argumenty w nawiasach ().
a = %w{ ant bee cat dog elk }
#przekaż blok z argumentem
a.each do |word|
puts word
end
#metoda each z Array mogła by być zaimplementowana następująco
def each
for each element
yield(element) #sposób na przekazanie argumentu do wykonywanego bloku
end
end
Definiowanie klas
Atrybuty i metody
Przez @nazwa oznacza się atrybuty instacji klasy, przez @@nazwa oznacza się atrybuty klasy. Nie trzeba wprost informować o istnieniu atrybutów, wystarczy że odwołania do nich pojawią się w kodzie metody.
Do klasy można się odwołać tylko przez metodę, nie ma czegoś takiego jak bezpośredni dostęp do atrybutu.
class Song
@@instances = 0 #atrybut klasowy
#inicjalizacja wywoływana automatycznie po utworzeniu obiektu metodą klasową new
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
@@instances += 1
end
def name
@name
end
def artist
@artist
end
def duration
@duration
end
def name=(value)
@name = value
end
def artist=(value)
@artist = value
end
def duration=(value)
@duration = value
end
end
#z klasy można korzystać w następujący sposób:
aSong = Song.new("Bicylops", "Fleck", 260)
aSong.duration = 257 #wykorzystanie settera
aSong.duration #wykorzystanie gettera, zwróci 257
W kodzie powyżej często pojawia się ten sam schemat - gettery i settery - istnieją bardziej zwięzłe notacje:
class Song
attr_reader :name, :artist, :duration #tworzy gettery
attr_writer :name, :artist, :duration #tworzy settery
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
end
end
Można też użyć "attr" aby utworzyć getter oraz true lub false w zależności czy ma być utworzony setter:
class Song
attr :name, true
attr :artist, true
attr :duration, true
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
end
end
Można rozbić definicje klasy - wtedy można korzytać tylko z metod które zostały już zdefiniowane:
class Song
attr :name, true
attr :artist, true
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
end
end
aSong = Song.new("Bicylops", "Fleck", 260)
aSong.name = "Aa"
class Song
attr :duration, true
end
aSong.duration = 45
Domyślnie metody są publiczne, oprócz initialize która jest zawsze prywatna (wywołuje ją new). Zakres dostępności można ustawiać:
class Song
protected
attr :name, true
attr :artist, true
public
attr :duration, true
#metoda initialize pozostaje prywatna
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
end
end
lub inaczej:
class Song
attr :name, true
attr :artist, true
attr :duration, true
def initialize(name, artist, duration)
@name = name
@artist = artist
@duration = duration
end
protected :name, :name=, :artist, :artist=
end
Dziedziczenie
Dziedziczenie oznacza się symbolem "<". Rozszerzmy klasę Song dodając atrybut lyrics:
class KaraokeSong < Song
attr :lyrics, true
def initialize(name, artist, duration, lyrics)
super(name, artist, duration)
@lyrics = lyrics
end
end
Użyteczne linki
Rails
Instalacja
W labolatorium w systemie Linux nie trzeba niczego instalować.
- Rails: Do instalacji najlepiej posłużyć się wspomnianym wcześniej narzędziem dla ruby: RubyGems. Wystarczy wydać komende gem install rails --include-dependencies
- SQLite: Przykłady poniżej będą potrzebowały obsługi bazy danych SQLite, aby ją zainstalować wystarczy wydać komende gem install sqlite3-ruby, pakiet sqlite3-ruby umożliwia obsługę bazy SQLite
- Rake: Program typu ant lub też make, instalacja poprzez gem install rake.
Szybki start
Główne cechy Rails zostaną przedstawione na przykładzie tworzenia prostego bloga.
Aby utworzyć nowy projekt wystarczy wydać polecenie:
rails blog
Gdzie blog to nazwa projektu, w katalogu bieżącym zostanie utworzony katalog blog wraz z wygenerowaną zawartością. Wśród wygenerowanych plików jest skrypt uruchamiający serwer:
cd blog
ruby script/server
Serwer uruchamia się domyślnie startując usługę http na porcie 3000, moża podać port wprost parametrem -p. Pod adresem http://localhost:3000/ powinna pojawić się strona startowa Rails.
Struktura wygenerowanego projektu
Rails jest frameworkiem opierającym się na architekturze MVC, czyli zakres odpowiedzialności zostaje rozdzielony w następujący sposób:
- model - odpowiedzialny za zapis, odczyt danych
- kontroler - odpowiedzialny za wykonywanie operacji (logiki biznesowej), przygotowanie widoku, odbieranie danych od widoku
- widok - odpowiedzialny za prezentacje danych, pobranie danych od użytkownika
W związku z tym Rails odpowiednio generuje strukture projektu:
- app/models - MVC: modele
- app/controllers - MVC: kontrolery
- app/views - MVC: widoki
- app/helpers - MVC: funkcje pomocne dla widoków
- db/migratinos - pliki migracji (generacja tabel w bazie danych)
- config - pliki konfiguracyjne - routes.rb, database.yml
- script - skrypty rails - generatory, obsługa serwera itp.
- log - logi
- public - grafiki, elementy html
- public/javascript - pliki JS
- test - miejsce na składowanie klas testujących
- doc - dokumentacja generowana przez ri
- vendor - katalog dla wtyczek i dodatków
- tmp - katalog tymczasowy
Baza danych
Aby móc zapisywać modele musimy skonfigurować bazę danych, Rails potrafi obsługiwać następujące silniki baz danych:
- MySQL
- PostgreSQL
- Oracle
- Microsoft SQL Server
- Sqlite (w wersji 2.x i 3.x)
- IBM DB2
- OpenBase
- Sybase
Będziemy korzystać z SQLite 3 w tym celu należy wyedytować plik konfiguracyjny config/database.yml, powiniec wyglądać jak poniżej:
development:
adapter: sqlite3
encoding: utf8
database: db/development.sqlite3
pool: 5
timeout: 5000
test:
adapter: sqlite3
encoding: utf8
database: db/test.sqlite3
pool: 5
timeout: 5000
production:
adapter: sqlite3
encoding: utf8
database: db/production.sqlite3
pool: 5
timeout: 5000
Zmienna RAILS_ENV ustawiana komendą rake ustala w jakim środowiku w danej chwili jesteśmy (domyślnie development).
Przepływa sterowania w RoR
Diagram prezentuje przepływ sterowania. Kolejne etapy wyglądają następująco:
Wstępna obsługa żądania
Serwer dostaje żądanie URL postaci [serwer][aplikacja]/[k1]/.../[kn]/item, czyli w takiej postaci jak by to było żądanie statycznego zasobu zagłębionego w n katalogach. Oczywiście nie będziemy rozkładać skryptów w ten sposób, żądanie takie kierowane jest do Dispatchera który zajmuje się przekształeceniem takiego żądania na konkretny kontroler i jego metodę (kontrolery są obiektami Ruby). Istnieje konwencja aby URL miał postać [serwer][aplikacja]/[kontroler]/[metoda] np: http://url.com/posts/new gdzie posts jest nazwą kodową kontrolera postów (w rzeczywistości nazywa się PostsController) a new jego metodą. Plik routes.rb definuje sposób przekształcania URL.
Etap kontrolera
Kontroler po obsłudze żądania (odpowiednia jego metoda została wywołana przez Dispatcher) może przekierować żądanie do innego punktu <kontroler,metoda> lub też przygotować dane do wyświetlenia w postaci swoich atrybutów i wyświetlić widok swój (przypisany krotce <kontroler,metoda>) lub inny.
Wyświetlenie odpowiedzi
Wybrany widok jest użyty do generacji odpowiedzi, korzysta z uprzednio przygotowanych przez kontroler atrybutów.
Generacja kontrolera, modelu i widoku
W Rails modele, widoki, kontrolery itd. generuje się za pomocą specjalnego skryptu "generate", skrypt potrafi generować każdy element z osobna jak i np. jednocześnie cały MVC dotyczący jakiegoś modelu (np. obługa postów).
Wygenerujemy model, kontroler i widok dla postów (post ma tytuł typu string i treść typu text):
ruby script/generate scaffold Post title:string body:text
Aby dokończyć CRUD dla postów musimy wygenerować tabelę w bazie danych (majac pliki ją generujące), wystarczy wydać polecenie:
rake db:migrate
Polecenie sprawdzi jakie nowe elementy się pojawiły i zaktualizuje strukture tabel. Możemy przystąpić do edycji postów, wystarczy wejść pod adres: http://localhost:3000/posts/
Chcielibyśmy też móc komentować posty, wygenerujemu więc też CRUD dla komentarzy który będziemy później intergrować z postami (komentarz powinien posiadać identyfikator posta do którego należy):
ruby script/generate scaffold Comment post_id:integer body:text
rake db:migrate
Pod adresem http://localhost:3000/comments/ można zobaczyć CRUD dla komentarzy, umożliwia np. edycje identyfikatora postu.
Migracje
W katalogu db/migrate są pliki migracyjne dla odpowiednio modelu post i comment. Dla post plik ma postać:
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end
def self.down
drop_table :posts
end
end
Pliki migracyjne odpowiadają za tworzenie struktury bazy danych dla modeli. Każdy plik ma metodę UP wprowadzającą zmieną oraz DOWN wycofującą (w miare możliwości) zmianę.
Migracje można generować skryptem: ruby script/generate migration create_posts
Nazewnictwo
Warto zauważyć z tabela nazywa się posts a przecież tworząc CRUD dla postów użyliśmy słowa kodowego post: jest to związane z konwencją przyjętą w Rails - nazwy modeli piszemy w liczbie pojedyńczej a nazwy tabel (i migracji) ich dotyczących w liczbie mnogiej - Rails zrobił to automatycznie.
Indeksy
Rails nie dodaje sam indeksów, trzeba je dopisać ręcznie. Np. jeśli chcielibyśmy wyszukiwać posty po tytule należało by dopisać:
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
add_index :posts, [:title]
end
def self.down
drop_table :posts
end
end
Złączenia
Struktura złączeń (np. post_id w tabeli comments) jest określona przez konwencje - musimy się jej trzymać aby Rails odpowiednio wykorzystał do złączeń naszą strukture bazy. Jeśli odpowiednio zaadnotujemy modele Post i Comment Rails automatycznie wykorzysta pole post_id z tabeli comments - także nie możemy zmienić jego nazwy.
W przypadku złączeń wiele do wielu musielibyśmy przykładowo dodać tabele złączeniową o nazwie comments_posts z polami post_id i comment_id, uzupełnić jej zawartość, usunąć kolumne post_id z comments.
Wtedy migracje wyglądały by następująco:
ruby script/generate migration CreateCommentsPosts
Plik mógłby mieć postać:
class CreateCommentsPosts < ActiveRecord::Migration
def self.up
class Comment < ActiveRecord::Base
end
create_table :comments_posts, :id => null do |t|
t.integer :comment_id
t.integer :post_id
end
comments = Comment.find(:all)
for comment in comments
execute "insert into comments_posts values (#{comment.id}, #{comment.post_id})"
end
remove_column :comments, :post_id
end
def self.down
.....
end
end
Model
W katalogu app/models są wygenerowane modele. Każdy model reprezentuje pewną isntancję danych np. post lub komentarz. Nazwy modeli są zawsze w liczbie pojedyńczej, tabela odpowiadająca modelowi ma nazwę w liczbie mnogiej. Przy tworzeniu CRUD-a Rails automatycznie generuje migracje czyli i nazwy tabel np: post ==> posts, comment ==> comments ale też person ==> people.
W modelach nie określna się atrybutów (była by to redundacja informacji mając migracje).
Złączenia
Modele łączą się ze sobą na różny sposób, informacja o złączeniu pozwala:
- mając zmienną np. typu Post pobrać listę komentarzy związanych z postem jeśli zadeklarujemy że post posiada komentarze
- ułatwia dodawanie danych, odpowiednie identyfikatory w tabelach złączeniowych są uzupełniane automatycznie
Aby złączenia mogły funkcjonować muszą istnieć odpowiednio nazwane tabele, np. jeśli komentarz należy do posta to musi posiadać pole post_id, w przypadku złączenia wiele do wielu musie istnieć odpowiednia tabela złączeniowa.
Typy złączeń (każda ze stron złączenia musi określić swoją relację):
- has_one <symbol w l. poj.>
- belongs_to <symbol w l. pol.>
- has_many <symbol w l. mn.>
- has_and_belongs_to_many <symbol w l. mn.>
Strona zawierająca może dodatkowo dopisywać atrybuty przenoszenia akcji na element zawierany, np:
- :dependent => :destroy - przy usuwaniu zniszcz zawierane elementy
- :dependent => :nullify - przy usuwaniu ustaw zawieranym elementom klucz obcy na NULL
Więcej na temat złączeń: http://guides.rubyonrails.org/association_basics.html
Walidacja
W klasie modelu można dodawać linijki kodu nakazujące walidację obiektu, jest wiele typów walidacji, np:
- validates_confirmation_of :email
- validates_presence_of :symbol_atrybutu
- validates_numericality_of :games_played, :only_integer => true
- validates_length_of :password, :in => 6..20
Więcej na temat walidacji:
- Pełna lista z opisami: http://api.rubyonrails.org/ - na liście metod zaczynającyh się od validate
- Manual: http://guides.rubyonrails.org/activerecord_validations_callbacks.html
- Ćwiczenie 1: Dodaj odpowiednie adnotacje do modelu Post i Comment (komentarz należy do postu, post ma wiele komentarzy, komentarze powinny być usunięte po usunięciu posta) oraz do modelu Post dodaj sprawdzanie istnienia tytułu i treści posta.
Rozwiązanie
Kontroler
Kontrolery są odpowiedzialne za:
- logikę biznesową
- przygotowywanie widoków (np. widok edycji przygotowywany przez metodę edit pobierającą obiekt do edycji z bazy i ustawiającą go na atrybucie)
- odbieranie danych (np. metoda create odbiera obiekt i zapisuje do bazy danych)
Krotka <kontroler, metoda> ma przypisany widok, może przekierowywać żądanie do innej krotki lub wyświetlić widok (swój lub innej krotki). Kontrolery dziedziczą z klasy Application, tam można umieszcać wspólny kod.
W kontrolerach można definiować filtry (metody after_filter, before_filter) przed lub po metodzie, pierwszy argument to nazwa metody do wykonania, drugi to wykluczone (:except) lub jedyne wybrane metody (:only) (jest opcjonalny) np.
after_filter :loguj, :except => [:create, :new]
Czyli po wykonaniu metod oprócz create i new uruchom logowanie.
Można też zadeklarować skip_before_filter, zostanie uruchomiony przed wszystkimi innymi filtrami.
- Ćwiczenie 2: W kontrolerze PostController do wszystkich metod oprócz index i show dodaj sprawdzanie czy użytkownik jest zalogowany a jeśli nie jest powienien wyświetlić się monit o podanie loginu i hasła administratora. Użyj metody authenticate_or_request_with_http_basic której należy przekazać blok przyjmujący parametr login, hasło i zwracający true lub false w zależności czy login, hasło są akceptowane, metoda sprawdza czy użytkownik jest zalogowany, jeśli nie to wyświetla monit.
Rozwiązanie
- Ćwiczenie 3: W kontrolerze CommentController potrzebna nam będzie tylko metoda create, zmodyfikuj create tak aby poprawnie ustawiała komentarzowi id posta. Załóż że kontroler dostaje dodatkowy paramter post_id, na koniec kontroler powinien przekierować użytkownika do posta. Do utworzenie komentarza użyj metody Post.comments.create! <obiekt Comment> (możemy pobrać kolekcje komentarzy z posta). W jaki sposób pobrać post z bazy danych, argumenty z żądania oraz zrobić przekierowanie sprawdź w PostController - metoda update.
Rozwiązanie
Mapowanie
Plik config/routes.rb definuje jak zamieniać adresy URL na konkretne kontrolery, metody i ich argumenty. W wygenerowanym pliku są dwa automatycznie wygenerowane wpisy dotyczące zasobów:
map.resources :comments
map.resources :posts
Są to pewne skróty notacyjne, np. map.resources :photos jest równoważne wprowadzeniu reguł w tabeli.
oraz wpisy końcowe w przypadku kiedy żaden poprzedni nie działa:
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
zachowujące konwencje określania kontrolera i metody.
Ponieważ chcemy aby komentarze należały do postów oraz żeby łatwo kojarzyć komentarz z postem zdefiniujemy dostęp do komentarzy za pomocą zagłębienia w poście do którego komentarz należy, np. komentarz 5 postu 2 miałby adres: /post/2/comment/5.
Wpisy dotyczące zasobów zamieniamy jednym wpisem:
map.resources :posts, :has_many => :comments
Jest to skrót notacyjny od:
map.resources :posts do |post|
post.resources :comments
end
Dodatkowo możemy określic co ma być stroną startową aplikacji, dodajemy wpis:
map.root :controller => "posts", :action => "index"
Aby routing zadziałał trzeba usunąc plik public/index.html
Teraz przejście pod adres http://localhost:3000/ przeniesie nas do listy postów.
Polecenie rake routes wypisze wszyskie zdefiniowane przekierowania.
Widok
Widok odpowiedzialny za wyświetlanie danych oraz ich pobieranie od użytkownika jest wydzielony dla każdej akcji kontrolera, pliki o nazwie danej akcji zapisuje się w katalogu o nazwie kontrolera. Widok jest stroną html ze wstawkami Ruby: kod ruby umieszczamy w <% %>, natomiast w <%= %> umieszczamy wyrażenie którego wynik zostanie wstawiony w html.
Wyświetlanie widoku
Domyślnie po zakończeniu akcji Rails wyświetla widok przypisany do tej akcji (według kontrolera i nazwy akcji). Do zmiany domyślnego zachowania służą np.:
- render :nothing => true - nie wyświetlaj nic
- render :controller => 'kontroler', :action =>'akcja' - renderuj widok akcji akcja kontrolera kontroler
- render :text => "OK" - zwróć tekst
- render :json => @product - zwróć w formacie JSON
- redirect_to posts_path - przekieruj do indeksu postów
Widoki częściowe
Istnieje możliwość stworzenia fragmentu (partial) widoku i włączania go do innego widoku. Partiale umieszcza się w plikach zaczynających się od _ w nazwie, nazwa pliku (bez _ i rozszerzenia) to nazwa partiala.
Włączanie partiali:
- Partiale nazwane można włączyć do widoku metodą render :partial => 'partial' aby włączyć wskazany _partial.html.erb.
- Można również za pomocą komendy render :partial => @post renderować model, wtedy używany jest partial o nazwie _post.html.erb i dostaje on obiekt Post, jeśli podamy kolekcję można tak jak poniżej wyświetlić całą listę używając partiala dla pojedyńczego elementu.
Popraw widok postu tak aby wyświetlał komentarze oraz formularz dodawania nowego komentarza:
Plik app/views/posts/show.html.erb powinien wyglądać następująco:
<p>
<b>Title:</b>
<%=h @post.title %>
</p>
<p>
<b>Body:</b>
<%=h @post.body %>
</p>
<p>
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'See All Posts', posts_path %>
</p>
<h2>Comments</h2>
<div id="comments">
<%= render :partial => @post.comments %>
</div>
<% form_for [@post, Comment.new] do |f| %>
<p>
<%= f.label :body, "New Comment"%><br/>
<%= f.text_area :body %>
</p>
<p><%= f.submit "Add comment" %></p>
<% end %>
Plik ten odwołuje się do widoku częściowego, należy go utworzyć:
Plik app/views/comments/_comment.html.erb powinien wyglądać następująco:
<% div_for comment do %>
<p>
<strong>Posted <%= time_ago_in_words(comment.created_at) %></strong>
<br/>
<%= h(comment.body) %>
</p>
<% end %>
Szablony
Szablony pozwalają wpisać w jeden plik elementy pojawiające się na wielu stronach. Domyślnie oddzielny szablom jest tworzony dla każdego kontrolera (w katalogu app/views/layouts) wystarczy go skasować aby wykorzystywany był ogólny szablon application.html.erb.
W szablonach wstawiamy metodę yield która jest potem podmieniana widokiem, więcej informacji o szablonach.
Do metody render można dodać nazwe szablonu jaki zostanie użyty - argument :layout.
Ajax
Rails ma wbudowane wsparcie dla Ajax, wystarczy zdefiniować do której akcji, kontrolera ma się odwołąc element na stronie, zdefiniować który element ma zostać zaktualizowany oraz oprogramowac odpowienią akcję w kontrolerze.
Prosty przykład
Zmienimy dodawanie komentarzy na zdarzenie w JavaScript, tak aby dodanie komentarza nie przeładowywało strony.
Należy dodać deklaracje biblioteki JS do pliku app/views/layouts/posts.html.erb (w elemencie head):
<%= javascript_include_tag :all %>
W pliku app/views/posts/show.html.erb formularz form_for zmieniamy na remote_form_for (przy wyłączonym JS działa jak form_for, przy włączonym zamiast przeładowywać stronę wysyła zpaytanie do serwera).
Zmieniamy CommentsController na:
class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.create!(params[:comment])
respond_to do |format|
format.html { redirect_to @post }
format.js
end
end
end
Kontroler inaczej reaguje na zapytanie JS - przechodzi do domyślnego widoku.
Dodajemy domyślny widok dla metody create w formacie JS który zostanie pobrany i uruchomiony na stronie pytającej:
Plik: app/views/comments/create.js.rjs.
page.insert_html :bottom, :comments, :partial => @comment
page[@comment].visual_effect :highlight
page[:new_comment].reset
Typowe narzędzia
- <%= link_to_remote "Update time", { :update => "time", :url => { :action => :now, :arg1 => var1, :arg2 => var2 } } %> - link odpytujący akcję now o nową zawartość elementu o id=time (aktualny czas). Wystarczy napisać akcję zwracającą string
- <% form_remote_tag :url %> - podobnie jak link_to_remote z tym że przesyłana jest zawartość formularza
- <%= observe_field('field_id', :update => 'id elementu', :controller => 'kontroler', :action => 'akcja') %> - w razie zmiany pola wykonaj akcję
Sprawdzenie umiejętności
- Ćwiczenie 4: Dodaj możliwość oceniania postu z wyświetlaniem aktualnej liczby punktów, ocenianie nie powinno przeładowywać strony przy włączonym Java Script, rozszerzenie: powinno działać przy wyłączonym Java Script. Kolumna w tabeli posts niech nazywa się rank, do kontrolera powinny być przekazane 2 parametry: post_id z identyfikatorem posta oraz vote o wartości +1/-1. Ewentualny routing w wersji rozszerzonej niech nazywa się rank.
Na stronie http://api.rubyonrails.org/ są opisy poszczególnych metod itp.
Baza danych
Wskazówka
Rozwiązanie
Kontroler
Wskazówka
Wersja podstawowa Rozwiązanie
Wersja rozszerzona Rozwiązanie
Widok
Wskazówka
Wersja podstawowa: Rozwiązanie
Wersja rozszerzona: Rozwiązanie
Gotowy blog
Użyteczne linki
- Ajax + Rails
- http://guides.rubyonrails.org/ - przewodniki na każdy temat
- Rails API
- Tutorial
- Podstawa przykładu bloga: Podobny tutorial, Screencast
- Rails na wiki