Ruby on Rails: Erste Schritte

Um Rails kennenzulernen schauen wir uns an, wie schnell und einfach man mit Rails Webapplikationen bauen kann. Manche werden dieses Kapitel einfach nur durchlesen um Rails kennenzulernen und danach vielleicht Rails zu installieren und richtig loszulegen. Andere werden schon eine funktionsfähige Railsinstallation haben und vielleicht die Schritte gleich am eigenen Rechner nachvollziehen wollen. Im Moment ist beides ok. Wir werden später alles was wir von diesem Projekt brauchen so aufgreifen, dass diejenigen, die jetzt "nur" mitlesen, ihren Code dann entsprechend ergänzen können. Erst bei unserem nächsten Projekt wird dann jedes Kapitel auf den Code des vorangegangenen Kapitels aufbauen.

Schritt 1: Vorarbeiten

Bearbeiten

Als Demo wollen wir ein Online-Glossar bauen. Glossareinträge enthalten ein Schlüsselwort (keyword) und eine Worterklärung (explanation). Die Glossareinträge wollen wir gekürzt und alphabetisch sortiert anzeigen. Außerdem soll man jeden Eintrag einzeln in voller Länge anzeigen können. Zur Administration reicht uns Authentifizierung und einfache CRUD-Funktionalität. D.h. man kann sich als Administrator einloggen und dann neue Einträge anlegen (c - create) und bestehende lesen (r - read), verändern (u - update) und löschen (d - delete).

Alles klar? Dann können wir loslegen.

Installation verifizieren

Bearbeiten

Wer das Kapitel am Rechner nachvollziehen will, sollte Rails bereits installiert haben. Wir arbeiten mit:

  • Ruby 1.8.6
  • RubyGems 1.3
  • Rails 2.3

Es ist aber auch ok dieses erste Kapitel nur durchzulesen, und sich einen ersten Eindruck zu verschaffen. Wir fangen im nächsten Kapitel noch mal von vorne an. Danach baut ein Kapitel auf das andere auf. Das bedeutet, dass dann immer die Codebasis vom Vorgängerkapitel vorausgesetzt wird.

Um die Ruby Installation zu verifizieren geben wir in der Konsole ein:

ruby -v

Ruby solllte sich melden und "ruby 1.8.6 .." zurückgeben.

RubyGems prüfen wir mit

gem -v

Das sollte dann 1.3.x ergeben, also beispielsweise 1.3.1.

Auch die Rails Installation können wir so prüfen:

rails -v

Und sie sollte 2.3 ergeben.

Ggf. können wir Ruby on Rails upgraden oder nachinstallieren .

So sah dieses Kapitel früher aus

Bearbeiten

Von diesem Kapitel gibt es eine Version aus der Zeit als Rails 1.2 aktuell war:

Wer nicht explizit an der Historie interessiert ist, kann direkt weiterlesen.

Schritt 2: Applikation anlegen

Bearbeiten

Und so gehts unter Rails typischerweise los. Wir gehen in die Konsole und legen mit rails Applikationsname eine neue Applikation an. In diesem Fall nennen wir sie "glossary".

rails glossary
bzw. rails new glossary

Rails legt dann im aktuellen Verzeichnis das Verzeichnis "glossary" an und darin ein Grundgerüst aus Verzeichnissen und Dateien. Das ist typisch für opinionated Software. Rails hat eine Meinung wie eine Webapplikation aufgebaut sein sollte. Wir können uns danach richten und ersparen uns dann viel Arbeit, aber wir müssen nicht. Wir können Rails jederzeit überstimmen und fast alles anders machen.

  BILD: Rails Kommando in der Konsole - unter Aptana RadRails

