Ruby on Rails Deel 2 - Gastenboek met AJAX
1. Inleiding
2. Het begin
3. Database
4. Scaffold
5. Design wijzigen
6. Sprinkle of AJAX
7. Afsluiting
1. Inleiding
Welkom bij mijn 2e tutorial over de fantastische taal Ruby en zijn geweldige framework Rails. Als je mijn eerste niet hebt gelezen,
raad ik je toch echt aan om die hier te lezen.
Wij gaan bij deze tutorial een leuk gastenboekje maken. Tussendoor zal ik de functies uitleggen die ik zal gebruiken. Ook gaan we een beetje AJAX gebruiken. Dit is alleen om te laten zien dat AJAX heel goed geïtegreerd is in Rails. Laten we beginnen !
top
2. Het begin
Een railsapplicatie hoor je te beginnen in de commandline. Je kunt er niet omheen. Waarom zou je, aangezien je via de command line de opzet van je applicatie kunt maken met 1 regeltje. Mooi toch? Laten we dat eens doen. Open je console en ga naar je map waar je je rails applicaties bewaart. Tik in: rails Gastenboek. Nu zul je zien dat er in je console een grote lijst word gemaakt. Dit is je applicatie. Rails staat voor het genereren van al die bestanden en Gastenboek staat voor de map waarin je applicatie zal komen. Ga nu (nog steeds in je console) naar deze map (Gastenboek). We willen zien of we op rails zitten. Dit doen we door de server te starten. Tik in ruby script/server.
Opmerking |
Het woord "ruby" in dit zin is belangrijk, want je kan ruby alleen gebruiken als deze ervoor staat. Als je een rubyprogramma
hebt, en je wilt dat testen in je console, dan zul je ruby ervoor moeten zetten.
|
Je hebt net ruby script/server ingetikt, dan zul je nu wat tekst moeten zien waarin staat dat de server is gestart op http://localhost:3000 en http://0.0.0.0:3000. Dit betekent dat de poort 3000 nu open staat voor onze rails applicatie. Surf naar http://localhost:3000 en je zou een bericht van Ruby on Rails moeten krijgen: "Welcome aboard, You’re riding the Rails!". Feliciteer jezelf maar. Na het feestje kun je doorgaan naar de volgende hoofdstuk. Let wel op dat je deze console niet meer kunt gebruiken. Je mag deze console voor deze tutorial niet sluiten.
top
3. Database
Een gastenboek werkt meestal met een database. Daarom zal ik hier ook een database gebruiken. Het is ook mogelijk om een filesysteem te gebruiken, maar ik raad iedereen aan een database te gebruiken. Je zou natuurlijk het mysql commando kunnen gebruiken, of zelfs phpmyadmin. Maar dit is Rails. Wij kunnen veel beter dan dat, toch?
Rails heeft een geweldige class voor het beheren van databases (qua aanmaak tabbelen). Nu heb ik wel geroepen dat je geen mysql commando of phpmyadmin hoeft te gebruiken. Ik heb niet gelogen, maar je zult ze wel moeten gebruiken voor de aanmaak van een database. Maak een database aan voor Rails en lees verder.
We moeten nog Rails onze database gegevens geven. Bij de meeste webbased talen doe je dit binnen in de applicatiecode. Rails heeft een aparte map voor zulke zaken. Ga naar je map Gastenboek, daarna naar de submap config. Open database.yml en wijzig de inhoud naar:
development:
adapter: mysql
database: je_dev_database
username: username
password: password
host: localhost
test:
adapter: mysql
database: je_test_database
username: username
password: password
host: localhost
production:
adapter: mysql
database: je_production_database
username: username
password: password
host: localhost
# de 3 databases kun je makkelijk aanmaken locaal, maar wanneer je je applicatie online zet, hoef je ze niet allemaal aan te maken.
# Want dan ga je van development modus naar production modus
# kortom, het aanmaken van de development database is nu meer dan genoeg
Open een nieuwe console en ga naar je rails applicatiemap (de root) en tik rake db:migrate in. Je hoort nu geen errors te krijgen. Als je toch een error krijgt, betekent dat dat Rails geen connectie met de database server kan maken. Bekijk de database file opnieuw om te zien of er een fout in zit.
Nu moeten we een model aanmaken.
Een model is een onderdeel van het MVC model dat Rails gebruikt. Een model regelt bijna alles wat te maken heeft met database.
Ook het valideren van datainput gebeurt via dit onderdeel.
Ga naar de console die je net hebt geopend en tik in: ruby script/generate model entry. Je zou nu een lijstje met bestanden en mappen moeten krijgen. Het woord "entry" staat voor de tabel die je gaat aanmaken, alleen de meervoud ervan. Rails is slim genoeg om te weten dat entry entries moet worden. Nu kunnen we de geweldige class van Rails gebruiken, genaamd ActiveRecord::Migration. Ga naar de root van je applicatie Gastenboek. Ga dan naar de submap db, daarna naar de submap migrate. Daarna moet je het bestand "001_create_entries.rb" openen. We moeten even een paar dingen vastleggen. We hebben de volgende velden nodig:
- id (deze hoef je niet in de functie hieronder te zetten, dit wordt automatisch gedaan door Rails)
- poster
- message
- datetime
Nu we dit weten, kunnen we de class bewerken. De class bestaat uit twee methoden, self.up en self.down. De self.up functie word aangeroepen bij het aanmaken van de tabel. Als je een volgende migratie uitvoert, word de functie self.down aangeroepen van de vorige versie. We hebben nu de self.up functie nodig. Kopieër de functie van hieronder.
def self.up
create_table :entries do |t|
t.column :poster, :string, :limit => 100
t.column :message, :text
t.column :datetime, :datetime
end
end
Met het keywoord def maak je een functie in Ruby. Na het def keywoord volgt de naam van de functie. Daarna gebruiken we de functie create_table. Binnen deze block kun je kolommen aangeven. De eerste hash-key (:hash-key) staat voor de naam van de veld in de database tabel. De volgende staat voor de type. En de optionele optie (:limit) kan ook worden gebruikt (er zijn wel meer hoor).
Nu je deze functie hebt aangemaakt, gaan we de tabel aanmaken. Ga opnieuw naar je console en tik in: rake db:migrate. Nu worden 2 tabellen aangemaakt. De eerste is de tabel die wij hebben aangemaakt. In de tweede worden de versies van de migrations opgeslagen. De migrate bestanden zien er zo uit: 001_enz. Onze volgende migrate bestand zou dan zijn 002_enz. En als je rake db:migrate zou gebruiken, dan kijkt Rails naar de tweede tabel en pakt dan de juiste migrate bestand. Deze methode word aangeraden omdat je dan zo je database geschiedenis kunt bijhouden.
Op naar de Rails magie.
top
4. Scaffold
Scaffold is een methode om een applicatie op te starten die heel erg standaard is. Van hieruit kun je je applicatie verder bouwen.
Ga naar je console en tik in: ruby script/generate scaffold Entry. Er is nu een standaard applicatie opgezet op basis van de velden in de database. Sluit deze server door Ctrl + C in te drukken. Start de server opnieuw met ruby script/server. Surf naar http://localhost:3000/entries
Je zou nu een interface moeten zien. Voeg een paar entries toe, en lees dan verder.
Zoals je ziet, is scaffold een krachtige functie. Scaffold maakt automatisch de add/delete/edit functies voor je aan, want elke applicatie heeft deze nodig. Maar wij willen deze lelijke interface niet. Ga dan naar de volgende hoofdstuk zodat we deze lelijke interface kunnen veranderen.
top
5. Design wijzigen
Zoals ik al eerder heb gezegd, gebruikt Rails het MVC model. Daardoor is het voor ons makkelijker om een template te wijzigen. Open je texteditor en open de volgende bestand: Gastenboek/app/views/entries/list.rhtml. Dit bestand hoort bij de functie list in de controller entries_controller.rb
Zoals je kunt zien in de list.rhtml bestand kun je inline Ruby gebruiken met behulp van de <% %> tags. Verander de hele pagina in dit:
<h1>Welkom op mijn kleine Ruby on Rails gastenboek</h1>
<h4>Laat eens een berichtje achter</h4>
<table border="0">
<% for entry in @entries %>
<tr>
<td>Gepost door:</td>
<td><%= entry.poster %></td>
</tr>
<tr>
<td>Gepost op:</td>
<td><%= entry.datetime.strftime("%d %m %Y om %H:%M") %></td>
</tr>
<tr>
<td><%= entry.message %> <br><hr /></td>
</tr>
<% end %>
</table>
<%= link_to 'Vorige pagina', { :page => @entry_pages.current.previous } if @entry_pages.current.previous %>
<%= link_to 'Volgende pagina', { :page => @entry_pages.current.next } if @entry_pages.current.next %>
<br />
<%= link_to 'New entry', :action => 'new' %>
Ik zal alleen de code tussen <% en %> uitleggen.
Eerste tag:
Als je even app/controllers/entries_controller.rb opent, en even de functie list opent, zul je zien dat het maar 1 regel bevat.
De ingebouwde functie paginate maakt de resultaten en returned ze. In Rails kun je meerdere waarden returnen. En die kun je dan ook tegelijk assignen. Een voorbeeld:
#dit zet je ergens in je controller VOOR de laatste end
def voorbeeld_returnen
return 'Dit ',
'is ',
'een ',
'test!'
end
#dit zet je IN de functie list
@dit, @is, @een, @test = voorbeeld_returnen
#dit moet je in je template list.rhtml helemaal onderaan zetten
<%= @dit + @is + @een + @test >
Je zou nu mooi "Dit is een test!" moeten zien onderaan je list pagina.
Een for loop maak je door eerst het woordje for te tikken. Daarna de variabele waarin je de waarden wilt zetten. Daarna moet je nog vertellen welke array hij moet doorlopen. Daarvoor gebruik je het woordje in. Meteen erachter is de array waarin de resultaten zijn. De for loop is te vergelijken met de foreach van PHP.
Een @var_naam word een instance naam genoemd. De instance naam die je in je controller functie defineert, word ook beschikbaar in het viewbestand van die functie. Dat kon je al zien in het voorbeeld hierboven. Mocht je een normale var gebruiken in je controller, dan zou je die variabele niet kunnen in je view template. Dat komt omdat dat een local variabele is.
Tweede tag:
In Rails kun je inline Ruby gebruiken. Er zijn twee soorten tags, de <% en %> tags is bedoeld om gewoon ruby te gebruiken. Je kunt het vergelijken met <?php en ?>. De tweede tag is <%= en %>, Dit is hetzelfde als <?= en ?> tags van php. Een kortere versie van echo (Ruby heeft alleen geen echo, maar print of puts. Trouwens, print/puts werken niet in de controllers en views).
Derde/vierde tag:
Het weergeven van de datum. Hier kun je zien waarom Ruby word gezien als 100% OO. De datum zit in de variabele entry. Dat is dus entry.datetime Er is een functie genaamd strftime waarmee je datums kunt manipuleren. Je zet het gewoon achter de rest. Het wordt nu entry.datetime.strftime("format"). We hebben nu een nogal saaie manier van maand weergeven. Laten we een helper gaan maken die de maandnummer omzet in de Nederlandse maand.
Een helper is een class waarin je functies kunt defineren die je kunnen helpen in de view template. Omdat view eigelijk bedoelt is voor design, is deze gedeelte van Rails ontwikkelt. Open het bestand app/helpers/entries_helper.rb. Defineer de volgende functie:
def maand_to_nl( maand )
maanden = [ 'Januari', 'Februari' , 'Maart' , 'April' , 'Mei', 'Juni', 'Juli',
'Augustus', 'September', 'Oktober', 'November', 'December'
]
maanden[ maand ]
end
Allereerst defineren we de functie genaamd maand_to_nl. Daarna maken we een array. Een array in ruby kan alleen integers als keys hebben. Wil je iets anders als keys, dan gebruik je Hashes (hierover waarschijnlijk in de volgende tutorial). In Ruby word de laatste regel code in een functie automatisch returned, dus hoef je het woordje return niet erbij te zetten.
Nu kunnen we deze functie gebruiken in ons viewbestandje. Open die weer eens. Ga eens naar de tr waar we de datum weergeven. Wijzig die naar dit:
<tr>
<td>Gepost op:</td>
<% maand = maand_to_nl( entry.datetime.month ) %>
<td><%= entry.datetime.strftime("%d #{maand} %Y om %H:%M") %></td>
</tr>
We gebruiken de functie month om de huidige maand mee te geven aan onze helperfunctie. Om een variabele binnen dubbele quotes te gebruiken moet je #{variabele_naam} gebruiken. Test het maar uit. De vijfde tag is gewoon het weergeven van het bericht dat je hebt gepost.
Laten we doorgaan naar de vijfde tag.
Vijfde tag:
Het stoppen van de for loop doe je met het woordje end.
Zesde/Zevende tag:
In Rails zijn er veel functie die je kunt gebruiken om standaard html tags te genereren. De 5e en 6e tag zijn bedoelt om de vorige/volgende links te maken. De eerste parameter is de naam van de link. Deze wordt alleen laten zien als de variabele @entry_pages.current.previous bestaat. Hetzelfde geldt voor de 7e tag.
Achtste tag:
Je kunt ook een link aanmaken naar een action in je applicatie door de hashkey :action. De waarde is dan de actie waarnaar je word verstuurd.
Nu ik jullie de view heb uitgelegd, gaan we eens wat AJAX gebruiken om het wat leuker te maken.
top
6. Sprinkle of AJAX
AJAX is een nogal populaire methode geworden in de webwereld. In Rails is het wel erg gemakkelijk gemaakt. De JS is ingebouwd in de Framework waardoor je geen letter JS hoeft te tikken. Dit kan leuk zijn, maar minder leuk als je custom javascript wilt hebben. Nu is dat niet onmogelijk, maar het valt buiten deze tutorial.
We willen graag een formuliertje onderaan onze pagina hebben waarin je makkelijk je bericht kunt schrijven. Laten we met dat eens beginnen.
Ik moet iets uitleggen voordat we doorgaan. Rails kan ook gebruik maken van partials. Dat zijn gedeeltelijke templates die je bij elke pagina kunt invoegen. Open app/views/entries/_form.rhtml. Daar zie je bovenaan een ruby inline tag. Die is bij deze tutorial niet belangrijk. Een datum is hier als veld aangegeven, terwijl een datum altijd automatisch word toegevoegd. Wat wij dus moeten doen is dat stukje vervangen door een hidden field met de huidige tijd als waarde. Verander dus de formulier partial in dit:
<%= error_messages_for 'entry' %>
<!--[form:entry]-->
<% form_remote_for :entry, :url => { :action => 'create' }, :html => { :id => 'entryform' } do |f| %>
<p><label for="entryposter">Poster</label>
<%= f.text_field :poster %></p>
<p><label for="entrymessage">Message</label>
<%= f.text_area :message, :rows => 9 %></p>
<%= hidden_field 'entry', 'datetime',
:value => Time.now %>
<%= submit_tag "Verstuur bericht" %>
<% end %>
<!--[eoform:entry]-->
We hebben nu de form begin en eind tag binnen de _form partial gezet (een partial kun je vergelijken met include bij PHP). We hebben nu ook een andere functie voor het aanmaken van de form, namelijk form_remote_for. Dit is een functie waarmee je AJAXbased formulieren kunt aanmaken. :entry zorgt ervoor dat Rails weet dat de array van de form entry moet worden genoemd ( entry[veldnaam] ). :url is om aan Rails te vertellen welke actie van de controller moet worden aangeroepen. Laten we die controller actie eens bekijken, want daar moet ook wat veranderd worden:
def create
@entry = Entry.new( params[:entry])
@entry.save
flash[:notice] = 'Entry was successfully created.'
respond_to do |format|
format.html { redirect_to :action => :list }
format.js
end
end
Wat er eerst word gedaan is het aanmaken van een nieuwe Model object. Daarna worden de resultaten opgeslagen via de functie save. De flash[:notice] is bedoelt om berichten weer te geven als de actie succesvol is gegaan. Nu komt de belangrijke stuff:
Aangezien we onze gastenboek ook willen laten werken voor mensen zonder JS, kunnen we controleren of de inkomende actie HTML is of JS. We maken een block met respond_to. Nu kun je met format.html een block aanmaken en daarin defineren wat er moet gebeuren bij een HTML request. Wij gaan dan gewoon de pagina redirecten.
Bij JS hoef je geen block aan te maken, want Rails zal de juiste template (RJS) aanroepen voor de actie.
We moeten nu deze form onder aan de berichten zetten. Ik had net over partials. Die herken je aan de _ voor zijn naam. We kunnen gewoon de partial _form.rhtml gebruiken. Onze list.rhtml ziet er nu zo uit:
<h1>Welkom op mijn kleine Ruby on Rails gastenboek</h1>
<h4>Laat eens een berichtje achter</h4>
<div id="entries">
<%= render :partial => 'entry', :collection => @entries %>
</div>
<%= link_to 'Vorige pagina', { :page => @entry_pages.current.previous } if @entry_pages.current.previous %>
<%= link_to 'Volgende pagina', { :page => @entry_pages.current.next } if @entry_pages.current.next %>
<%= render :partial => 'form' %>
Met :collection kun je de resultaten meegeven aan de partial zodat in de partial geen for loop gebruikt hoeft te worden. Wij hebben de entries ook in een partial gezet (genaamd _entry.rhtml), zonder for loop:
<table border="0">
<tr>
<td>Gepost door:</td>
<td><%= entry.poster %></td>
</tr>
<tr>
<td>Gepost door:</td>
<% maand = maand_to_nl( entry.datetime.strftime("%m").to_i - 1 ) %>
<td><%= entry.datetime.strftime("%d #{maand} %Y om %H:%M") %></td>
</tr>
<tr>
<td><%= entry.message %> <br><hr /></td>
</tr>
</table>
We zien nu bij http://localhost:3000/entries/list een formulier onderaan de berichten. Maar zoals je merkt, werkt hij niet. Dat komt omdat wij nog de JS bestanden moeten includen. En het beste is, Rails include alle JS voor je met 1 regel rubycode :)
Open het bestand app/views/layouts/entries.rhtml, dit is de standaard layout voor elke pagina in je applicatie (de entries controller alleen natuurlijk). Je ziet ergens in dit bestand <%= stylesheet_link_tag 'scaffold' %>. Zet onderaan deze code het volgende:
<%= javascript_include_tag :defaults %>
Nu zal Rails alle JSbestanden die hij nodig heeft includen. Maar we kunnen nog steeds niet iets toevoegen :) We zijn de RJS template glad vergeten. Een RJS template is een template bestand waarin je via Ruby javascript kunt aanroepen. Verwijder de volgende bestand: app/views/entries/new.rhtml en maak een nieuw bestand in diezelfde map aan genaamd: create.rjs (je kunt nu ook de functie new verwijderen in de entries_controller.rb bestand)
Nu gaan we de AJAX schrijven. Je kunt de webpagina manipuleren met de object genaamd page in een RJS template. Wat wij willen is dat bij het submitten het volgende gebeurd:
- Onze bericht word onderaan toegevoegd zonder dat pagina herlaad
- Er moet bovenaan een berichtje komen dat de opdracht succesvol is gegaan
- De formulier moet gereset worden
Dat zijn de zaken die we allemaal in onze RJS template gaan toevoegen. Open je create.rjs template en plaats het volgende erin:
page.insert_html :bottom, :entries, :partial => 'entry', :object => @entry
Dit stukje ruby zorgt ervoor dat onze bericht onderaan de andere berichten word toegevoegd (:bottom gedeelte). De volgende parameter is de idnaam van onze div waar de berichten inzitten. De derde is de partial waar de entries in zitten. En als laatste geef je nog de object mee die je in je controller hebt geset (zie actie create).
Nu moeten we een berichtje boven weergeven:
page.replace_html :notice, flash[:notice]
De replace_html functie is bedoeld om de html element met idnaam "notice". Ga naar app/views/layouts/entries.rhtml en zoek naar de p element in deze bestand waar je ook notice ziet staan. Voeg aan de html element een id toe genaamd "notice".
Nu nog de formulier resetten:
page[:entryform].reset
En als laatst nog een klein bugfixje, want de berichtje bovenaan blijft als je de pagina refreshed. Elke flash notice blijft 1 request meeleven, zodat je die dus op een andere pagina kan weergeven. Dit verhelpen we met deze ruby code:
flash.discard
Onze gastenboek is nu eindelijk klaar :)
top
7. Afsluiting
We hebben nu binnen een uurtje tijd een gastenboek gemaakt (met pagina navigatie). We hebben partials gebruikt en we hebben AJAX gebruikt. In mijn volgende artikel gaan wij een klein forumpje maken. Alleen zullen wij het deze keer niet van scaffold opbouwen, maar gewoon van scratch. Zo leer je meer over Models en Controllers in ruby. Tot de volgende keer :)
top
|