Rails Tutorial DHBW-Heidenheim | ||
Verzeichnisstruktur einer rails AnwendungAlle Rails Projekte haben die gleiche - von rails erzeugte - Verzeichnisstruktur. In dieser Struktur sind u.a. Verzeichnisse für Konfigurationen, Tests, öffentliche Bereiche und Anwendungsteile enthalten. Im Folgenden wollen wir uns die wichtigsten einmal ansehen:
app enthält die Models, Controller, Views und Helper Dateien config enthält Konfigurationsdateien für das Rails Projekt, beispielsweise für die Datenbank, das Routing oder die Rails-Umgebung db enthält die Definition der Datenbank. Im Verzeichnis db/migrate liegen die einzelnen Migrationsdateien für jede Datenbanktabelle. Darin ist beschrieben, wie die entsprechende Tabelle aufgebaut ist und ggf. welchen Inhalt sie haben soll. Genauer gesagt sind hier die Informationen enthalten, die benötigt werden, um die Tabelle in einer bestimmten Version herzustellen. Rails unterstützt bei Datenmodellen mehrere Versionen. Wird etwa im Zuge eines refactorings eine Tabelle in der Entwicklungsumgebung geändert, muss die Tabelle später in der Produktionsumgebung auch angepasst werden. Genau dies leistet eine Migration (Deshalb heisst sie auch so). Die Datei db/schema.rb enthält eine Zusammenfassung der einzelnen Migrationen. lib enthält Code, der von mehreren Models, Controllern oder Views verwendet wird. Es kann aber auch Code abgelegt werden, der keine der MVC-Komponenten direkt betrifft. public enthält Dateien und Verzeichnisse, die von aussen aufrufbar sein sollen. Das sind typischerweise statische HTML-Dateien, Stylesheets, Bilder usw. Das Verzeichnis kann beliebig erweitert werden. Per Definition gibt es bereits die Unterverzeichnisse /images, /stylesheets und /javascripts. test enthält alle Testdateien für eine Rails-Anwendung, wie beispielsweise Dateien für Funktionstests oder Modelltests. Es können aber auch Beispieldaten abgelegt werden. vendor In diesem Verzeichnis werden projektspezifische Rails-Erweiterungen abgelegt. Model erzeugen: AltersgruppeEin Model entspricht einer Klasse aus dem Analysis-Level Datenmodell. Es besteht aus einem Namen (hier: Altersgruppe) und den zugehörigen Attributen (hier: von; bis). Ein Model hat genau eine zugehörende Datenbanktabelle. Per Konvention ist der Tabellenname der Plural des Modelnamens und die Spalten entsprechen den Attributen. Die Klasse, oder das Modell spiegelt die Datenbanktabelle wieder, ein Objekt entspricht einem einzelnen Datensatz. Es wäre mühsam dieses Konstrukt von Hand anzulegen, deshalb verwenden wir den von Ruby on Rails bereitgestellten Modelgenerator: mb:rs till$ rails generate model Altersgruppe von:integer bis:integer person:references invoke active_record create db/migrate/20110210181453_create_altersgruppes.rb create app/models/altersgruppe.rb invoke test_unit create test/unit/altersgruppe_test.rb create test/fixtures/altersgruppes.yml Was wurde generiert ? Der Modelgenerator hat vier Dateien generiert: Die Migrationsdatei, die Modeldatei, die Testdatei und die Datei für Beispieldaten. Die Ausgabe des Modelgenerators hat jedoch noch zwei weitere Zeilen erzeugt. Diese haben einen informativen Charakter und sagen aus, dass während der Generierung des Models Altersgruppe zwei weitere Generatoren aufgerufen wurden, der active_record Generator und der test_unit Generator. Datenbanktabelle für das Model erzeugenMit dem Befehl rake db:migrate wird die Datenbank auf den aktuellen Stand gebracht, hier also die Tabelle für die Altersgruppen erzeugt. Rake ist die Ruby-Variante von make. Ruby Make oder einfach nur rake ist ein Tool, mit Hilfe dessen mehrere Schritte in Abhängigkeit von Bedingungen zusammengefasst werden können. Unsere Beispielanwendung hat laut Datenmodell fünf Datenbanktabellen. Die Tabellen könnten manuell in der Datenbank unter Berücksichtigung der Rails Konventionen angelegt werden. Dazu muss zunächst das Analysis-Level Datenmodell in SQL übersetzt werden, anschliessend der SQl-Code ausgeführt und schliesslich die Daten in die Datenbank geschrieben werden. Rake kann diese Aufgaben zusammenfassen. Im Ruby Standard ist ein entsprechendes Rakefile für Migrationen enthalten. In einem Rakefile sind alle Tasks für einen Job zusammengefasst. Eine Liste der vorhandenen Rake Tasks kann mit dem Befehl rake --task abgefragt werden: mb:rs till$ rake --task (in /rs/Beispiel) rake about # List versions of all Rails frameworks and the environment rake db:create # Create the database from config/database.yml for the current Rails.env (use db:create:all to create al... rake db:drop # Drops the database for the current Rails.env (use db:drop:all to drop all databases) rake db:fixtures:load # Load fixtures into the current environment's database. rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false). rake db:migrate:status # Display status of migrations rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n). rake db:schema:dump # Create a db/schema.rb file that can be portably used against any DB supported by AR rake db:schema:load # Load a schema.rb file into the database rake db:seed # Load the seed data from db/seeds.rb rake db:setup # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the... rake db:structure:dump # Dump the database structure to an SQL file rake db:version # Retrieves the current schema version number rake doc:app # Generate docs for the app -- also availble doc:rails, doc:guides, doc:plugins (options: TEMPLATE=/rdoc... rake log:clear # Truncates all *.log files in log/ to zero bytes rake middleware # Prints out your Rack middleware stack rake notes # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus) rake notes:custom # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM rake rails:template # Applies the template supplied by LOCATION=/path/to/template rake rails:update # Update both configs and public/javascripts from Rails (or use just update:javascripts or update:configs) rake routes # Print out all defined routes in match order, with names. rake secret # Generate a cryptographically secure secret key (this is typically used to generate a secret for cookie... rake stats # Report code statistics (KLOCs, etc) from the application rake test # Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:pro... rake test:recent # Run tests for recenttest:prepare / Test recent changes rake test:uncommitted # Run tests for uncommittedtest:prepare / Test changes since last checkin (only Subversion and Git) rake time:zones:all # Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET paramet... rake tmp:clear # Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:s... rake tmp:create # Creates tmp directories for sessions, cache, sockets, and pids Am interessantesten sind die Tasks, die die Datenbank betreffen. So erzeugt rake db:create eine leere Datenbank. Mit dem Zusatz RAILS_ENV="environment" kann die Umgebung ausgewählt werden für die die Datenbank erzeugt werden soll, etwa rake db:create RAILS_ENV="production" Soll die existierende Datenbank gelöscht und wieder neu angelegt werden, also praktisch auf den "Auslieferungszustand" zurückgesetzt werden, kann dies durch den Befehl rake db:reset erreicht werden. Am wichtigsten ist rake db:migrate. Dadurch wird die Datenbank auf den aktuellen (oder einen angegebenen älteren) Stand gebracht, also etwa die Tabellen und deren Inhalt angelegt. Wenn es erforderlich ist, können eigene Rakefiles erzeugt werden. Hier wird jedoch nicht darauf eingegangen (siehe zum Beispiel hier ) mb:rs till$ rake db:migrate (in /rs/Beispiel) == CreateAltersgruppes: migrating ============================================ -- create_table(:altersgruppes) NOTICE: CREATE TABLE will create implicit sequence "altersgruppes_id_seq" for serial column "altersgruppes.id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "altersgruppes_pkey" for table "altersgruppes" -> 0.0128s == CreateAltersgruppes: migrated (0.0129s) =================================== mb:rs till$ \d altersgruppes; Table "public.altersgruppes" Column | Type | Modifiers ------------+-----------------------------+------------------------------------------------------------ id | integer | not null default nextval('altersgruppes_id_seq'::regclass) von | integer | bis | integer | person_id | integer | created_at | timestamp without time zone | updated_at | timestamp without time zone | Indexes: "altersgruppes_pkey" PRIMARY KEY, btree (id) Die generierte Modeldatei /app/model/altersgruppe.rb: class Altersgruppe < ActiveRecord::Base belongs_to :person end Das Schlüsselwort class leitet eine Klassendefinition ein. Die Klasse hat den Namen Altersgruppe. Der Ausdruck "< ActiveRecord::Base" bedeutet, dass die Klasse Altersgruppe von ActiveRecord::Base erbt. Doch wo sind die Methoden dieser Klasse ? Die generierte Klasse hat bereits alle Methoden, die wir in Lesson 2 unbewusst verwendet haben, etwa das Finden von Datensätzen auf der Datenbank wie zum Beispiel find, find_by_sql, new, create, delete und so weiter. Eine Liste mit allen Klassenmethoden von ActiveRecord::Base ist hier zu finden. Mit der Ruby Shell können wir jetzt auf Klassen und die Datenbank unseres Projektes zugreifen. mb:rs till$ rails console Loading development environment (Rails 3.0.3) irb(main):001:0> a = Altersgruppe.new => #<Altersgruppe id: nil, von: nil, bis: nil, person_id: nil, created_at: nil, updated_at: nil> irb(main):002:0> a.von = 10 => 10 irb(main):003:0> a.bis = 20 => 20 irb(main):004:0> a.save => true irb(main):005:0> b = Altersgruppe.find(1) => #<Altersgruppe id: 1, von: 10, bis: 20, person_id: nil, created_at: "2011-02-10 18:23:34", updated_at: "2011-02-10 18:23:34"> irb(main):006:0> b.von => 10 irb(main):007:0> b.bis => 20 irb(main):008:0> Mit dem Befehl a = Altersgruppe.new wird ein neues Objekt der Klasse Altersgruppe mit Namen a erzeugt. Dem erzeugten Objekt können wir jetzt Werte zuweisen. Mit dem Befehl a.save werden die Änderungen in die Datenbank geschrieben: select * from altersgruppes; id | von | bis | person_id | created_at | updated_at ----+-----+-----+-----------+----------------------------+---------------------------- 1 | 10 | 20 | | 2011-02-10 18:23:34.530822 | 2011-02-10 18:23:34.530822 (1 row) Mit der Anweisung b = Altersgruppe.find(1) wird der Datensatz mit der ID 1 aus der Tabelle altersgruppes ausgelesen und die Werte in den Spalten den korrespondierenden Objektattributen von b zugewiesen. Beispielsweise ist der Wert vom Objektattribut b: mb:rs till$ rails console Loading development environment (Rails 3.0.3) irb(main):001:0> b = Altersgruppe.find(1) => #<Altersgruppe id: 1, von: 10, bis: 20, person_id: nil, created_at: "2011-02-10 18:23:34", updated_at: "2011-02-10 18:23:34"> irb(main):002:0> b.von => 10 Ein einfacher ControllerIm vorherigen Kapitel haben wir das Model Altersgruppe erzeugt. Diese Modellklasse steht jetzt in unserem Projekt bereit und kann verwendet werden. Allerdings kann nicht, wie in Lesson 2, auf Daten "vom Browser aus" zugegriffen werden. Hierzu sind noch einige Schritte notwendig. Was ist ein Controller, oder besser, was kontrolliert er? Ein Controller kontrolliert nicht, er steuert. Er steuert die Ausgabe für den Benutzer, also was ein Benutzer sieht. In unserer Terminologie also den View. Wenn unsere Beispielanwendung fertig ist, registrieren sich viele Menschen. Unter diesen sind Mary und Jim. Beide haben ein eigenes Profil, in dem beispielsweise Nickname oder Alter gespeichert ist. Wenn jemand sein Profil ansehen möchte stellt der Controller sicher, dass die richtigen Daten aus der Datenbank beschafft werden. Wenn Mary Ihr Profil ansehen möchte, stellt der Controller sicher, dass Marys Daten aus der Datenbank abgeholt werden und nicht die von Jim. In unserm Beispiel gibt es aber nicht nur unterschiedliche Daten, sondern auch unterschiedliche Anzeigemöglichkeiten. Der Controller reagiert je nach Art des Aufrufs oder der vorgegebenen Bedingungen, an welche View Daten übergeben werden.
Von diesem MVC-Muster gibt es in der Praxis unterschiedliche Ausprägungen (siehe etwa hier ). In rails ist der Controller eine Instanz zwischen Model und View. Eine View greift nicht direkt auf Daten zu und ein Model gibt keine Daten direkt an Views weiter. Der Controller holt Daten durch das Model aus der Datenbank und gibt diese an die entsprechende View weiter . Das Gleiche gilt für Manipulationen der Daten. Einen Controller anlegenWie beim Model stellt Ruby on Rails auch hier einen Generator bereit, der das gesamte Konstrukt rund um den Controller erzeugt. mb:rs till$ rails generate controller altersgruppes create app/controllers/altersgruppes_controller.rb invoke erb create app/views/altersgruppes invoke test_unit create test/functional/altersgruppes_controller_test.rb invoke helper create app/helpers/altersgruppes_helper.rb invoke test_unit create test/unit/helpers/altersgruppes_helper_test.rb Wiederum werden einige Dateien erzeugt, der eigentliche Controller (altersgruppes_controller.rb), ein Unit-Test dazu (altersgruppes_controller_test.rb), eine Klasse für Helper (altersgruppes_helper_.rb) und auch hierfür ein Test (altersgruppes_helper_test.rb). Ausserdem wird ein Verzeichnis für die zugehörigen Views angelegt (app/views/altersgruppes). Wie schon bei der Generierung der Modelklassen erwähnt, werden durch die invoke Kennzeichnung Untergeneratoren ausgeführt. class AltersgruppesController < ApplicationController end Das Schlüsselwort class leitet wiederum eine Klassendefinition ein. Die Klasse hat den Namen AltersgruppesController. Klassennamen werden im CamelCase, also mit grossen Anfangsbuchstaben geschrieben. Per Konvention ist der Name des Controllers aus dem Namen der Klasse (im Plural) und dem Begriff Controller zusammengesetzt. Der Controller ist absofort hierüber ansprechbar. Genau wie das Model ist der Controller eine Ruby Klasse. Der Controller AltersgruppesController erbt von der Klasse ApplicationController, die die benötigten Funktionen bereitstellt. Der Controller stellt die Steuerzentrale einer mit Ruby on Rails geschriebenen Webapplikation dar. Ein spezieller Controller, der ApplicationController, wird beim Erstellen einer Anwendung mitgeneriert und in app/controllers/ abgelegt. Der ApplicationController ist innerhalb der anderen Controller erreichbar, denn diese erben von Ihm.
Der ApplicationController dient u.a. zum Schutz vor Manipulationen oder zur Absicherung der Webapplikation. Öffentliche Methoden sind in allen anderen Controllern erreichbar. Wir gehen später detaillierter auf die Möglichkeiten des ApplicationControllers ein. Eine ControllermethodeBisher tut unser Controller noch nichts, das wollen wir jetzt ändern. Wir erstellen eine index-Methode, die zunächst einfach einen festen Text ausgibt: Class AltersgruppesController < ApplicationController def index render :text =>"Hello world - from altersgruppes_controller" end end Das Routing einrichten (hierzu später mehr) Beispiel::Application.routes.draw do resources :altersgruppes # The priority is based upon order of creation: # first created -> highest priority. # Sample of regular route: # match 'products/:id' => 'catalog#view' # Keep in mind you can assign values other than :controller and :action # Sample of named route: Server starten mb:rs till$ rails server => Booting WEBrick => Rails 3.0.3 application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2011-02-10 18:30:35] INFO WEBrick 1.3.1 [2011-02-10 18:30:35] INFO ruby 1.8.7 (2008-08-11) [i486-linux] [2011-02-10 18:30:35] INFO WEBrick::HTTPServer#start: pid=4296 port=3000 Ergebnis angucken (http://localhost:3000/altersgruppes/index)
Der View dazuUnser erster Controller hat den Text direkt ausgegeben. Das ist natürlich nicht der Sinn der Sache, die Ausgabe soll ja über einen View erfolgen, den wir jetzt anlegen. Der Generator des Controllers Altersgruppes hat das Verzeichnis app/views/altersgruppes erzeugt, in diesem Verzeichnis werden alle Views für den Controller Altersgruppes abgelegt. Einen View anlegenDa unsere Controller-Methode index heisst, legen wir im Verzeichnis app/views/altersgruppes eine Datei mit dem Namen index.html.erb an. In der Präsentationsschicht können verschiedene Anzeigemöglichkeiten verwendet werden. Das Ergebnisset einer Controllermethode könnte beispielsweise in xml, html oder pdf angefordert werden. Standardmässig wird vom Controller auf Anfragen mit der HTML-Variante geantwortet, es wird also eine HTML-Seite ausgegeben. Vom Controller beschaffte Daten werden für die HTML-Ausgabe in der View aufbereitet. Um dies zu ermöglichen, gibt es ERB-Templates. ERB steht daher für embedded Ruby: ERB-Templates bieten die Möglichkeit, Ruby-Code in HTML einzubetten. Es kann u.a. auf Daten des Controllers oder auf Methoden des Models zugegriffen werden. Wie bei anderen, ähnlichen Technologien auch, muss hier zwischen HTML-Code und dem eigebettenen Ruby-Code unterschieden werden. Dazu gibt es verschiedene Möglichkeiten: Wird der Code mit <% Ruby Code %> eingebettet, wird der Code zwar ausgeführt, es wird jedoch keine Ausgabe erzeugt. Der Tag wird bspl. für Schleifen oder IF-Verzweigungen etc. verwendet. <%= Ruby Ausgabe%> erzeugt eine Ausgabe. Diese Variante wird verwendet, wenn etwa eine Variable des Controllers ausgegeben werden soll. ERB-Templates werden im Übrigen auch von den Standardgeneratoren wie etwa scaffold oder dem Controller Generator erzeugt. Aber es kann nicht nur HTML erzeugt werden, für andere Formate wie XML müssen nur entsprechende Templates erstellt werden.
Wir fügen der Datei app/views/altersgruppes/index.html.erb einen Text hinzu: <P>Hello from index view</> Bevor wir uns das Ergebnis ansehen können, muss die Ausgabe vom Controller entfernt werden: Class AltersgruppesController < ApplicationController def index end end Das liefert dann folgendes Ergebnis:
Die Verbindung zur Person - Assoziationen in Ruby on RailsUnser Datenmodell zeigt uns die Bezieg´hungen der Klasse Person: Eine Person hat eine Altersgruppe (1:1), mehrere Messages (1:n), mehrere Hobbies (1:n) und gehört zu mehreren Gruppen (1:n). Solche Beziehnungen werden in rails durch Assoziationen abgebildet. Eine Assoziation setzt sich aus Beziehungen auf der Datenebene und aus Beziehungen auf der Modellebene zusammen Beziehungen auf DatenebeneAuch hier existieren wieder einige Konventionen: Als wir das Model Altersgruppe angelegt haben, haben wir dem Generator die Beziehung mitgeteilt. (person:references). Die Datenbanktabelle altersgruppes hat die Spalte person_id. Beziehungen auf ModellebeneAls nächstes müssen wir die Beziehungen auf der Modellebene einrichten. Der Fremdschlüssel person_id in der Tabelle altersgruppes verweist auf die Tabelle Person. Der Modelgenerator hat die Modeldatei altersgruppe.rb um die Zeile :belongs_to :person erweitert.Es fehlt noch die Beziehung im Person Modell. Wir erweitern die Datei /app/models/person.rb daher wie folgt: class Person < ActiveRecord::Base has_many :messages end Eine Person gehört zu genau einer Altersgruppe, hier liegt eine 1:1 Beziehung vor. Bei einer 1:1-Beziehung steht in dem Model, auf das verwiesen wird has_one (/app/models/person.rb): Class Person < ActiveRecord::Base has_many :messages has_one :altersgruppe end Model um Methoden erweiternNicht nur der Controller, sondern auch das Model wird im Allgemeinen um eigene Methoden erweitert. Hier können etwa aus den enthaltenen Daten andere Werte berechnet werden (Auf die viel häufigeren Beispiele wie etwa die Überprüfung von Daten, die sogenannte Validierung gehen wir später ein). Wir wollen in unserer Anwendung anzeigen, wie viele Menschen zu einer bestimmten Altersgruppe gehören. Dazu bearbeiten wir das Altersgruppe-Modell: class Altersgruppe < ActiveRecord::Base belongs_to :person #----------------------------------------------- #get_sum_person #Übergabeparameter: id der Altersgruppe #Methode gibt die Summe aller #Menschen in einer Altersgruppe zurück #------------------------------------------------ def get_sum_person(altersgruppe_id) altersgruppe = Altersgruppe.find(altersgruppe_id) summe = altersgruppe.people.count return summe end end Warum gehört diese Methode ins Model und nicht in den Controller? Alle Funktionen, die Daten eines Objektes verwenden oder diese manipulieren bzw. eine direkte Funktion eines Objektes sind, werden im Modell implementiert. Verwendet werden diese in den Controllern oder den Views. Eine Model-Methode wird ebenso wie eine Methode eines Controllers mit dem Schlüsselwort def (= define) eingeleitet, gefolgt vom Methodennamen. Der Rückgabewert einer Klassenmethode kann mit return gesetzt werden. In unserem Fall wird die Summe der Menschen in der Altersgruppe "von", "bis" zurückgegeben. Controller jetzt mal ernsthaft - was noch fehltIn unserer Anwendung soll es nicht möglich sein, Altersgruppen zu erzeugen, zu löschen, zu bearbeiten oder zu ändern. Dies würde wohl wenig Sinn ergeben, die Altersgruppen werden einmal in der Datenbank angelegt und dann nie wieder verändert. Dies könnte natürlich über eine separate SQL-Datei erfolgen, die dann manuell ausgeführt wird, rails bietet aber genau dazu die Möglichkeit, sogenannte seed-Daten anzulegen. In der Datei db/seeds.rb kann ruby Code hinterlegt werden, der durch die task rake:seed ausgeführt wird. Oft ist es einfacher, Startdaten mit einem kurzen Script zu erzeugen, als diese direkt als SQL-INSERT-Befehle zu kodieren. In unserem Beispiel wollen wir verscheidene Altersgruppen erzeugen. Dies ist natürlich mit einer Schleife einfach: Altersgruppe.create(:von => 0, :bis => 19) Altersgruppe.create(:von => 60, :bis => 99) (2..5).each {|a| Altersgruppe.create(:von => a*10, :bis => (a+1)*10-1) } Deshalb wird unser AltersgruppesController ziemlich einfach, er hat nur die Methode index. Werden andere Funktionen, etwa für die Verwaltung der Daten benötigt, kann der Controller erheblich komplizierter werden, siehe etwa beim PeopleController. class AltersgruppesController < ApplicationController def index end end Allerdings wollen wir uns anzeigen lassen, wie viele Menschen in einer bestimmten Altersgruppe sind. Hierzu bearbeiten wir den PeopleController wie folgt: # GET /people/1 # GET /people/1.xml def show @person = Person.find(params[:id]) @Nachrichten = @person.messages @person_in_altersgruppe = Altersgruppe.get_sum_person(@person.altersgruppe_id) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @person } end end In der Variablen @person_in_altersgruppe steht das Ergebnis der Klassenmethode get_sum_person von der Klasse Altersgruppe. Durch das vorstehende @ ist diese Variable auch ausserhalb der Klasse sichtbar. Eine View kann diese Variable jetzt verarbeiten. Wird kein @ vorangesellt, ist die Variable nur innerhalb der entprechenden Controllermethode sichtbar, also eine lokale Variable. Person view anpassenDie Anzahl der Personen in einer Altersgruppe wird in der Controller Methode show ermittelt und wird daher in der View show.html.erb im Ordner app/views/people ausgegeben: <p> <b>Ichkurz:</b> <%= @person.IchKurz %> </p> <p> Es sind weitere <%= @person_in_altersgruppe %> Menschen in dieser Altersgruppe </p> <p> <%=@Nachrichten.count%> Nachrichten </p> <%= link_to 'Edit', edit_person_path(@person) %> | <%= link_to 'Back', people_path %> In einer View wird hauptsächlich html code verwendet. Werden Daten vom Controller benötigt, oder muss weitere Logik implementiert werden, kann aber auch Ruby-Code eingesetzt werden. Nach Möglichkeit sollte dies aber vermieden werden, weil dadurch die saubere Trennung zwischen Darstellung (View) und Logik (Controller und Model) verwischt wird. Dies hat eine Reihe von Nachteilen: Hat man etwa mehrere Views (etwa einen für normale Rechner, einen für iphones, einen für XML-Ausgabe) muss die Logik an mehreren Stellen implementiert werden. Dies führt im Lauf der Zeit zu Problemen bei der Wartung. Ausserdem ist es besser, wenn die Views möglichst wenig Code enthalten, dann können diese einfacher ohne Programmierkenntnisse gewartet werden. Soll im View auf Variablen des Controllers zugegriffen werden, oder beliebiger ruby-Code ausgeführt werden, muss kenntlich gemacht werden, dass jetzt etwas anderes als HTML-Text folgt. Dies erfolgt durch spezielle Tags: In einem Absatz der mit einem P-Tag umschlossen ist, wird Text ausgegeben. Nach "Es sind weitere" wird Ruby Code eingeleitet, der eine Ausgabe ermöglicht. Der Inhalt der Variablen @person_in_altersgruppe wird ausgegeben. Nach dem schliessenden %> wird der restliche Text "Menschen in dieser Altersgruppe " ausgegeben. Die Ausgabe:
Die restlichen Daten: Gruppen und HobbiesIm nächsten Schritt legen wir die Models und Controller zu den Gruppen und Hobbies an. Wir legen die Models mit den entsprechenden Attributen aus dem Datenmodell an. Die Controller bleiben zunächst leer. mb:rs till$ rails generate model Hobby lang:string kurz:string invoke active_record create db/migrate/20110210184208_create_hobbies.rb create app/models/hobby.rb invoke test_unit create test/unit/hobby_test.rb create test/fixtures/hobbies.yml mb:rs till$ rails generate model Gruppe lang:string kurz:string invoke active_record create db/migrate/20110210184221_create_gruppes.rb create app/models/gruppe.rb invoke test_unit create test/unit/gruppe_test.rb create test/fixtures/gruppes.yml mb:rs till$ mb:rs till$ rails generate controller hobbies create app/controllers/hobbies_controller.rb invoke erb create app/views/hobbies invoke test_unit create test/functional/hobbies_controller_test.rb invoke helper create app/helpers/hobbies_helper.rb invoke test_unit create test/unit/helpers/hobbies_helper_test.rb mb:rs till$ rails generate controller gruppes create app/controllers/gruppes_controller.rb invoke erb create app/views/gruppes invoke test_unit create test/functional/gruppes_controller_test.rb invoke helper create app/helpers/gruppes_helper.rb invoke test_unit create test/unit/helpers/gruppes_helper_test.rb Effiziente Views durch partialsEin wesentliches Prinzip bei rails ist Don't repeat yourself. In der Praxis kommt es oft vor, dass in mehreren Views die selben Inhalte ausgegeben werden. Eine grundlegende Idee bei rails ist, bei solchen Situationen keinen Code (keine Informationen) zu duplizieren, sondern diese nur an einer Stelle zu halten. Die Code Teile, die öfters verwendet werden, werden an einem zentralen Ort einmal abgelegt, an den Stellen, an denen sie benötigt werden, wird dann nur verweisen. Durch diese Partials kann effizient entwickelt werden, denn ein Codeabschnitt wird nur einmal geschrieben und wenn er geändert wird, geschieht dies ebenfalls an einer Stelle. Das erleichtert insbesondere die Wartung erheblich. Wir legen im Verzeichnis app/views eine Ordner names shared an. Alle Dateien in diesem Ordner beginnen mit einem Unterstrich "_" - das ist der Hinweis auf ein Partial (Konvention !). In dieser Datei wird Code in gewohnter Weise abgelegt. An der Stelle einer View, an der das Partial ausgegeben werden soll, wir folgende Zeile eingefügt: <%= render :partials=>shared/name_des_partials %> Partials müssen nicht in einem gemeinsamen Ordner gespeichert werden, wenn Sie nur von einem einem Controller/View verwendet werden, können sie (natürlich) im entsprechenden View-Ordner abgelegt werden. |