Sehen wir uns die Verzeichnisstruktur mal genauer an. Dieses Grundgerüst ist für alle Railsanwendungen gleich. Das hat den Vorteil, dass sich andere Railsentwickler sofort in unserer Applikation zurechtfinden - weil alles so geordnet ist, wie sie es von anderen Rails-Projekten gewohnt sind. Im "config"-Verzeichnis stehen Konfigurationsinformationen. Beispielsweise welche Datenbank wir verwenden und wie sie angesprochen wird. Fürs erste sind die Voreinstellungen für uns ok. Im "app"-Verzeichnis sehen wir die Unterverzeichnisse "controllers", "helpers", "models" und "views". Rails schlägt uns so eine Model-View-Controller Architektur vor und eine passende Ablagestruktur für unsere Model-, View- Controller-Klassen. Dazu kommt ein Verzeichnis für Helper-Klassen, die grundlegende Funktionen bereitstellen und eventuell von mehreren Applikationsbereichen angesprochen werden.

Und auch wenn wir noch nichts programmiert haben, haben wir schon einen Webserver. Und den starten wir jetzt mal.

cd glossary
ruby script/server
oder 
rails server

Der Server wird mit script/server gestartet und mit Strg-C beendet.

Wenn wir jetzt ein Browserfenser aufmachen und http://localhost:3000/ ansehen ..   BILD: Erster Startbildschirm im Browser

.. dann sehen wir etwas erstaunliches. Es gibt tatsächich schon eine Webapplikation die einen Startbildschirm im Browser anzeigen kann. Und der zeigt eine kurze Anleitung zum weiteren Vorgehen und Links z.B. auf die Rails-Dokumentation.

Schritt 3: Applikationsgerüst erstellen

Bearbeiten

So wie wir mit einem Befehl eine Applikation angelegt haben, können wir in Rails mit Generatoren auch Modelle, Controller oder ein ganzes Gerüst erzeugen, das aus Model, View und Controller besteht. In diesem Fall erzeugen wir ein Gerüst für Glossareinträge. Den Generator rufen wir mit script/generate scaffold glosentry auf. Wir können sogar gleich ein korrektes Modell mit den richtigen Attributen anlegen indem wir Sie einfach ergänzen. Unser Glossareintrag braucht auf jeden Fall ein Kennwort (keyword) und eine Erklärung (explanation). Das keyword ist immer kurz genug, so dass string als Datentyp ausreicht. Die Erklärung kann auch mal richtig lang werden. Deshalb nehmen wir hier den Datentyp text.

$ script/generate scaffold glosentry keyword:string explanation:text

Rails erzeugt Model, View Controller, Datenbank-Migration und ein Gerüst für Softwaretests.

Macintosh:glossary ovhaag$ script/generate scaffold glosentry keyword:string, explanation:text
     exists  app/models/
     ..
     create    db/migrate/20090418122003_create_glosentries.rb
Macintosh:glossary ovhaag$ 

Schauen wir uns die Migration an:

#20090418122003_create_glosentries.rb

class CreateGlosentries < ActiveRecord::Migration
  def self.up
    create_table :glosentries do |t|
      t.string :keyword
      t.text :explanation

      t.timestamps
    end
  end

  def self.down
    drop_table :glosentries
  end
end

Rails schlägt uns zusätzlich zu keyword und explanation noch timestamp-Spalten vor. Das ist manchmal nützlich und deshalb lassen wir das so. Aber die Keyword Spalte ergänzen wir noch. Für jeden Glossareintrag muss immer ein Stichwort (Keyword oder Keyphrase) angegeben werden (:null => false). Und es muss kurz sein. Sagen wir maximal 64 Buchstaben (:limit => 64). Damit sieht die Migration so aus:

#20090418122003_create_glosentries.rb
 
  def self.up
      # ..
      t.string :keyword, :limit => 64, :null => false
      t.text :explanation
      # ..
  end

Schritt 4: Datenbank aufbauen

Bearbeiten

Jetzt können wir das Modell in der Datenbank anlegen. Dazu schauen wir uns zunächst an, wie die Datenbank konfiguriert ist. Das finden wir in der database.yml Datei im config-Verzeichnis.

# /config/database.yml

# SQLite version 3.x
#   gem install sqlite3-ruby (not necessary on OS X Leopard)
development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  adapter: sqlite3
  database: db/test.sqlite3
  pool: 5
  timeout: 5000

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

Es gibt drei Datenbanken. Eine für die Entwicklung, eine für Tests und eine für den Produktivmodus. Sqlite ist vorkonfiguriert und für uns im Moment ok. Wir können Die Datei also unverändert lassen und die Migration in der Konsole ausführen. "rake db:migrate" führt alle anstehenden Migrationen aus. In diesem Fall 20090418122003_create_glosentries.rb.

Macintosh:glossary ovhaag$ rake db:migrate
(in /Users/ovhaag/Documents/Aptana Studio/glossary)
==  CreateGlosentries: migrating ==============================================
-- create_table(:glosentries)
   -> 0.0028s
==  CreateGlosentries: migrated (0.0032s) =====================================

Macintosh:glossary ovhaag$

Es gibt jetzt eine Datenbank-Datei "development.sqlite" im "db"-Verzeichnis. Wenn wir sie mit einem Tool wie der Firefox-Extension "SQLiteManager" ansehen finden wir die Tabelle "glosentries" mit den Feldern "id", "keyword", "explanation", "created_at" und "updated_at". Die ID-Spalte enthält eine Integerzahl als primary key. Rails legt die ID-Spalte immer an, wenn wir das nicht explizit unterbinden. "created_at" und "updated_at" sind die Timestamp spalten und entspechen der "t.timestamps" Anweisung in unserer Migration. Und schließlich sind da noch die Spalten "keyword" und "explanation", so wie wir sie explizit in der Migration beschrieben haben.

Das folgende Bild zeigt die Tabelle nachdem ein paar Daten eingefügt wurden.

TODO Bild

Eine andere Tabelle heisst "schema_migrations". Dort werden die Migrationen verwaltet. Aber das schauen wir uns später an. Jetzt wollen wir die Applikation ausprobieren.

Schritt 5: Applikation ansehen

Bearbeiten

Da wir die Datenbank geändert haben, starten wir den Server neu.

Ctrl-C    # Server herunterzufahren
$ script/server    # Server wieder hochfahren

Wenn wir jetzt im Browser die Adresse "http://localhost:3000/glosentries" aufrufen, sehen wir eine Übersicht über Einträge in unserem Glossar. Noch ist sie leer aber mit "new" können wir Eintrage anlegen. Und wenn Einträge da sind, können wir sie einzeln ansehen (show), ändern (edit) oder löschen (delete). Unser Glossar mag noch etwas einfach aussehen und ein paar Macken haben aber wir können es benutzen. Und die wichtigsten Macken können wir gleich beseitigen:

  • Für das Stichwort zu einem Glossareintrag kann man einen Leerstring eintragen. Das macht keinen Sinn. Das Stichwort soll obligatorisch werden.
  • In der Aufzählung werden die Erklärungen vollständig angezeigt. Das wird schnell unübersichtlich. Bei langen Erklärungen wollen wir in Zukunft nur den Anfang anzeigen und die Fortsetzung durch 3 Punkte andeuten. Nur in der Einzeldarstellung soll der volle Text angezeigt werden.
  • Eine alphabetische Sortierung wäre schön.
  • Und ein Layout, bei dem die Zeilen abwechselnd hell und dunkel hinterlegt sind.

Bevor wir den ersten Punkt mit "Validations" angehen, schauen wir noch in die Datei "config/routes.rb". Hier wird konfiguriert, auf welche Adressen die Applikation reagiert.

# config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.resources :glosentries
  ..
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end

Ganz oben hat Rails bei der Erstellung des Gerüsts "map.resources :glosentries" eingetragen. Deshalb funktioniert die Adresse "http://localhost:3000/glosentries", die wir gerade benutzt haben. Wenn wir zusätzlich eine "map.root .."-Zeile einfügen

# config/routes.rb
ActionController::Routing::Routes.draw do |map|
  ..
  # You can have the root of your site routed with map.root -- just remember to delete public/index.html.
  # map.root :controller => "welcome"
  map.root :controller => "glosentries"
  ..
end

und public/index.html löschen machen wir die Glossarübersicht zur neuen Startseite. Wer sich nicht traut, kann public/index.html auch umbenennen, beispielsweise zu public/info.html. Dann kann man die Infoseite weitehin ansehen, in diesem Fall mit "http://localhost:3000/info.html".

Schritt 6: Applikation ergänzen

Bearbeiten

Validations

Bearbeiten

Das Stichwort soll obligatorisch werden. Und die Maximallänge ist 64. Das erreichen wir mit zwei validates-Anweisungen.

# app/models/glosentry.rb
class Glosentry < ActiveRecord::Base
  validates_presence_of :keyword
  validates_length_of :keyword, :maximum => 64
  ...
end

Wenn eine Validierung fehlschlägt wird eine Fehlermeldung angezeigt. Dazu müssen wir nichts unternehmen, denn unser scaffold hat die Anzeige im View schon angelegt.

Erklärung kürzen

Bearbeiten

In der Übersicht wollen wir die Erklärungen kürzen. Das wollen wir nicht direkt im view machen, denn wenn wir diese verkürzte Darstellung noch anderswo benötigen, schreiben wir den selben Code nochmal. Das vermeiden wir, indem wir unser Glosentry Modell um ein virtuelles Attribut short_explanation erweitern.

Das entwickeln wir testgetrieben und schauen uns bei der Gelegenheit an wie man Railsapplikationen testet. Scafold hat uns ja zum Modell bereits einen Unit-Test angelegt ( test/unit/glosentry_test.rb). Den ändern wir jetzt ab und testen unser virtuelles Attribut short_explanation.

require File.dirname(__FILE__) + '/../test_helper'
# require 'test_helper'

class GlosentryTest < ActiveSupport::TestCase

  test "short_explanation should shorten explanation" do
    g = Glosentry.create({:keyword => "sample", 
      :explanation => "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."})
    assert_equal "Lorem...", g.short_explanation(8) 
    assert_equal "Lorem ipsum d...", g.short_explanation(16)
    assert_equal "Lorem ipsum dolor...", g.short_explanation(20)
  end
  
  test "short_explanation should not change already short explanation" do
    g = Glosentry.create({:keyword => "sample", :explanation => "Lorem ipsum"})
    assert_equal "Lorem ipsum", g.short_explanation(11)
    assert_equal "Lorem ipsum", g.short_explanation(26)
  end
  
  test "short_explanation default length should be 20" do
    g = Glosentry.create({:keyword => "sample", :explanation => "Lorem ipsum dolor sit amet"})
    assert_equal "Lorem ipsum dolor...", g.short_explanation
  end

end

Um den Test auszuführen öffnen wir eine Konsole und wechseln in das Verzeichnis in dem unsere Applikation liegt.

Zunächst stellen wir sicher, dass wir eine Test-Datenbank haben und dass sie auf dem neuesten Stand ist.

$ rake db:test:load

lädt die Testdatenbank neu aus db/schema.rb.

Danach rufen wir den Test auf.

rake test:units TEST=test/unit/glosentry_test.rb

Zunächst scheitert der Test - natürlich.

Loaded suite test/unit/glosentry_test
Started
EEE
Finished in 0.184291 seconds.

  1) Error:
test_short_explanation_default_length_shold_be_20(GlosentryTest):
NoMethodError: undefined method `short_explanation' for #<Glosentry:0x24838dc>
    test/unit/glosentry_test.rb:22:in `test_short_explanation_default_length_shold_be_20'

...

3 tests, 0 assertions, 0 failures, 3 errors

Wir haben das Modell ja noch nicht erweitert. Das wollen wir jetzt tun. Zunächst legen wir das virtuelle Attribut short_explanation in der Datei app/models/glosentry.rb an.

# app/models/glosentry.rb
..
  def short_explanation(len=20)
  end

Das verändert die Fehleranzeige schon mal.

..
<"Lorem ipsum dolor..."> expected but was <nil>.
..

Jetzt implementieren wir die einfachste Methode, die alle Tests erfüllt. Dazu benutzen wir den TextHelper (ActionView::Helpers::TextHelper) von Rails, den wir auch im view verwendet hätten.

class Glosentry < ActiveRecord::Base
  ...
  include ActionView::Helpers::TextHelper

  def short_explanation(len=20) 
    truncate(self.explanation, :length => len)
  end
end

Jetzt läuft unser Test durch.

...
3 tests, 6 assertions, 0 failures, 0 errors

Aber der Code ist noch nicht schön, denn unser Modell sollte eigentlich kein Helper sein, sondern einen Helper benutzen. Deshalb implementieren wir etwas umständlicher:

class Glosentry < ActiveRecord::Base
  ...
  class GlosentryHelper
    include ActionView::Helpers::TextHelper
  end
  
  def helper
    @h ||= GlosentryHelper.new
  end
    
  def short_explanation(len=20) 
    helper.truncate(self.explanation, :length => len)
  end
end

Und sehen gleich einen Grund warum Tests hilfreich sind. Wenn wir den Test nach dem Umschreiben noch mal laufen lassen, sehen wir am Ergebnis

...
3 tests, 6 assertions, 0 failures, 0 errors

ob bzw. dass wir keinen Fehler eingebaut haben.

Jetzt müssen wir nur noch den View in der Datei app/views/glosentries anpassen.

    <td><%=h glosentry.short_explanation %></td>

Und wir sind fertig.

Einträge sortieren

Bearbeiten

Die Sortierung der Glossareinträge lässt sich einfach realisieren. Es reicht, wenn wir die Abfrage der Glossareinträge um einen Order-Parameter ergänzen.

  def index
    @glosentries = Glosentry.all(:order => :keyword)
    ..
   end

Zeilen hinterlegen

Bearbeiten

Wir ergänzen die Tabellenzeilen () um eine Hintergrundfarbe und wechseln diese Hintergrundfarbe mit der cycle Anweisung aus den ActionView::Helpers::TextHelper.

# views/glosentries/index.html.erb

..
<% @glosentries.each do |glosentry| %>
  <tr style="background-color: <%= cycle 'silver', 'white' %>;">
    <td><%=h glosentry.keyword %></td>
    <td><%=h glosentry.short_explanation(50) %></td>
    <td><%= link_to 'Show', glosentry %></td>
    <td><%= link_to 'Edit', edit_glosentry_path(glosentry) %></td>
    <td><%= link_to 'Destroy', glosentry, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
..

So langsam wirds übersichtlich. Jetzt fehlt nur noch ein geschützter Adminbereich

Admin Bereich schützen

Bearbeiten

Für die Userverwaltung gibt es mächtige Plugins. Aber uns reicht eine kleine Lösung, die ohne login nur die show-Seiten anzeigt und beim login ein festes Passwort abfragt.

Zunächst nehmen wir uns die Übersichtsseite vor. Den Show-Link können wir auf das Stichwort (keyword) legen, wir haben ja sichergestellt, dass immer ein Stichwort angegeben ist. Edit und delete legen wir in ein Tabellenfeld, und zeigen sie nur an, wenn die wir im Admin-Modus sind. Dazu benutzen wir eine Helper-Methode: admin?(). Dabei können wir die Aktionen gleich deutsch benennen, also bearbeiten statt edit usw.

Die Übersichtsseite (view, Quelltext) sieht nun so aus:

# index.html.erb

<table>
  <tr style="background-color: teal;">
    <th>Stichwort</th>
    <th>Erklärung</th>
	<% if admin? %>
    <th>Actions</th>
	<% end %>
  </tr>

<% @glosentries.each do |glosentry| %>
  <tr style="background-color: <%= cycle 'silver', 'white' %>;">
    <td><%= link_to glosentry.keyword, glosentry %></td>
    <td><%=h glosentry.short_explanation(50) %></td>
	<% if admin? %>
    <td><%= link_to 'bearbeiten', edit_glosentry_path(glosentry) %>
	    <%= link_to 'löschen', glosentry, :confirm => 'Eintrag wirklich löschen?', :method => :delete %></td>
	<% end %>
  </tr>
<% end %>

  <tr style="background-color: teal;">
    <th></th>
    <th></th>	
	<% if admin? %>
	<th></th>
	<% end %>
  </tr>
</table>

<br />
		
<% if admin? %>
<p><%= link_to 'Neuer Eintrag', new_glosentry_path %></p>
<% end %>

Damit das funktioniert brauchen wir die helpermethode admin?(). Wir wollen die Methode später nicht nur im View verwenden, sondern auch in den Controllern. Deshalb legen wir sie in den Applicationcontroller und deklarieren sie als helper-methode. Für das erste tut es ein Fake der immer true oder immer false zurückgibt. Der eigentliche Code kommt dann gleich.

# application_controller.rb

class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time

  helper_method :admin?    
  protected  
  def admin?  
    false  
  end
end

Jetzt haben wir ein Zwischenergebnis zum anschauen. Wenn wir in der admin?-Methode false zurückgeben, werden die Admin-Links ausgeblendet, wenn wir true zurückgeben werden sie angezeigt. Gut, oder fast gut. Denn abgesehen von der fehlenden Implementierung von admin() haben wir noch eine Sicherheitslücke: Jeder der die URLs für die Administration der Glossareinträge kennt, kann Sie direkt in die Adresszeile des Browsers eintippen und unser Glossar verändern, auch wenn er dazu eigentlich nicht berechtigt ist. Beispielsweise "../glosentries/new" für einen neuen Eintrag. Da wir nicht wollen, dass unser Glossar in fünf Monaten zu 90% aus Spam besteht, blockieren wir Fremdzugriffe, indem wir die Admin-Methoden filtern.

Mit "before_filter :authorize, :except => [:index, :show ]" lassen wir im GlosentriesController nur Zugriffe die Anzeigefunktionen (index und show) ohne explizite Erlaubnis zu. Alle anderen Methoden (new, create, edit, update und destroy) sind so durch den vorgeschalteten Aufruf von "authorize()" geschützt.

# glosentries_controller.rb

class GlosentriesController < ApplicationController  
  before_filter :authorize, :except => [:index, :show ] 
  ..
end

Gleichzeitig ergänzen wir diese authorize-Methode im ApplicationController. Sie ist auf der basis von admin?() einfach zu implementieren. Zunächst zeigen wir die fehlende Berechtigung als Notiz an, denn eine Notizanzeige haben wir bereits auf der Startseite. Wir machen uns aber einen Vermerk, dass wir das später als Fehler anzeigen wollen.

# application_controller.rb

class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time
  
  helper_method :admin?    
  protected  
  def admin?  
    false  
  end
    
  def authorize  
    unless admin?  
      flash[:notice] = "Zugriff auf die Administration erfordert Berechtigung!" 
      # TODO :error wäre besser als :notice, aber die Error-Anzeige auf der home seite fehlt noch
      # flash[:error] = "Zugriff auf die Administration erfordert Berechtigung!"   
      redirect_to root_path  
      false  
    end  
  end
end

Zum Testen gehen wir auf die Detailanzeige von einem Glossareitrag. Dort haben wir die Administrationslinks noch nicht ausgebaut und so können wir den Filter testen ohne die Url von Hand einzutippen. Wie erwartet landen wir auf der Startseite und unsere Notitz wird angezeigt.

Natürlich korrigieren wir auch die show-Seite und zeigen auch hier die Admin-Links nur an, wenn admin? true ergibt.

# show.html.erb

...
<p>
<% if admin? %>
<%= link_to 'Edit', edit_glosentry_path(@glosentry) %> |
<% end %>
<%= link_to 'Back', glosentries_path %>
</p>

Dann können wir endlich die admin?-Methode angehen. Wie oben erwähnt brauchen wir keine ausgefeilte Rechte- und Benutzerverwaltung. Uns reicht es ein festes Passwort abzuprüfen. Sagen wir "geheim". Damit wir beim nächsten Seitenaufruf noch wissen, dass der Benutzer Administrationsrechte hat, speichern wir das Passwort in der Session.

# application_controller.rb
   def admin?  
     session[:password] == "geheim"  
   end

An- und Abmelden wickeln wir über einen SessionsController ab. Den generieren von der Konsole aus. Dabei legen wir auch gleich die Methoden an, die der controller haben soll. Wir wollen das Design restful halten und benutzen new und create fürs Anmelden und destroy fürs Abmelden.

# console
# Syntax: script/generate controller controller_name method_1 method_2 ..

$ script/generate controller sessions new create destroy

Beim Einloggen speichern wir das Passwort in der Session, beim Ausloggen bereinigen wir die Session mit reset_session(). Die Anmeldung gilt nur, wenn danach Adminrechte bestehen, sprich wenn das Passwort stimmt.

# sessions_controller.rb

class SessionsController < ApplicationController  
  def new  
  end  
  
  def create  
    session[:password] = params[:password]  
    flash[:notice] = "Erfolgreich als Administrator angemeldet." if admin?
    redirect_to root_path  
  end  
  
  def destroy  
    reset_session  
    flash[:notice] = "Erfolgreich abgemeldet."
    redirect_to root_path  
  end  
end

Fürs Einloggen legen wir ein Formular an.

# app/views/sessions/new.html.erb

<h1>Online-Glossar - Administrationsbereich</h1>
<% form_tag sessions_path do %>  
	Passwort: <%= password_field_tag :password %>  
	<%= submit_tag "Anmelden" %>
<% end %>

Und zuletzt richten wir Pfade ein.

  • map.resources :sessions für das login-Formular in app/views/sessions/new.html.erb
  • map.login '/login', .. und map.logout '/logout', .. fürs An- und Abmelden.
# routes.rb
..
  map.resources :sessions
  map.login '/login', :controller => 'sessions', :action => 'new' 
  map.logout '/logout', :controller => 'sessions', :action => 'destroy'
..

Jetzt können wir uns mit den Urls ../login und ../logout an- und abmelden. Zusätzlich bauen wir eine Login/Logout-Link ein. Da wir den Link auf allen Glossar-Seiten haben wollen, machen wir das in die Seitenvorlage für alle Glossarseiten.

Formatvorlage (Template) überarbeiten

Bearbeiten

Im Grunde genommen sind wir jetzt fertig. Zumindest mit kleinen Glossare mit sagen wir 50 oder 60 Einträgen kommt unsere Applikation zurecht. Für größere Glossare wollen wir vielleicht Paging oder einen Buschstabenindex ergänzen. Das kommt dann später. Jetzt wollen wir uns nur noch das Layout ein bischen verbessern.

Dazu gehen wir in die Formatvorlage /app/views/layouts/glosentries.html.erb. Wir sehen in der Mitte eine Yield Anweisung (<%= yield %>). Hiermit werden die views (/app/views/glosentries/edit.html.erb, ..) eingebunden. Darüber eine <%= flash[:notice] %>-Anweisung, die Meldungen anzeigt. Wir erinnern uns an die Validierung der Eingaben. Wenn eine Validierung fehlschlägt, weil beispielsweise das keyword leer ist wird eine Meldung hinterlegt und hier ausgegeben.

In dieser Formatvorlage ergänzen wir ein paar div-blöcke, z.B.

<div id="header">

und

<div id="footer">

.

Dazu ergänzen wir ein stylesheet für die Applikation und passen die view Dateien (/app/views/glosentries/edit.html.erb, ..) an die Formatvorlage an. Das Ergebnis sieht dann so aus:


.. TODO Quelltext rein
..
..

Das ist doch ein guter erster Wurf.

TODO Bild rein

Ausblick

Bearbeiten

Das war ein langes Kapitel und wahrscheinlich war nicht alles auf Anhieb verständlich, denn manche Dinge haben wir einfach gemacht, ohne sie zu erklären. Wir haben das Ziel des Kapitels erreicht, wenn wir eine ersten Eindruck gewonnen haben wie effizient sich auch komplexe Webapplikationsstrukturen mit Rails entwickeln lassen.

Bevor wir im nächsten Kapitel noch mal von vorn anfangen und dann bei jeden Schritt genau ansehen, was wie gemacht wird und warum es funktioniert, rekapitiulieren wir welche grundlegenden Mechanismen wir eingesetzt haben und welchen Code wir nicht geschrieben haben - nicht schreiben mussten.

  • Scaffolding ..
  • Konvention over Konfiguration ..
  • Ruby Syntax

In den nächsten Kapiteln ...