14:27 Uhr @HeikoKrebs wenn es ma losgehen würde ;-)
14:26 Uhr Moa, der Flieger nach Tripolis hat schon 1 1/2h Verspätung. Wehe, ich verpasse den Anschlussflug.
09:04 Uhr Ist das hier ein Kaffeetreff oder das Bürgeramt in Gievenbeck?
vom 30.03.2010
Das offizielle Quickstart Tutorial von Zend zum Zend Framework ist nicht schlecht. Allerdings fehlt es hin und wieder an der Erwähnung von einigen Stolpersteinen. Einer davon hat mich zwar nicht lange aufgehalten, sei aber dennoch erwähnt.
Verwendet man wie vorgeschlagen einen eigenen virtuellen Host und setzt dabei die Befehle AllowOverride All und Order allow,deny ein, um mit htacess Dateien arbeiten zu können, muss zwingend das Rewrite module von PHP eingeschaltet sein. Bei einer Standard-PHP-Installation ist das nicht der Fall.
Das Modul kann in der httpd.conf im conf Verzeichnis des Apaches (wenn ihr den verwendet) durch entfernen der Raute vor der Zeile LoadModule rewrite_module modules/mod_rewrite.so aktiviert werden.
vom 28.03.2010
Eigentlich sollte es ganz einfach sein Ruby und Ruby gems ordentlich zum Laufen zu bringen, schließlich habe ich das jetzt auch nicht zum ersten Mal gemacht. Windows 7 x64 hielt aber gerade eine neue Überraschung für mich bereit. Nach erfolgreicher Installation wollte ich ein Update für gems über gem update --system -y vornehmen. Das führte zu folgender Fehlermeldung:
sh.exe": /c/Web/Ruby/bin/gem: D:/Users/Luis/projects/oss/installer2-trunk/ruby/bin/ruby.exe bad interpreter: no medium
Ursache des Problems sind zwei falsche Pfadangaben in den Skriptdateien gem und rake, die beide im bin-Verzeichnis der Rubyinstallation liegen. Hier lautet die erste Zeile in der gem
#! D:/Users/Luis/projects/oss/installer2-trunk/ruby/bin/ruby.exe
und in rake
#!D:/Users/Luis/projects/oss/installer2-trunk/ruby/bin/ruby
Diesen Pfad gibt es so natürlich gar nicht. Das Problem konnte ich lösen, indem ich den Pfad in der gem angepasst habe auf
#!c:/Web/Ruby/bin/ruby.exe
und in der rake auf
#!/user/bin/env ruby
Im Falle der rake, muss natürlich der Pfad in den Umgebungsvariablen gesetzt sein. Sollten jetzt noch Probleme auftauchen, die permission denied als Ursache haben, könnt Ihr mal probieren, die Bash als Administrator auszuführen.
vom 12.03.2010
Es ging mal wieder hoch her in der letzten Zeit, ein neues Projekt hat meine ganze Aufmerksamkeit gefordert. Seit langem habe ich wieder Zeit für etwas Web-Entwicklung und da wollte ich mir die neue Version vom Zend Framework angucken (1.10.2). Zuletzt habe ich mit der 1.5 gearbeitet, es gibt also viel Neues zu entdecken.
Doch gleich am Anfang hakte es. Das zf Command Line Tool wollte nicht so, wie ich es gerne hätte, bzw. wie es die Online-Hilfe suggerierte. Beim Anlegen eines neuen Projekts erhielt ich nur folgende Meldung:
PHP Warning: mkdir(): No such file or directory in C:\Program Files\Zend\ZendServer\share\ZendFramework\library\Zend\Tool\Project\Provider\Project.php on line 55 Warning: mkdir(): No such file or directory in C:\Program Files\Zend\ZendServer\share\ZendFramework\library\Zend\Tool\Project\Provider\Project.php on line 55 An Error Has Occurred Could not create requested project direcotry 'test' [...]
Kurzer Vorspann bis hier hin (unter Windows):
zf show version ausführen. Als Ergebnis sollte die aktuelle Version des Zend Frameworks zurückgegeben werden.zf --setup storage-directory und anschließend zf --setup config-file ausführen. Der erste Befehl erzeugt ein Verzeichnis .zf in Eurem Windows-Benutzerstammverzeichnis, der zweite Befehlt legt in diesem eine Datei .zf.ini an, in der die Konfiguration gespeichert wird. Diese könnt Ihr jetzt mit einem Editor öffnen und sofern nicht vorhanden den include_path mit dem Pfad zum library-Verzeichnis anlegen (inklusive "\library\").Trotz all dieser Aktivitäten trat der oben genannte Fehler auf. Nach etwas Ausprobieren, war die Lösung dann jedoch sehr einfach, wenn auch nicht wirklich logisch. Statt direkt den ganzen Befehl zum Erzeugen eines Projekts einzugeben (zf create project PFADANGABE), habe ich zunächst nur zf create projet eingegeben, woraufhin nach dem Pfad gefragt wird:
zf create project Please provide a value for $path zf> C:\Program Files\Zend\Apache2\htdocs\test Creating project at C:\Program Files\Zend\Apache2\htdocs\test [...]
Wie man sieht, hat das den gewünschten Erfolg gebracht - klingt komisch, ist aber so. Vielleicht hilfts wem.
vom 09.01.2010
Man stößt doch immer mal wieder auf Probleme, die auf den ersten Blick absurd sind und bei denen man sich wundert, dass man sie vorher noch nie bemerkt hat. Ein solches Problem ist mir heute untergekommen: Die Verwendung vom base-Tag und Anker-Links (also Verweise innerhalb einer einzelnen Seite). Hier tritt das Problem auf, dass der Anker an die im base-Tag angegebene Adresse angehängt wird und man bei einem Klick zu dieser weitergeleitet wird - das ist ja nun wirklich nicht der Sinn und Zweck eines Ankers.
Die erste Lösgung war, an alle Links mit Ankern mittels einer serverseitigen Programmiersprache die aktuelle Adresse voranzustellen, das gefiel mir aber so gar nicht. Die nächste Idee war es, das Ganze mit jQuery abzufangen und diese Lösung gefällt mir bisher ganz gut - auch wenn sie längst nicht ideal ist (was ist zum Beispiel, wenn jemand kein JavaScript aktiviert oder zur Verfügung hat?). Hier also erst einmal der Code:
<script type="text/javascript" charset="utf-8">
//<![CDATA[
$(document).ready(function() {
$("a[href^='#']").live("click", function(event){
document.location.hash=this.href.substr(this.href.indexOf('#')+1);
return false;
});
});
//]]>
</script>
Zunächst einmal wird der Code immer ausgeführt, sobald das DOM vollständig geladen ist ($(document).ready(function()). Anschließend werden mittels $("a[href^='#']") alle Links betrachtet, deren Adress-Tag (href) ausschließlich einen Anker enthalten, das heißt als erstes Zeichen einen Hash (#) haben. Von diesen Links wird das Klick-Event abgefangen, mit live, um auch nachgeladene Links zu erfassen. Wird jetzt einer dieser Links ausgewählt, wird dessen Anker ausgelesen (substr(this.href.indexOf('#')+1)) und über document.location.hash wird die aktulle URI (location) mit dem Ankerwert (hash) angesteuert. Letztendlich muss noch die Standardaktion für Links ausgeschaltet werden. Dies könnte man auch mit der Funktion preventDefault erledigen, ich verwende hier return false;.
Wie gesagt, nicht ideal, aber zumindest eine Lösung, die für den Großteil der Nutzer zweckdienlich ist. Wer hier eine bessere Strategie kennt, kann mir die gerne mal schicken.
vom 02.01.2010
Erst kürzlich habe ich diesen Vortrag vom JavaScript Guru Douglas Crockford entdeckt, den er auf der im Rahmen der Google Tech Talks Web Exponents Serie am 27. Februar 2009 gehalten hat.
vom 14.12.2009
Es gibt ja mittlerweile schon eine - für die kurze Zeit beachtliche - Anzahl an Erweiterungen für Google Chrome, zumindest, wenn man die aktuelle Beta installiert hat. Diese läuft bei mir bisher einwandfrei und ich bleibe zuversichtlich, dass das so bleibt. Ein paar Erweiterungen konnte ich auch schon identifizieren, auf die ich nach wenigen Tagen nicht mehr verzichten möchte:
Zum normalen Surfen ist Chrome damit super gerüstet. Beim Entwickeln bleibt der Firefox weiterhin mein Favorit.
vom 01.12.2009
Nachdem in meinem kleinen Railsprojekt die Migrationsskripte (definieren die Datenbankstruktur) ferti erstellt sind, wird es endlich Zeit, dass ich mich um die Model-Ebene (ActiveRecord) der MVC-Architektur kümmere. Die Modelle beziehungsweise in Rails ActiveRecord steuern unabhängig von Controller und View den Zugriff auf - in diesem Fall - die Datenbank. ActiveRecord ist eine ORM-Schicht, die die Objekte der Anwendung direkt in der Datenbank vorhält. Dabei wird eine Klasse eines Models auf eine Datenbanktabelle sowie deren Attribute auf Datenbankspalten abgebildet. Damit ist eine Klasseninstanz gleichzusetzen mit einem Datensatz aus einer Tabelle. Sehr kurz gefasst könnte man sagen, dass ein Model alle Operationen enthält, die direkten Datenbankzugriff erfordern und mit der Datenbank in enger Beziehung stehen. Dazu zählen beispielsweise die Beziehungen zwischen den Modellen und Validierungen von Daten.
Das war jetzt alles andere, als eine präzise Beschreibung. Widmen wir uns lieber gleich der Praxis, dann wird so manches klarer. Im Folgenden möchte ich zunächst die grundlegenden Schritte erläutern, die man beim Aufbau eines Models durchführen sollte.
Die Dateien der Models finden sich in /app/models/. Diese wurden automatisch mit den Migrationen erzeugt und sind genauso benannt wie die korrespondierenden Datenbanktabellen nur im Singular. Das heißt aktuell liegen folgende Dateien vor:
All diese Dateien sind zu Beginn leer, mal von der Definition der Klasse abgesehen, die alle von ActiveRecord::Base erben:
class BlogcategoriesBlogentry < ActiveRecord::Base end
Genauso wie in der Datenbank die Beziehungen zwischen den einzelnen Tabellen abgelegt werden, müssen diese Beziehungen auch auf die mit den Datenbanktabellen korrespondierenden Models übertragen werden. Die Abbildung dieser Beziehungen werden uns später vieles erleichtern, da verschiedenste vorgedachte Automatismen greifen können.
Diese Assoziation (auch Relation) bedeutet, dass ein Objekt einer Klasse A höchstens oder genau zu einem Objekt einer Klasse B zugeordnet ist, die Klasse B aber beliebig viele Objekte der Klasse A haben kann, die auf sie verweisen. In unserem Beispiel gehört ein Kommentar immer genau zu einem Blogeintrag. Ein Blogeintrag kann wiederum beliebig viele Kommentare haben.
In der Datenbank haben wir diese Struktur und die folgenden so weit wie möglich abgebildet. Jetzt bleibt die Frage, wie wir diese auch in der Anwendung selbst festmachen. Ziel dabei ist es, dass ActiveRecord die korrekte Verarbeitung der Beziehungen automatisiert übernehmen kann. Der sinnigste Platz dafür sind natürlich die Models.
Um eine 1:n Assoziation abzubilden, werden die beiden Methoden has_many und belongs_to verwendet. Dabei wird im Model, dessen Klasse eine andere Klasse referenziert belongs_to eingesetzt, im korrespondierenden Model auf der anderen Seite die Methode has_many.
Eigentlich ganz einfach. Im folgenden Beispiel aus der Anwendung, gehört ein (belongs to) Kommentar genau zu einem Blogeintrag. Daher sieht das Model von comments wie folgt aus:
class Comment < ActiveRecord::Base belongs_to :blogentry end
Ein Blogeintrag wiederum hat beliebig viele (has many) Kommentare. Das Model dazu sieht so aus:
class Blogentry < ActiveRecord::Base has_many :comments end
Ganz entscheidend ist hier der verwendete Numerus. Wie Ihr seht, heißt es blogentry im Singular, aber comments im Plural. Rails folgt hier im prinzip dem normalen Sprachgebrauch, denn ein Kommentar gehört ja zu EINEM Blogeintrag, also Singular. Ein Blogeintrag hat aber VIELE Kommentare, also Plural.
Das sind natürlich noch längst nicht die vollständigen Models, aber um die Assoziation abzubilden reicht das zunächst einmal.
Bei dieser Assoziation können einer Klasse A beliebig viele Objekte einer Klasse B zugeordnet werden und umgekehrt. Im Beispiel kann ein Blogeintrag in beliebig vielen Kategorien sein und eine Kategorie wiederum kann beliebig viele Blogbeiträge enthalten.
Wir haben bereits bei der Datenbank gesehen, dass bei einer n:m Assoziation immer eine weitere Tabelle zwischen den eigentlichen Tabellen steht, die quasi die Assoziationen enthält. In Rails gibt es (meines Wissens) zwei Möglichkeiten dies auf Ebene der Models abzubilden:
Verwendet man die Methode has_and_belongs_to_many, wird für die Assoziationstabelle kein Model benötigt und in den jeweils anderen Models wird diese Methode verwendet:
class Blogentry < ActiveRecord::Base has_and_belongs_to_many :blogcategories end class Blogcategory < ActiveRecord::Base has_and_belongs_to_many :blogentries end
Verwendet man diesen Weg, darf die Assoziationstabelle nur die beiden Fremdschlüssel enthalten, das ist aber in vielen Fällen nicht ausreichend (siehe auch Problem mit Assoziationstabellen und Löschweitergaben). Ganz glücklich bin ich mit diesem Weg nicht wirklich geworden.
Eine Lösung ist eine eingenes Model für die Assoziationstabelle. Jetzt verwenden wir in den Models Blogentry und Blogcategory wieder die Methode has_many und im Model BlogcategoriesBlogentry zweimal die Methode belongs_to, für jedes der anderen Models einmal:
class Blogentry < ActiveRecord::Base has_many :blogcategories, :through => :blogcategories_blogentries end class BlogcategoriesBlogentry < ActiveRecord::Base belongs_to :blogentry belongs_to :blogcategory end class Blogcategory < ActiveRecord::Base has_many :blogentries, :through => :blogcategories_blogentries end
Die Regeln für Singular und Plural sind die selben wie zuvor. Auffällig ist noch die Option :through. Diese ermöglicht es uns direkt von einem Blogeintrag alle Kategorien aufzurufen und umgekehrt, ohne einen Umweg über das BlogcategoriesBlogentry Model zu machen.
Ein Objekt einer Klasse A ist höchstens oder genau zu einem Objekt einer Klasse B zugeordnet, die Klasse B ist genau einem Objekt der Klasse A zugeordnet. Im Beispiel gibt es keine solche Assoziation und mir fällt dabei auch gerade keine gutes Beispiel ein. Ein Beispiel wäre: Ein Mensch hat genau einen deutschen Personalausweis oder nicht. Ein Personalausweis gehört nur genau einem Menschen (großartiges Beispiel ;-).
Bei einer 1:1 Assoziation verwendet man im Model dessen Klasse ein Objekt referenziert wieder die Methode belongs_to. In der referenzierten Klasse bekommt das Model die Methode has_one. Bei beiden Methoden wird der Singular des anderen Models verwendet.
vom 17.11.2009
Seit längerem musste ich heute mal wieder eine JavaScript Datei komprimieren, da die über 8000 Codezeilen stattliche 270 KB zur Folge hatten. Das ist nichts für eine Produktivumgebung. Da ich mit dem letzten Tool nicht wirklich zu frieden war, habe ich mich mal wieder umgeschaut und bin dabei auf den YUI Compressor von Yahoo! gestoßen. Dieser hat mich sehr überzeugt und ist auch recht einfach zu bedienen.
Der Kompressor ist in Java geschrieben und benötigt eine Version ab 1.4, die bei Sun Microsystems heruntergeladen werden kann. Zudem benötigt man natürlich noch den YUI Compressor selbst, der aktuell in Version 2.4.2 vorliegt. Nach der Installation von Java (gerne auch davor oder während), könnt Ihr den YUI Compressor in ein beliebiges Verzeichnis entpacken, worauf dieser direkt einsatzbereit ist.
Das für die Komprimierung relevante Verzeichnis ist der Ordner build. In diesem befindet sich eine .jar-Datei (yuicompressor-2.4.2.jar), die den Kern der Kompression durchführt.
Die zu komprimierende JavaScript Datei wird jetzt ebenfalls in dieses Verzeichnis kopiert. Anschließend muss wie so oft die Konsole bemüht werden, aber darin habe ich ja Dank Rails jetzt übung :D. In der Konsole navigiert Ihr zum build-Verzeichnis des YUI Compressors. Um eine JavaScript Datei quelljsdatei.js zu komprimieren gebt Ihr jetzt den folgenden Befehl ein. Dabei kann der Name der komprimierten Datei frei gewählt werden (hier zieljsdatei.js), nur am besten nicht gleich dem ursprünglichen Dateinamen.
java -jar yuicompressor-2.4.2.jar quelljsdatei -o zieljsdatei.js
So, das wars auch schon. Die Komprimierung erscheint recht gut zu sein und vor allem weiterhin korrekten JavaScript Code zu erzeugen.
vom 16.11.2009
Es hat mir ja doch keine Ruhe gelassen, dass ich über Migrationen mit der Standardfunktionalität von Rails keine Fremdschlüssel (foreign keys) der Datenbank hinzufügen kann. Aus diesem Grund habe ich noch etwas Recherche betrieben und bin zunächst auf ein Plugin gestoßen, was allerorts im Internet referenziert wird, aber wohl leider nicht mehr verfügbar ist. Schade! Dann habe ich auf einen Codeschnippsel aus 'Agile Web Development with Rails' von Sam Ruby, Dave Thomas und David Heinemeier Hansson, zurückgegriffen. Dieser funktioniert - nach einer kleinen Ergänzung - bisher einwandfrei.
Erstellt Euch zunächst im Verzeichnis lib/ eine .rb-Datei. Ich hab als Namen migration_helpers.rb gewählt. In diese fügt Ihr folgenden Code ein:
module MigrationHelpers
def foreign_key(from_table, from_column, to_table)
if !ActiveRecord::Base.configurations[RAILS_ENV]["adapter"].include? "sqlite"
constraint_name = "fk_#{from_table}_#{from_column}"
execute %{ALTER TABLE #{from_table} ADD CONSTRAINT #{constraint_name} FOREIGN KEY (#{from_column}) REFERENCES #{to_table}(id)}
end
end
end
Als erstes wird ein Modul MigrationHelpers mit einer Methode foreign_key erstellt. Die Methode hat als Parameter die Tabelle, die den Fremdschlüssel enthält, den Spaltennamen des Fremdschlüssels und die referenzierte Tabelle. Im Wesentlichen wird von der Methode ein einfaches SQL-Statement ausgeführt, mit dem die Fremdschlüsselbeziehung hergestellt wird. Zu beachten ist, dass immer auf die Spalte id in der Ursprungstabelle des Schlüssels referenziert wird, bei Einhaltung der Railskonventionen sinnvoll, ansonsten muss man hier noch einen Parameter übergeben.
Problematisch an diesem Statement ist natürlich, dass nicht jede Datenbank Fremdschlüssel unterstützt. Probleme treten beispielsweise mit SQLite auf. Daher habe ich eine - aktuell noch etwas unschöne - Lösung in Form einer Abfrage eingebaut, die überprüft, ob der zurzeit verwendete Adapter sqlite ist, beziehungsweise als Begriff enthält. Erst einmal zweckmäßig, aber das muss noch verbessert werden.
Die Verwendung dieser Methode gestaltet sich sehr einfach. Ich habe mir für die Fremdschlüssel eine eigene Migration set_foreign_keys angelegt:
ruby script/generate migration set_foreign_keys
Zunächst muss das eben geschriebene Modul über require "migration_helpers" eingebunden werden. Damit die Klasse SetForeignKeys auf die Methoden des MigrationHelpers Modul zugreifen kann, muss es dieses über extend MigrationHelpers erweitern.
In der self.up können jetzt die Fremdschlüssel für die verschiedenen Tabellen ergänzt werden, in dem man jeweils die foreign_key Methode aufruft:
require "migration_helpers" class SetForeignKeys < ActiveRecord::Migration extend MigrationHelpers def self.up foreign_key(:blogentries, :user_id, :users) foreign_key(:blogcategories_blogentries, :bogentry_id, :blogentries) foreign_key(:blogcategories_blogentries, :category_id, :blogcategories) foreign_key(:blogentries_tags, :bogentry_id, :blogentries) foreign_key(:blogentries_tags, :tag_id, :tags) foreign_key(:comments, :blogentry_id, :blogentries) foreign_key(:medias, :user_id, :users) end def self.down end end
Für self.down muss ich noch mal eine Methode ergänzen, die die Fremdschlüsselbeziehungen wieder aufhebt...
Wenn man das Ganze auf mehrere Migrationen aufteilen möchte, kann es unter Umständen hilfreich sein, das Einbinden des Moduls in die environments.rb zu verlegen, um das nicht in jeder Migration erneut machen zu müssen.
vom 15.11.2009
In letzter Zeit war viel los und ich musste beruflich auch mal wieder recht viel entwickeln, daher war meine Motivation für dieses Projekt nicht so groß. Ich hoffe aber, dass es jetzt mit neuem Schwung weitergehen kann.
Ich habe ja vor einiger Zeit schon von Git berichtet und wie man einen SSH Schlüssel mit msysGit erzeugen kann. Gerade wieder musste ich feststellen, dass ein paar ganz kleine Änderungen einem die Arbeit doch um einiges leichter machen können.
Zunächst einmal ist es nervig, wenn die Bash von Git immer im Homeverzeichnis startet, da man dann jedes Mal erst das richtige, gegebenenfalls tiefer eingegliederte, Verzeichnis ansteuern muss. Das lässt sich sehr einfach über die Eigenschaften der Verknüpfung zu Git Bash ändern. Hier einfach auf den Reiter 'Verknüpfungen' und bei Ausführen in das jeweilige Verzeichnis angeben (zum Beispiel 'C:\Web\Rails\oliverliebschde').
Ebenso einfach und auch sehr sehr hilfreich, wenn nicht unabdingbar, in Projekten mit mehreren Beteiligten, ist das Einstellen eines konkreten Benutzers. So werden bei jeder Transaktion mit dem Server entsprechende Daten übermittelt, die den jeweils anderen mitteilen, wer hier tätig gewesen ist:
git config --global user.name "Vorname Nachname" git config --global user.email "deine@emailadresse.de"
Es gibt natürlich noch viel mehr Einstellungsmöglichkeiten (siehe zum Beispiel http://progit.org/book/ch1-5.html), aber allein diese beiden sind viel wert.
vom 2.11.2009
Es hätte eigentlich so einfach sein können/sollen, aber leider hat da jemand wohl noch nicht ganz zu Ende gedacht, als es darum ging Löschweitergaben (dependent destroy) in Bezug auf Assoziationstabellen (join tables) zu implementieren.
In der Regel benötigt eine Assoziationstabelle, hier zum Beispiel blogcategories_blogentries, kein id Feld als Primärschlüssel, sondern es reicht ein zusammengesetzter Primärschlüssel aus bogentry_id und category_id.
In Rails gibt es ein ansich nettes Konstrukt: Löschweitergaben. Diese sorgen dafür, sofern im Model definiert, dass bei der Löschung von Objekten, beispielsweise einer Kategorie, alle abhängigen Daten, hier in der Assoziationstabelle, automatisch mit gelöscht werden. Leider funktioniert das wohl nicht einwandfrei ohne eine explizite id Spalte. Schade! Es tritt folgender Fehler auf:
ActiveRecord::StatementInvalid in CategoriesController#destroy Mysql::Error: Unknown column 'id' in 'where clause': DELETE FROM `blogcategories_blogentries` WHERE `id` = NULL
Aus diesem Grund werde ich alle Assoziationstabellen doch wieder um eine eigene id als Primärschlüssel ergänzen. Das bedeutet im Beispiel,
create_table :blogcategories_blogentries, :id => false do |t|
wird zu
create_table :blogcategories_blogentries do |t|
Zwar kommen die Models erst in einem der nächsten Beiträge, aber für alle Neugierigen, so sehen die relevanten Teile des Models für Löschweitergaben aus:
class Blogentry < ActiveRecord::Base has_many :blogcategories_blogentries, :dependent => :destroy has_many :blogcategories , :through => :blogcategories_blogentries end
class Blogcategory < ActiveRecord::Base has_many :blogcategories_blogentries, :dependent => :destroy has_many :blogentries , :through => :blogcategories_blogentries end
class BlogcategoriesBlogentry < ActiveRecord::Base belongs_to :blogcategory belongs_to :blogentry end
PS: has_and_belongs_to_many ist wohl auch keine Lösung?!
vom 1.11.2009
Heute war viel los und daher habe ich nicht viel mehr geschafft, als die Migrationsskripte fertig zu stellen - hoffentlich fertig :-). Wer möchte werfe einen Blick darauf. Dargestellt ist immer nur der Inhalt der up Action.
create_table :blogentries do |t| t.integer :user_id, :null => false t.string :title, :limit => 45, :null => false t.text :teaser, :null => false t.text :text t.string :permalink, :limit => 80, :null => false t.integer :status, :null => false, :default => 0 t.integer :comments_count, :null => false, :default => 0 t.datetime :released_at t.timestamps end add_index :blogentries, :user_id, :name => 'FK: blogentries_user_id' add_index :blogentries, :title, :name => 'I: blogentries_title' add_index :blogentries, :permalink, :name => 'U: blogentries_permalink', :unique => true add_index :blogentries, :released_at, :name => 'I: blogentries_released_at'
create_table :blogcategories do |t| t.string :name, :limit => 45, :null => false t.string :permalink, :limit => 80, :null => false t.integer :blogcategories_blogentries_count, :null => false, :default => 0 t.timestamps end add_index :blogcategories, :name, :name => 'I: blogcategories_name' add_index :blogcategories, :permalink, :name => 'U: blogcategories_permalink', :unique => true
create_table :blogcategories_blogentries, :id => false do |t| t.integer :bogentry_id, :null => false t.integer :category_id, :null => false t.timestamps end add_index :blogcategories_blogentries, [:bogentry_id, :category_id], :name => 'PK/U: blogcategories_blogentries', :unique => true add_index :blogcategories_blogentries, :category_id, :name => 'I: blogcategories_blogentries_category_id', :unique => false
create_table :tags do |t| t.string :tag, :limit => 45, :null => false t.string :permalink, :limit => 80, :null => false t.integer :blogentries_tags_count, :null => false, :default => 0 t.timestamps end add_index :tags, :tag, :name => 'I: tags_tag', :unique => true
create_table :blogentries_tags, :id => false do |t| t.integer :bogentry_id, :null => false t.integer :tag_id, :null => false t.timestamps end add_index :blogentries_tags, [:bogentry_id, :tag_id], :name => 'PK/U: blogcategories_blogentries', :unique => true add_index :blogentries_tags, :bogentry_id, :name => 'I: blogentries_tags_bogentry_id', :unique => false add_index :blogentries_tags, :tag_id, :name => 'I: blogentries_tags_tag_id', :unique => false
create_table :comments do |t| t.integer :blogentry_id, :null => false t.string :name, :limit => 45, :null => false t.text :email, :limit => 45, :null => false t.text :website, :limit => 80 t.text :text t.integer :status, :null => false, :default => 1 t.timestamps end add_index :comments, :blogentry_id, :name => 'FK: comments_blogentry_id' add_index :comments, :created_at, :name => 'I: comments_created_at'
create_table :users do |t| t.string :firstname, :limit => 45, :null => false t.string :lastname, :limit => 45, :null => false t.string :permalink, :limit => 80, :null => false t.string :login, :limit => 45, :null => false t.string :email, :limit => 45, :null => false t.string :crypted_password, :limit => 255, :null => false t.string :password_salt, :limit => 255, :null => false t.string :persistence_token, :limit => 255, :null => false t.string :single_access_token, :limit => 255, :null => false t.string :perishable_token, :limit => 255, :null => false t.integer :login_count, :null => false, :default => 0 t.integer :failed_login_count, :null => false, :default => 0 t.datetime :last_request_at t.datetime :current_login_at t.datetime :last_login_at t.string :current_login_ip, :limit => 45 t.string :last_login_ip, :limit => 45 t.timestamps end add_index :users, :login, :name => 'U: users_login', :unique => true add_index :users, :email, :name => 'U: users_email', :unique => true add_index :users, :permalink, :name => 'U: users_permalink', :unique => true
create_table :medias do |t| t.integer :user_id, :null => false t.string :name, :limit => 45, :null => false t.string :description t.string :filename, :null => false t.string :filetype, :null => false t.integer :filesize, :null => false t.integer :width, :limit => 4, :null => false t.integer :height, :limit => 4, :null => false t.timestamps end add_index :medias, :user_id, :name => 'FK: medias_user_id' add_index :medias, :name, :name => 'U: medias_name', :unique => true add_index :medias, :created_at, :name => 'I: medias_created_at'
vom 31.10.2009
Wie bereits angeklungen, verwendet Rails sogenannte Migrationsskripte, um alle Datenbankänderungen durchzuführen. Diese werden in /db/migrate/ abgelegt. Über ein Migrationsskript kann eine Tabelle zum Beispiel erstellt, gelöscht oder geändert werden. Grundsätzlich kann man solche Migrationen natürlich per Hand anlegen, aber das geht auch einfacher mit dem Model Generator und über die Konsole mit dem Befehl
ruby script/generate model Modelname
Wichtig sind die Konventionen für die Benennung der Models, die damit erzeugt werden: Bei 'normalen' Tabellen ist der Modelname (Klassenname des Models) gleich dem Tabellennamen im Singular mit großem Anfangsbuchstaben. Über das Skript wird daraus dann automatisch ein Tabellenname im Plural mit kleinem Anfangsbuchstaben. Zum Beispiel User als Modelname, hat die Tabelle users.
Bei Assoziationen werden die Namen der zu verknüpfenden Tabellen in alphabetischer Reihenfolge angegeben. Nur der letzte Name ist im Singular alle anderen im Plural. Jeder einzelne Tabellennamen wird mit einem Großbuchstaben begonnen, zum Beispiel UsersGroup. Daraus wird dann users_groups.
Praktisch sieht das dann für meine Tabellen so aus:
Mit jedem dieser Befehle werden drei Dateien erzeugt, zum Beispiel: /app/models/blogentry.rb,/test/unit/blogentry_test.rb, /db/migrate/20091031162701_create_blogentries.rb.
Die blogentry.rb ist die zur Tabelle gehörende ActiveRecord-Klasse (Model). Models sind für die Datenbankoperationen zuständig. In blogentry_test.rb können später Unit-Tests erstellt werden und die 20091031162701_create_blogentries.rb enthält alle Informationen, um die Tabellenstruktur aufzubauen.
Hat man eine Migration erst einmal durchgeführt, sollte man diese nicht mehr abändern (es gibt abhängig von der Verwaltung der Migrationen noch Ausnahmen). Sollten dennoch Änderungen an einer Tabelle von Nöten sein, legt man eine neue Migration an.
ruby script/generate migration namedermigration
Dabei wird nicht das Model mit erzeugt, sondern nur ein neues Migrationsskript angelegt, welches die Änderungen durchführt. Der Name sollte möglichst sprechend sein, zum Beispiel add_subtitle_column. Änderungen werden in einem zukünftigen Beitrag genauer bleuchtet.
Aktuell sieht eine Migration in etwa wie folgt aus:
class CreateBlogentries > ActiveRecord::Migration
def self.up
create_table :blogentries do |t|
t.timestamps
end
end
def self.down
drop_table :blogentries
end
end
die erste Methode (Action) def self.up definiert, was bei der Erstellung der jeweiligen Tabelle alles angelegt werden soll. Die Action def self.down droppt die Tabelle einfach wieder.
Innerhalb der up Action wird die Struktur der Tabelle innerhalb des Blocks create_table definiert. Diesem können noch weitere Parameter mitgegeben werden:
| Parameter | Beschreibung |
|---|---|
| :force | Sorgt dafür, dass eine vorhandene Tabelle mit gleichem Namen beim Ausführen der Migration zunächst gelöscht wird |
| :temporary | Legt eine temporäre Tabelle an, die beim Datenbankverbindungsabbau wieder gelöscht wird |
| :options | Bietet die Möglichkeit datenbankspezifische Befehle zu integrieren, im Sinne der Datenbankunabhängigkeit ist hier Vorsicht geboten |
Würden wir an dieser Stelle rake db:migrate (siehe unten) ausführen, würden nur leere Tabellen angelegt. Das liegt daran, dass ja noch gar keine Informationen darüber vorliegen, wie die Struktur der Tabellen aussieht. Grundsätzlich könnte man das dem Befehl script/generate model schon mitgeben, aber das ist meines Erachtens nach nur wenig sinnvoll. Viel besser ist es, sich jetzt die einzelnen Migrationsskripte in /db/migrate/ vorzunehmen und die Struktur zu ergänzen. Handarbeit ist hier nämlich in jedem Fall erforderlich.
Aktuell werden bei der Erstellung der Tabelle nur eine Spalte id als Primärschlüssel (diese wird immer angelegt, es sei denn man unterbindet es explizit, siehe unten) und zwei Spalten, created_at und updated_at, über die magische Spalte t.timestamps hinzugefügt. Das sollte man durchaus im Kopf behalten und kann sehr praktisch sein, diese Felder werden nämlich auch automatisch gefüllt - wenn ein Datensatz erstellt oder aktualisiert wird.
Wenn wir uns das Datenbankschema angucken, reichen diese Spalten natürlich nicht aus. Wir benötigen weiterhin user_id, title, teaser, text, permalink, status, comments_count, released_at. Die fertige Action sieht dann wie folgt aus.
def self.up create_table :blogentries do |t| t.integer :user_id, :null => false t.string :title, :limit => 45, :null => false t.text :teaser, :null => false t.text :text t.string :permalink, :limit => 80, :null => false t.integer :status, :null => false, :default => 0 t.integer :comments_count, :null => false, :default => 0 t.datetime :released_at t.timestamps end add_index :blogentries, :user_id, :name => 'FK: blogentries_user_id' add_index :blogentries, :title, :name => 'I: blogentries_title' add_index :blogentries, :permalink, :name => 'U: blogentries_permalink', :unique => true add_index :blogentries, :released_at, :name => 'I: blogentries_released_at' end
Jeder der sich mit Datenbanken nur etwas auskennt, wird sicher keine Schwierigkeiten mit der Interpretation haben: Der erste Wert hinter dem t ist der Datentyp der Spalte, als nächstes kommt der Spaltenname mit einem führenden Doppelpunkt. Anschließend folgen weitere Eigenschaften.
In einigen Belangen muss man bei der Definition der Datenbankstruktur Abstriche machen. Dies ist der Datenbankunabhängigkeit geschuldet - natürlich kann man diese auch ignorieren, aber davon will ich hier mal absehen.
| Datentyp | Beschreibung |
|---|---|
| :binary | Binärdaten |
| :boolean | Boolsche Werte, also wahr (true) oder falsch (false) |
| :date | Ein Datum |
| :datetime | Datum sowie Zeit |
| :decimal | Dezimalzahlen mit festgelegten Nachkommastellen |
| :float | Fließkommazahlen |
| :integer | Ganze Zahlen (positiv und negativ) |
| :string | Zeichenkette (maximal 255 Zeichen) |
| :text | Längere Zeichenkette |
| :time | Die Zeit (ohne Datum) |
| :timestamp | Zeitstempel der Erstellung oder Aktualisierung eines Datensatzes |
Spaltennamen werden in Rails vollständig klein geschrieben und dürfen natürlich keine Sonderzeichen oder ähnliches enthalten, mit einer Zahl oder einem Unterstrich beginnen. Bei zusammengesetzten Begriffen werden die einzelnen Wörter durch Unterstriche getrennt. Fremdschlüssel werden nach der Konvention 'Tabellenname im Singular'+'_id' bekannt.
| Eigenschaft | Beschreibung |
|---|---|
| :default | Standardwert, der vergeben wird, wenn kein Wert explizit gesetzt wird (nil entspricht NULL) |
| :limit | Beschränkt die maximale Länge der Inhalte, bei string und text Zeichen, bei binary und integer Bytes |
| :null | Boolscher Wert, ob der Wert der Spalte NULL sein darf |
| :options | Datenbankabhängige Befehle |
| :precision | Anzahl aller Stellen bei Dezimalzahlen |
| :scale | Anzahl der Nachkommastellen bei Dezimalzahlen |
Wie bereits erwähnt, wird mit jeder Modelmigration automatisch eine Spalte id vom Typ integer als Primärschlüssel erzeugt. Das sollte man in der Regel auch genauso belassen. Einzig sinnvolle Ausnahme, die mir bisher untergekommen ist, sind Assoziationstabellen, also Tabellen, die eine n:m-Beziehung zwischen zwei anderen Tabellen herstellen. Hier ist es sinniger die beiden Fremdschlüssel als zusammengesetzten Primärschlüssel zu verwenden. Um zu verhindern, dass bei solchen Tabellen die Spalte id mit erzeugt wird, ergänzt Ihr die create_table um :id => false:
create_table :blogentries_categories, :id => false do |t|
Die Option :primary_key bietet die Möglichkeit, den Namen des Primärschlüssels auf einen beliebigen Namen zu ändern. Das ist aber nicht wirklich sinnvoll.
Um einen Index auf eine oder mehrere Spalten zu legen, definiert Ihr diese nach dem create_table Block wie folgt, dabei ist die Angabe eines Namens optional:
add_index :blogentries, :title, :name => 'I: blogentries_title'
Bei einem Unique Index, wird die Definition zusätzlich nur noch um :unique => true ergänzt. Hier sehen wir auch, wie ein zusammengesetzter Index erzeugt werden kann:
add_index :blogentries_categories, [:bogentry_id, :category_id], :name => 'PK/U: blogentries_categories', :unique => true
Da längst nicht alle Datenbanken das Konzept von Fremdschlüsseln beherrschen, sieht Rails auch keine eigene Möglichkeit zur Definition von Fremdschlüsseln in Migrationen vor. In jedem Fall sollte man einen Fremdschlüssel schon mal mit einem normalen Index belegen. Zusätzlich kann/ sollte man dann noch über den Parameter :option für alle Datenbanken, die Fremdschlüssel beherrschen, entsprechenden Code vorhalten:
t.integer :user_id, :null => false, :options => "CONSTRAINT fk_blogentries_users REFERENCES users(id)"
Dieses Vorgehen ist natürlich schon wieder recht aufwendig und ich bin mir bezüglich des Verhaltens bei Datenbanken, die das nicht unterstützen gerade nicht ganz sicher. Es gibt auch eine ganze Reihe von Plugins, die diese Funktionalität auf einen einfachen Methodenaufruf reduzieren, aber auch hier muss ich erst mal genauer schaun, was diese so taugen. Bis dahin werde ich auf Fremdschlüssel in Migrationsskripten erst einmal verzichten.
Hat man seine Migrationsskripte soweit erstellt, dass man die Datenbank füllen möchte, kommt der Befehl rake db:migrate zum Einsatz. Alle erfolgreich durchgeführten Migrationen werden in der Datenbank in der Tabelle schema_migrations mit ihrer Versionsnummer abgelegt. rake db:migrate führt immer alle Migrationen aus, die noch nicht in dieser Tabelle verzeichnet sind.
Manchmal ist es erforderlich, alle Migrationen ab einer bestimmten Version durchzuführen, also zum Teil vielleicht auch noch einmal durchzuführen. Dieses ist mit dem Befehl rake db:migrate VERSION=versionsnummer möglich.
Möchte man Migrationen wieder zurück nehmen, bietet der Befehl rake db:migrate:rollback STEP=anzahl dazu die Möglichkeit. STEP definiert dabei die Anzahl der zurückzunehmenden Migrationen. Wird STEP nicht angegeben wird nur die letzte Migration zurückgenommen.
Sollen die Migrationen einerseits rückgängig andererseits danach wiederholt werden, kann man das mit rake db:migrate:redo STEP=anzahl erledigen. Möchte man das für alle Migrationen ausführen, dann ist rake db:migrate:reset der Befehl Euer Wahl.
vom 31.10.2009
Jetzt wo wir die erste Anwendung erzeugt haben, wird es Zeit diese mit Leben zu füllen. Initial spielen dabei die vier Verzeichnisse app, config, db und public eine Rolle. Widmen wir uns erst einmal dem public-Verzeichnis. Hier müssen wir zunächst nur etwas aufräumen. Alle Standardbilddateien in images werden gelöscht, ebenso wie alle JavaScript Dateien bis auf die application.js in javascripts (ich arbeite später mit jQuery und nicht prototype). Weiterhin lösche ich die index.html, die direkt in public liegt. Wenn Ihr jetzt die Seite aufruft, bekommt Ihr eine Fehlermeldung ("We're sorry, but something went wrong. We've been notified about this issue and we'll take a look at it shortly."), aber keine Panik, dass beheben wir bald.
Jetzt ist es an der Zeit ein paar Basiskonfigurationseinstellungen vorzunehmen. Daher schauen wir uns zunächst die datei /config/database.yml genauer an. Diese, so lässt der Name schon vermuten, enthält Informationen über die Datenbankverbindungen. Für jede Umgebung (hier: development, test und production), kann hier eine eigene Datenbankverbindung eingerichtet werden. Dabei sind folgende Einstellungen für mich relevant:
Es gibt noch ein paar weitere Optionen, aber die sind für mich gerade nicht von Belang. Die database.yml sieht jetzt also wie folgt aus:
development: adapter: mysql encoding: utf8 database: oliverliebschde_dev username: Datenbankbenutzername password: Datenbankpasswort host: localhost test: ... production: ...
Zwar lassen sich die meisten Datenbankoperationen auch mit Rails erledigen, allerdings kann es nicht schaden, auch einen direkten Zugriff auf die Datenbank zu haben. Zu diesem Zweck sind die MySQL GUI Tools sehr hilfreich. Auch die MySQL Workbench ist ein sehr nettes Tool.
Auf Basis der soeben gemachten Einstellungen können wir jetzt ganz leicht über die Konsole eine korrespondierende Datenbank erzeugen. Dabei unterstützt uns der folgende Befehl (bei allen Befehlen müsst Ihr Euch im Verzeichnis Eurer Anwendung befinden!):
rake db:create RAILS_ENV='development'
Dieser erzeug für die Umgebung development eine Datenbank, mit den Einstellungen aus der database.yml und dem Abschnitt development. Wenn Ihr alles richtig konfiguriert habt und der MySQL-Server gestartet ist, sollte alles glatt gehen. Um die Datenbank zu testen, könnt Ihr
rake db:migrate
ausführen, der im Erfolgsfall lediglich eine Zeile (in C:/Web/Rails/oliverliebschde) zurückliefern sollte. Der Befehlt führt alle Migrationsskripte im Verzeichnis /db/migrate aus, die ihrerseits eine Änderung der Datenbank bewirken.
Da wir noch keine Migrationen erstellt haben, wird nur eine Tabelle schema_migrations und eine Datei /db/schema.rb erzeugt.
Jetzt sind wir bereit, um die benötigten Tabellen für die Anwendung unserer Datenbank hinzuzufügen.
Ganz so einfach war es mal wieder nicht. Aber zum Glück ließ sich das Problem schnell beheben. Wenn Ihr also auch einen ähnlichen Fehler bekommt, dann hängt das vermutlich damit zusammen, dass Eure MySQL 5.1 Client Bibliothek nicht so gut mit Rails zusammenspielt. Um Abhilfe zu schaffen, könnt Ihr Euch eine ältere Bibliothek runterladen (zum Beispiel von instantrails.rubyforge.org (libmySQL.dll)). Diese müsst Ihr dann in das bin-Verzeichnis Eurer Ruby Installation kopieren. Anschließend sollte der rake-Befehl einwandfrei durchlaufen, ggf. noch den MySQL Server neu starten.
vom 30.10.2009
Zwar konnte ich in den vergangen Tagen mangels Zeit nicht viel am Projekt hier weiterarbeiten, aber dennoch geht es voran. Martin hat mir nämlich Platz auf einem seiner Server eingeräumt uns so kann ich bald auch online mit Rails loslegen. Die Daten müssen dann natürlich auch vom lokalen Rechner auf den Server und für diesen Zweck werde ich/wir Git einsetzen, da ich damit gute Erfahrungen gemacht habe. Die Macher selbst beschreiben das Projekt so:
Git is a free & open source, distributed version control system designed to handle everything from small to very large projects with speed and efficiency.Da ich unter Windows arbeite, kommt am ehesten msysGit in Frage, um damit zu arbeiten. Hat man sich das Ganze installiert, sollte man sich zunächst einen privaten und einen öffentlichen Schlüssel erzeugen, da diese für den Austausch mit dem Server von Nöten sind. Das ist auch recht einfach. Mit der Installation von msysGit werden auch zwei Programme installiert: Die grafische Benutzeroberfläche Git GUI und die Kommandozeile Git Bash. Da ich die GUI irgendwie nicht mag, arbeite ich auf der Bash. Um mit dieser jetzt ein Schlüsselpaar zu erzeugen geht man folgendermaßen vor:
ssh-keygen -t rsa -C "deine-email@adresse.de" ausführen
Das war's auch schon. Auf diese Weise wurden jetzt im angegebenen Verzeichnis (unter Windows 7 zum Beispiel /c/Users/Benutzername/.ssh/) zwei Dateien erzeugt:
Der öffentliche Schlüssel sieht in etwa wie folgt aus: ssh-rsa größere Zahlen- und Buchstabenkolonne ==deine-email@adresse.de. Diesen und Euer Passwort benötigt Ihr für die Authentifizierung am Server. Dazu später mehr.
vom 25.10.2009
Ich habe mir sagen lassen, dass es unter anderem aus Performance Gründen sinnvoll sei, den mit Ruby ausgelieferten WEBrick Server durch einen Mongrel Server zu ersetzen. Das was ich dazu im Internet gefunden habe, lässt vermuten, dass da was dran ist. Was also muss man machen, um Mongrel zu installieren?
Mongrel lässt sich wieder über ein Gem installieren, dazu müsst Ihr nur den Befehl gem install mongrel in die Konsole eingeben. Das wars auch schon.
Um den Server zu verwenden gibt es theoretisch zwei spezifische Befehle: mongrel_rails start, um den Server zu starten und mongrel_rails stop, um den Server zu stoppen (jeweils im Verzeichnis Eurer Anwendung ausgeführt). Allerdings zeigt die Konsole dann bei mir keine Aktivität des Servers mehr an, das ist zum Beispiel für das Debugging nicht so super. Vielleicht mache ich ja was falsch.
Als Alternative könnt Ihr den Server aber auch einfach wie gewohnt mit ruby script/server starten. Dabei fällt auf, dass nicht wie zuvor WEBrick, sondern jetzt Mongrel gebootet wird (=> Booting Mongrel) - perfekt.
Hat für mich nicht so viel Sinn gemacht, aber der Vollständigkeit halber sei erwähnt, dass man den Server auch als Windows Dienst nutzen kann. Dafür müsst Ihr Mongrel Win32 wie folgt installieren:
gem install mongrel_service, dadurch bekommt Ihr die Möglichkeit Befehle für einen Dienst an mongrel_rails anzughängen (-h für Hilfe).mongrel_rails service::install -N myapp \-c c:\my\path\to\myapp -p 4000 -e environmentname (z. B. production, development) ausführen. 4000 ist der Port (man muss sich im Verzeichnis der Anwendung befinden).net start myapp kann der Service gestartet über net stop myapp gestoppt werden. Der Ruby Server wird damit automatisch für die entsprechende App gestartet und ist danach über den Port 4000 erreichbar.-N verwendet, um der App einen entsprechend anderen Namen zuzuweisen, zum Beispiel myapp_dev.mongrel_rails service::remove -N myapp löscht den Service aus der ListeEin Problem trat bei mir anschließend auf: Der Mongrel Server ließ sich nicht mehr eindwandfrei über Ctrl-C beenden, es passierte einfach nichts. So schien es zumindest. Interessanterweise habe ich nach zigfachem rumprobieren herausgefunden, dass der Befehl anscheinend erst beim Server ankommt, wenn man die Seite im Browser neu aufruft.
Nachdem ich zum Beispiel den Server über Ctrl-C gestoppt und anschließend http://localhost:3000 erneut aufgerufen habe - was dann natürlich in einem 101 Fehler endete - kam der Befehl auch in der Konsole an. Komisch.
Die bisherige Umgebung ist sicher absolut ausreichend für kleine, private Anwendungen, bietet aber noch viel Optimierungspotenzial. Beispielsweise wäre es sinnvoll, alle statischen Dokumente direkt vom Webserver, zum Beispiel Apache, zu erhalten und nur nicht statische Anfragen über den Mongrel. Insgesamt habe ich mich mit der Thematik aber noch nicht umfassend beschäftigt und kann daher jetzt noch keine Tipps dazu geben.
Es schadet übrigens nicht, wenn man seine Umgebung regelmäßig auf den neusten Stand bringt, auch wenn gerade keine Probleme existieren. Ein Update von RubyGems über update_rubygems im bin-Verzeichnis von Ruby (notwendig?) und anschließend ein Update von Gem über gem update, ist schon mal ein guter Anfang. Sonst noch was?
vom 25.10.2009
Ein paar Worte vorweg. Ruby on Rails ist ein Framework das nach der Model View Controller (MVC) Architektur gestaltet ist. Bei einer derartigen Architektur, wird die Anwendung grob in drei Schichten unterteilt, die die Verantwortlichkeiten bei der Ausführung strikt trennen. Um jetzt nicht völlig vom Thema abzukommen, überlasse ich die genau Erklärung einer MVC-Architektur mal anderen, oder komme später noch einmal darauf zurück.
Verinnerlichen sollte man sich allerdings das folgende Bild, welches den Ablauf einer Rails Anwendung gut beschreibt. Diese ist entstanden in Anlehnung an 'Agile Web Development with Rails' von Sam Ruby, Dave Thomas und David Heinemeier Hansson, Figure 2.2:
Das Modul ActiveRecord hat die Kommunikation zwischen dem objektorientierten Klassen und der relationalen Datenbank zur Hauptaufgabe. Pragmatisch formuliert: Es sorgt dafür, dass wir Daten aus der Datenbank auslesen und in unserer Anwendung verwenden können und, dass wir umgekehrt Daten aus unserer Anwendung in die Datenbank schreiben können. Hierbei gibt es natürlich viele Aufgaben, aber alles zu seiner Zeit.
Views dienen der Ausgabe von Daten, die sie von den Controllern bekommen. Dabei kann die Ausgabe in unterschiedlichsten Formaten stattfinden. Logik wird hier in der Regel überhaupt nicht verwendet. Im Standardfall ist eine View ein HTML-Template, wo an den jeweiligen Stellen Ruby Code eingebunden wird, um Daten des Controllers auszulesen.
Die Daten die in der View dargestellt werden, kommen letztendlich aus der Datenbank und müssen daher über das Model laufen. Die Kommunikation zwischen Model und View übernehmen dabei die Controller. Ein Großteil der Anwendungslogik wird hier implementiert.
| Verzeichnis | Beschreibung |
|---|---|
| app | app steht für application. Dieses Verzeichnis beinhaltet einen Großteil Eures Codes in Form der Models, der Controller, der Views und deren Helpern. |
| app/controllers | Enthält Eure Controller-Klassen. Format: controllername_controller.rb |
| app/helpers | Hilfsklassen für die Views. Format: helpername_helper.rb |
| app/models | Enthält Eure Model-Klassen. Format: controllername.rb |
| app/views | Hier finden sich die passenden Views zu jedem Controller. Dabei hat jeder Controller ein separates Verzeichnis, in dem seine Views liegen. Die Views selber korrespondieren mit den Methoden der Controller-Klasse, aber dazu später mehr. Format: actionname.html.erb |
| config | Enthält die wichtigsten Konfigurationsdateien für Eure Rails-Anwendung, unter anderem für die Datenbankverbindungen. |
| db | Hier werden die Schema- und Migration-Dateien abgelegt. |
| doc | Enthält die autmatisch erzeugte Dokumentation. |
| lib | Hier liegen Klassen, die von mehreren Controllern, Models oder Views geteilt werden. |
| log | Enthält die Log-Dateien der Anwendung |
| public | Dieses Verzeichnis ist das einzig öffentlich zugängliche Eurer Anwendung. Also quasi das, auf welches Eure Domain zeigt. |
| public/images | Die von Eurer Anwendung genutzten Bilder |
| public/javascripts | ... und JavaScripts Dateien |
| public/stylesheets | ... und CSS-Dateien. |
| script | Enthält Skripte, die Ihr, meist mit der Konsole, aufrufen könnt. Diese erleichtern Euch die Arbeit ungemein. |
| test | Alles was mit dem Testen der Anwendung zu tun hat, zumindest fast alles, kommt hier rein. |
| tmp | Verzeichnis für temporäre Anwendungsdaten. |
| vendor | Enthält zum Beispiel Code von Plug-ins. |
vom 25.10.2009
Jetzt wo alles so schön läuft, wird es Zeit mit der ersten Anwendung zu starten. Für die habe ich zuvor ja genug Vorbereitungen getroffen, so dass sich jetzt direkt die Frage stellt, wie das mit Ruby on Rails funktioniert.
Zunächst einmal erstelle ich mir ein eigenes Verzeichnis unter 'c:\Web' mit dem Namen Rails, in dem ich alle meine Anwendungen ablegen werde. Anschließend wechsele ich in der Konsole in dieses Verzeichnis (man merkt schon, ohne die Konsole geht nicht viel in Rails). Um eine Anwendung zu beginnen und von Rails automatisch die benötigten Verzeichnisse und Dateien zu erzeugen verwendet man folgenden Befehl:
rails meinprojekt
Das Tolle ist, dass kann man jetzt sogar direkt testen:
cd meinprojekt ruby script\server
Der zweite Befehl startet den in Rails integrierten Webserver WEBrick auf dem Port 3000. Über die Ergänzung von -p 3001 kann auch ein anderer Port verwendet werden. Das Projekt kann nun im Browser unter http://localhost:3000/ oder http://127.0.0.1:3000/ aufgerufen werden. Der Server wird in der Konsole über Strg+c beendet.
Sollte an dieser Stelle ein Meldung zu fehlenden Gems kommen, könnt Ihr diese wie gehabt über gem update --system -y nachinstallieren.
Ist alles nach Plan gelaufen, dann werdet Ihr folgende Seite in Eurem Browser sehen:
Woohoo!
Bevor es jetzt in den Code geht, sollte man sich mit der von Rails automatisch erzeugten Struktur vertraut machen. Dazu mehr im nächsten Beitrag. Jetzt brauche ich eine Pause.
vom 25.10.2009
Nachdem ich mich in den vergangenen Tagen erst einmal unserem Hausnetzwerk gewidmet und zudem zwei Rechner neu aufgesetzt habe, ist heute ein guter Zeitpunkt das Thema Ruby on Rails, im Speziellen, die lokale Installation auf einem Rechner, zu besprechen. Da mein Desktop-Rechner zu Hause eh ganz neu aufgesetzt wurde, kann ich den Weg jetzt noch einmal live nachvollziehen und so sicherstellen, dass mir nichts entgeht.
Da ich hier auf einem Rechner mit Windows 7 arbeite, gelten die folgenden Ausführungen auch nur für eine solche Umgebung - andere Windows Betriebssysteme werden sich sicherlich nur marginal davon unterscheiden, aber für Mac, Linux und Co. kann das ganz anders aussehen.
Folgendes werden wir benötigen:
Na dann, auf geht's!
Die einfachste Art Ruby zu installieren, ist der One-Click Windows Installer. Dieser liegt zurzeit in der Version 1.8.6-26 als Final Release vor. Andere Versionen wie die 1.8.6-27 als Release Candidate 2, die 1.8.7 als Quellcode zum selbst kompilieren oder die 1.9.1 in der Technology Preview2 will ich hier nicht verwenden. Sollen die die Dinger doch erst mal ordentlich fertig machen, dann kann man die auch einsetzen.
Mit der 1.8.6-26 kann man auf jeden Fall gut arbeiten und gerade als Einsteiger werden die Unterschiede wohl nicht so ins Gewicht fallen - korrigiert mich, wenn ich falsch liege.
Die Datei findet Ihr auf rubyforge.org.
Während der Installation werdet Ihr aufgefordert die zu installierenden Komponenten auszuwählen. Ich werde den SciTE Editor nicht verwenden - das bleibt Euch aber selbst überlassen. Nebenbei bemerkt: Ich verwende unter Windows den e-Texteditor, ein Pendant zu TextMate auf dem Mac.
RubyGems hingegen solltet Ihr in jedem Fall mit installieren (also: Haken setzen).
Als Installationsverzeichnis wähle ich 'c:\Web\Ruby', aber das ist auch nicht zwingend. Das wars auch so weit schon an Einstellungen. Zweimal noch auf 'Next' und dann startet die Installation. Zeit für einen Kaffee.
Um zu überprüfen, ob alles geklappt hat, starte ich zunächst die Winows Konsole (zu erreichen zum Beispiel über 'Start > Programme/Dateien durchsuchen > cmd (eingeben) > Enter'). Jetzt kann man einfach ruby -v eintippen und bei einer erfolgreichen Installation sollte in etwa folgendes in der Konsole zu sehen sein: ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32].
Um zunächst gems auf den neusten Stand zu bringen, ist es sinnvoll jetzt den Befehl gem update --system -y auszuführen. Das -y (oder auch --include-dependencies) steht für die automatische Installation alle Abhängigkeiten und erspart Euch eine Menge Schreibarbeit.
Dabei trat allerdings folgender Fehler auf:
Attemting remote update of rubygems-update ERROR: While executing gem ... (Gem::GemNotFoundException)
Lösung: gem env in der Konsole ausführen. Dann bekommt Ihr in etwa so etwas:
RubyGems Environment: - VERSION: 0.9.4 (0.9.4) - INSTALLATION DIRECTORY: c:/Web/ruby/lib/ruby/gems/1.8 - GEM PATH: - c:/Web/ruby/lib/ruby/gems/1.8 - REMOTE SOURCES: - http://gems.rubyforge.org
Öffnet das Verzeichnis unter GEM PATH und löscht die Datei source_cache.
Jetzt könnt Ihr den Befehl gem update --system -y ausführen. Das Update kann ein wenig dauern. Am Ende einer längeren Reihe von Informationen steht dann hoffentlich 'RubyGems system software updated'.
Um Rails zu installieren, müsst Ihr in der Konsole in das Verzeichnis '...\ruby\bin' wechseln und anschließend den Befehl gem install rails -y ausführen. Eingentlich wäre jetzt wieder Zeit genug für einen Kaffee, aber ich hatte ja gerad erst einen. Mal sehen, was es neues in meinem RSS-Reader gibt.
Ruby on Rails unterstützt verschiedenste Datenbanksysteme. Mein Favorit ist dabei MySQL. Dafür lade ich mir zunächst den MySQL Community Server herunter (aktuell ist die Version 5.1.40 stabil).
Nach der Installation startet Ihr die Konfiguration (Configure the MySQL Server now). Im Konfigurationsprozess wählt Ihr dann folgende Einstellungen:
Um die Installation des MySQL-Servers zu testen, wechselt Ihr zunächst in der Konsole in das bin-Verzeichnis des MySQL-Servers. Anschließend gebt Ihr den Befehl mysql.exe -h 127.0.0.1 -u root -p ein. Ihr werdet aufgefordert das zuvor vergebene Passwort einzugeben. Schließlich solltet Ihr eine Nachricht erhalten, die mit 'Welcome to the MySQL Monitor.' beginnt und das wäre dann ein gutes Zeichen.
Nach der erfolgreichen Installation, muss jetzt noch der MySQL Adapter von Rails installiert werden. Das könnt Ihr über den Befehl gem install mysql erledigen (natürlich wieder in der Konsole).
Jetzt sollte die Installation aller Basiskomponenten abgeschlossen sein und wir können richtig starten. Dazu mehr im nächsten Beitrag.
vom 21.10.2009
So, das ist dann auf die Schnelle zusammengebaut das Datenbankmodell für die untenstehenden Funktionen. Ich bin mir sicher, dass da noch kleine Änderungen notwendig werden, aber im Großen und Ganzen sollte es so passen.
Jetzt noch eine kurze Erklärung des Modells:
Grundsätzlich sollt für jede Spalte immer der passende Datentyp gewählt werden. Speichert man eine Zahl ab, sollte der Datentyp auch einer für Zahlen sein, z. B. integer. Weiterhin sollte man die länge der Felder auf das Notwendige begrenzen. Ein Feld zur Eingabe eines Namens muss nicht als Varchar mit einer Länge von 255 Zeichen gespeichert werden, wenn doch 40 locker für 98 Prozent der Fälle ausricht - vielleicht nicht für Hadschi Halef Omar Ben Hadschi Abul Abbas Ibn Hadschi Dawuhd al Gossarah. Wer den aufnehmen möchte, der erweitere bitte auf 72 Zeichen. Ich habe mich bei den Namen schon mal an die Konventionen von Rails gehalten.
Im Zentrum stehen die Beiträge, hier die Tabelle blogentries. Die einzelnen Spalten korrespondieren im Wesentlichen mit den gewünschten Inhalten aus dem vorherigen Beitrag.
Ein Beitrag kann in beliebig viele Kategorien (blogcategories) eingeteilt werden. Um diese n:m-Beziehung, also Beiträge dürfen in beliebig vielen Kategorien stehen und Kategorien haben beliebig viele Einträge, abzubilden, muss noch eine Assoziationstabelle geschaffen werden, die die jeweiligen Einträge in Beziehung setzt, das ist die Tabelle blogcategories_blogentries.
Ähnlich verhält es sich bei den Tags, die in der Tabelle tags gespeichert werden. Diese müssen ebenfalls in einer n:m-Beziehung mit den Beiträgen über die Tabelle blogentries_tags verknüpft werden.
Kommentare, Tabelle comments stehen in einer 1:n-Beziehung zu Beiträgen, das heißt, dass ein Kommentar immer genau zu einem Beitrag gehört, ein Beitrag aber beliebig viele Kommentare haben darf.
Für die Authentifizierung eines/ mehrerer Admins und um die Urheberschaft von Beiträgen sicherzustellen, gibt es noch eine Tabelle users, die alle Benutzer der Seite speichert.
Zu guter Letzt werden alle Bilder und sonstigen Dateien für die Beiträge in einer separaten Tabelle medias abgelegt.
Datenbankindizes sind eine eigene Indexstruktur, die separat von der Datenstruktur aufgebaut werden und für die Performanz sehr wichtig sind. Eine ausführliche Erläuterung führt jetzt zu weit, ggf. hole ich das noch nach. Daher hier nur kurz, wie die Indizes hier gesetzt wurden.
Dieser dient dazu, einen Eintrag in einer Tabelle eindeutig zu identifizieren. Ein Wert eines Primärschlüssels darf nur genau einmal innerhalb einer Tabelle vorkommen. Ebenso darf es in einer Tabelle nur eine Spalte mit dem Index Primärschlüssel geben. Primärschlüssel werden zumeist mit dem Extra 'auto_increment' versehen. Das bedeutet, dass beim Anlegen eines neuen Datensatzes der Primärschlüssel automatisch hochgezählt und vergeben wird - praktisch. Primärschlüssel sind in der Regel, müssen aber nicht, Zahlen vom Typ integer. Primärschlüssel heißen bei mir immer id.
Das sind spezielle Indizes, die bei der Verknüpfung von zwei Tabellen auftreten. Beispielsweise hat ein Beitrag immer einen Autor. Dieser wird in der Tabelle blogentries durch die Spalte user_id referenziert. Damit ist die user_id quasi ein Fremder Schlüssel, nämlich eine Referenz auf den Primärschlüssel id der users Tabelle, innerhalb der blogentries. Es können auch mehrere Fremdschlüssel in einer Tabelle referenziert werden. Die Tabelle blogcategories_blogentries verknüpft die Tabellen blogcategories und blogentries, in dem sie die jeweiligen Primärschlüssel referenziert. Fremdschlüssel benenne ich immer nach dem Muster 'Tabellenname deren Primärschlüssel referenziert wird in Kleinbuchstaben'+'_id'.
Neben den genannten Indizes gibt es noch weitere. Die wichtigsten sind Unique Keys und Index Keys. Unique Keys sind Schlüssel, die dann auf Spalten gesetzt werden, wenn ein Wert in einer Spalte nur einmal vorkommen darf, im Grunde genommen genau wie bei Primärschlüsseln. Im Beispiel trifft das auf die Spalte tag der Tabelle tags zu. Ein Tag soll also nur genau einmal in der Tabelle vorkommen - macht irgendwie Sinn.
Index Schlüssel haben prinzipiell keine Besonderheit, die auf die Daten selbst zurückzuführen ist, sondern auf deren Verwendung. Das setzen von Index Schlüssel macht dann Sinn, wenn die betroffenen Spalten besonders häufig bei Abfragen Verwendung finden. Zum Beispiel ist es sinnvoll auf die Spalte released_at der blogentries einen Index zu setzen, da nach dieser Spalte sortiert wird. Ebenso sinnig ist ein Index bei der title Spalte, da nach dem Titel in der Suche geschaut wird.
Ein paar Indizes fehlen in meinem Modell noch, aber lieber erst einmal zu wenig machen und später ergänzen, als inflationär mit diesen umzugehen!
Ein Index kann nicht nur auf genau eine Spalte alleine gesetzt werden. Oft machen auch sogenannte zusammengesetzte Indizes Sinn. Beispielsweise habe ich in der Tabelle blogentries_tags einen Unique Index auf die beiden Fremdschlüssel gesetzt, da diese in Kombination zusammen nur genau einmal auftreten dürfen.
vom 20.10.2009
Bevor ich mich an die technische Detailplanung mache, erst mal eine kleine Übersicht darüber, das es alles für Funktionen geben soll:
So, da später sicher noch mal Sachen dazukommen und das fürs Erste absolut genug ist, mache ich jetzt mal Schluss mit der Liste und planen den nächsten Schritt. Da es ja nur eine ganz kleine Anwendung ist, überspringe ich mal ein paar Schritte und mache mit dem Datenbankmodell weiter. Also frisch ans Werk und bis später.
vom 18.10.2009
Mittlerweile sollte auch bei dem Letzten angekommen sein, dass das Internet kein rechtsfreier Raum ist. Das bringt leider auch einige Verpflichtungen mit sich. Insbesondere jeder, der Inhalte in das Internet einbringt, vor allem Webmaster, muss sich der Rechtslage bewusst sein. Leider ist die meistens alles andere als eindeutig. Ich bin kein Jurist und nichts liegt mir ferner, als hier eine Rechtsberatung zu geben. Daher möchte ich hier nur dazu aufrufen, das Thema nicht zu vernachlässigen und sich gut zu informieren. Jede Webseite kann anderen Bestimmungen unterliegen. So haben z. B. Ärzte andere Informationspflichten auf ihrer Webseite als eine Anwaltskanzlei.
Wer sich eigenständig informieren möchten, für den habe ich hier drei Quellen, die ich gerne zur Rate ziehe: Das Skriptum Internet-Recht von Prof. Dr. Thomas Hoeren, Direktor des Instituts für Informations-, Telekommunikations- und Medienrecht der Uni Münster. Im Juli 2003 veröffentlichte Prof. Dr. Thomas Hoeren dieses Online-Buch zum Thema Internetrecht zum ersten Mal. Seitdem wird dieses regelmäßig aktualisiert und erweitert.
Das Bundesministerium der Justiz (BMJ) veröffentlichte einen Leitfaden zur Impressumspflicht, der leider keine Rechtssicherheit schafft:
Dies kann zwar im Einzelfall eine rechtliche Beratung nicht ersetzen, hilft aber, die bestehenden Pflichten überhaupt zu erkennen. Der Leitfaden soll Ihnen dabei als Orientierungshilfe dienen, rechtsverbindlich ist er nicht.Dann wäre da noch der Law-Blog, der rund um die Themen IT-Recht, Geistigen Eigentum, Gewerblichen Rechtsschutz und angrenzende Rechtsgebiete informiert.
vom 18.10.2009
Eine Frage, die mich immer wieder umtreibt und auf die ich noch keine allgemeingültige Antwort gefunden habe und wahrscheinlich auch nie werde. Flexible Seitenbreite passt sich natürlich immer perfekt an den zur Verfügung stehenden Platz an. Auf kleinen Monitoren finde ich das super, aber wenn eine Seite meinen 24 Zöller ganz ausfüllt, dann ist das für die Benutzbarkeit nicht gerade förderlich. Sicherlich kann man hier das Fenster einfach kleiner machen. Für Designfetischisten ist eine flexible Breite auch nichts, aber Form follows ja eigentlich Function ^^.
Gerade was kleinere Anzeigeflächen - z. B. beim Handy - angeht, sind moderne mobile Browser aber mittlerweile auch sehr gut und man hat hier nicht mehr mit Scrolleisten zu kämpfen (zumindest, wenn die Seite ordentlich aufgebaut ist). Auch die Argumentation, dass die Seiten bei der browserseitigen Vergrößerung der Schrift nicht mitskallieren, ist bei modernen Browsern hinfällig.
Und jetzt? Man sollte das anwendungsfallbezogen entscheiden. Im Fall dieser Webseite entscheide ich mich für eine starre Breite von 640 Pixeln mit horizontaler Zentrierung. Das wird sich später noch ändern, wenn hier noch ein paar Features dazu kommen.
vom 18.10.2009
Um die Seite ein wenig zu stylen, werde ich jetzt ein wenig CSS einbauen. Grundsätzlich habe ich für mich folgende Regeln bei der Erstellung von Stylesheets festgelegt:
Zunächst kopiere ich mir mal meine Basis-CSS-Datei. Teil 1:
* { margin: 0; padding: 0; line-height: 0; list-style: none; }
img, fieldset { border: 0 none; }
Hier werden einige Eigenschaften von allen Elementen quasi auf null zurückgesetzt, um später nicht mit irgendwelchen vorgegebenen Werten arbeiten zu müssen. Beispielsweise haben alle Überschriften h[1...6] standardmäßig Außenabstände nach oben und unten.
Jetzt kommen ein paar Basiseigenschaften für Hyperlinks und den Body des Dokuments:
a { color: #006699; text-decoration: none; }
a:hover { text-decoration: underline; }
body {
background: #fff;
color: #000;
font: 80% Arial, Verdana, Helvetica, sans-serif;
}
Jetzt benötige ich noch ein paar Regeln, um ein paar Problemen mit floatenden Elementen Herr zu werden. Eine ausführliche Erläuterung findet ihr bei Peter Müller.
/* ----- clearing ----- */
.clear { clear: both; }
.clearfix:after {
content: ".";
display: block;
clear: both;
height: 0;
line-height: 0;
visibility: hidden;
}
/* IE-Patch */
.clearfix { display: inline-block; }
/* Commented Backslash Hack: Vor IE5/Mac verstecken \*/
html[xmlns] .clearfix { display: block; }
* html .clearfix { height: 1%; }
Damit sind die Grundlagen geschaffen. Die Seite sieht so aber noch nicht wirklich gut aus - eigentlich sogar schlimmer als zuvor. Daher müssen noch ein paar weitere Regeln her: Zunächst einmal für Überschriften, für Paragraphen, für Listen, für Quellcode und für Zitate.
vom 17.10.2009
Der Plan war gut, aber leider ist mir ein wichtiges Detail entgangen: Wenn man mit Ruby on Rails entwickeln möchte, sollte der eigene Hoster das vielleicht auch unterstützen! Tja, und genau das liegt das Problem ;-). Muss ich mich wohl erst mal nach einem entsprechenden Angebot umgucken und ggf. meine Domain umziehen.
Gut, dass ich mich zwischenzeitlich auch mit anderen Dingen beschäftigen kann. Aktuell sieht die Seite ja schon ein wenig langweilig aus und die Darstellung fördert nicht gerade die Lesbarkeit. Daher widme ich mich als nächstes doch schon einem Basislayout und die ersten Versuche mit Rails bleiben weiterhin lokal auf meinem Rechner.
Als ich das Video aus dem letzten Beitrag zunächst mit dem Google Code in meine Webseite eingebaut hatte, bekam ich 13 Fehler und zwei Warnungen vom HTML Validator und das gefiel mir gar nicht.
Aus diesem Grund habe ich nach einer Lösung gesucht, die diese Probleme vermeidet und den Code vollständig valide macht. Dazu musst der folgende Ausgangsquellcode einfach nur verschlankt werden.
<object width="640" height="385"> <param name="movie" value="http://www.youtube.com/v/fL_GZwoC2uQ&hl=de&fs=1&"></param> <param name="allowFullScreen" value="true"></param> <param name="allowscriptaccess" value="always"></param> <embed src="http://www.youtube.com/v/fL_GZwoC2uQ&hl=de&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></embed> </object>
Der neue Code des Videos entfernt alle nicht validen Elemente und berücksichtigt dabei alle relevanten Eigenschaften, wie zum Beispiel die Breite und Höhe des Videos (hier: 560x340), die ID des Videos (fL_GZwoC2uQ) und zusätzliche Parameter für Sprache, Farbe oder ähnliches.
<object type="application/x-shockwave-flash" width="560" height="340" data="http://www.youtube.com/v/fL_GZwoC2uQ"> <param name="movie" value="http://www.youtube.com/v/fL_GZwoC2uQ&hl=de&fs=1&" /> </object>
Wenn Ihr also selbst ein YouTube Video auf Eurer Webseite integrieren möchtet, dann schaut Euch einfach den Code an, den YouTube Euch bereitstellt und zieht Euch die relevanten Parameter raus und ersetzt die aus dem letzten Codeschnipsel damit.
vom 17.10.2009
Ich könnte jetzt tagelang darüber reden, möchte mich aber kurz fassen. An anderer Stelle haben sich schon viele Leute damit auseinandergesetzt. Mir fällt jetzt gerade kein prägnanter Artikel ein, der die folgende Problemantik genau erläutert. Vielleicht kann mir ja jemand auf die Sprünge helfen.
Worum geht's? Es geht darum, dass man für alle Inhalte auch immer passende HTML-Tags findet, die die Inhalte semantisch korrekt auszeichnen. Überschriften werden beispielsweise mit <h[1...6]>, Paragraphen mit <p> und Zitate mit <cite> ausgezeichnet. Insbesondere sollen auch Tabellen nicht für die Erstellung von Layouts missbraucht, sondern nur dann eingesetzt werden, wenn der Inhalt semantisch in eine Tabelle gehört. Wie dann eine 'gute' Tabelle aussieht, werde ich dann bei Gelegenheit mal erläutern.
Zu Layouttabellen habe ich gerade noch was beim Google Webmaster Central Channel gefunden. In einem Videobeitrag wird erläutert, dass es auch für tabellenbasierte Layouts bei Suchmaschinenrankings keine Nachteile geben soll und man sich bemüht gute Inhalte auch in diesen zu identifizieren und hoch zur ranken.
Insgesamt denke ich aber, dass man es den Suchmaschinen schon einfacher macht, wenn das HTML so genutzt wird, wie es gedacht wird. Zudem blähen Tabellen den Code der Seite schon sehr stark auf und auch das verbessert die Durchsuchbarkeit nicht gerade.
vom 17.10.2009
Natürlich liegt mir nichts ferner, als meinen Besuchern nachzustellen, aber ein paar Informationen möchte man dann doch über seine Seitenbesucher, vor allem deren Anzahl, haben. Zwar habe ich in der Vergangenheit mit großer Begeisterung meine eigenen Auswertungssysteme geschrieben, aber letztendlich spricht nichts dagegen für grundlegende Auswertungen auf Google Analytics zurückzugreifen. Es spricht sogar sehr viel dafür.
Also muss wieder das Google Konto bemüht werden, um Zugriff auf Analytics zu bekommen. Hier richten wir einen neuen Account ein. Im vierten Schritt der Einrichtung bekommt man ein bisschen JavaScript Quellcode, den man direkt vor dem </body> seiner Seite einfügt. Damit der Code nicht durch XML-Parser interpretiert wird packen wir diesen noch in //<![CDATA[ ... //]]>.
Bis das Tracking gestartet wird kann einige Zeit vergehen, also nicht gleich nervös werden, wenn in Analytics nicht direkt Daten auftauchen.
vom 17.10.2009
Ich will nicht, vor allem nicht an dieser Stelle, übermäßigen Aufwand betreiben, was die Suchmaschienenoptimierung angeht. Ein paar Kleinigkeiten möchte ich allerdings jetzt schon beherzigen:
Die webmaster tools eignen sich hervorragend, um grundlegende Analysen durchzuführen: Wie crawlt und indiziert Google meine Seite? Gibt es dabei Probleme? Wie ist der Zugriff durch Suchmaschinen?
Desweiteren können Google grundlegende Informationen bereitgestellt werden, z. B. die Seitenstruktur, damit die Indizierung genau so geschieht, wie man es möchte.
Zunächst einmal Registrieren - bin ich schon, es genügt ein normales Google Konto. Ist man Angemeldet, bietet sich einem die Möglichkeit eine Webseite hinzuzufügen. Das mache ich jetzt und zwar sowohl mit der Adresse www.oliver-liebsch.de, als auch mit der Domain ohne "www".
Jetzt kommen ein paar Grundeinstellungen in den webmaster tools - für beide Seiten:
# Definiert für welche Crawler die folgenen Anweisungen gelten sollen. # Durch die Wildcard * gilt das Folgende für alle Webcrawler User-agent: * # Hier wird festgelegt, dass alle Verzeichnisse und Dateien ausgelesen werden dürfen Allow: / # URL der Sitemap (unter anderem für nur Googlebot, Yahoo! Slurp, msnbot, Ask.com) Sitemap: http://www.oliver-liebsch.de/sitemap.xml
Das ist jetzt natürlich nur eine absolute Minimalversion, aber fürs Erste genügt das vollkommen. Ich werde später noch darauf zurück kommen.
Einzeilige Kommentare werden mit # maskiert.
RewriteCond (vgl. de.selfhtml.org) beschreiben Bedingungen, die erfüllt sein müssen, damit die RewriteRule ausgeführt wird. Erwartet als Parameter:
RewriteRule (vgl. de.selfhtml.org) sind Umleitungsregeln. Diese können mehrmals vorkommen, dabei ergibt jedes Vorkommnis eine eigene Umleitung. Die Reihenfolge der Umleitungen ist wichtig, da diese in der Reihenfolge ihres Vorkommens angewendet werden. Erwartet als Parameter:
Und so sieht die Datei zunächst einmal aus. Im weiteren Verlauf werden wir hier sicher noch mal Hand anlegen müssen.
# Abfrage auf Vorhandensein von mod_rewrite:
<IfModule mod_rewrite.c>
# Rewrite einschlaten:
RewriteEngine On
# Standardzeichensatz setzen:
AddDefaultCharset UTF-8
# Basis für Umleitungen definieren:
RewriteBase /
Alle *.html auch als *.htm erreichbar machen:
RewriteRule ^(.*)\.htm$ $1.html
# Directory Listing verhindern:
Options -Indexes
# Probleme mit Trailing Slashes verhindern:
RewriteCond %{REQUEST_URI} (.*)/$
RewriteRule ^(.*)/$ $1 [L,R=301]
# Alles auf URL mit www davor umleiten:
RewriteCond %{HTTP_HOST} ^oliver-liebsch\.de
RewriteRule (.*) http://www.oliver-liebsch.de/$1 [R=301,L]
# Verhindern, dass die PHPSESSID in der URL angezeigt wird:
RewriteCond %{QUERY_STRING} PHPSESSID
RewriteRule ^(.*)$ $1? [L,R=301]
# Fehlerdokumente setzen:
# ErrorDocument 404 kommt noch
# ErrorDocument 403 kommt noch
# Register Globals Off (falls Ihr PHP nutzt, aber später sind wir ja nicht in der PHP-Welt):
php_flag register_globals off
</IfModule>
vom 17.10.2009
Zunächst einmal muss natürlich der grundlegende Aufbau in HTML erstellt werden. Dabei sind einige Entscheidungen zu treffen:
Diese ersten Überlegungen führen zu folgender Grundstruktur:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de"> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/> <meta name="language" content="deutsch, de" /> <meta name="author" content="" /> <meta name="publisher" content="" /> <meta name="copyright" content="" /> <meta name="description" content="" lang="de" /> <meta name="keywords" content="" /> <title>Oliver Liebsch . Meine persönliche Spielwiese</title> </head> <body> </body> </html>
Jetzt möchte ich gerne noch Geo-Tags hinzufügen, denn die Seite soll örtlich zu meinem zu Hause zugeordnet werden. Daher kommen noch folgende Meta-Tags im Head der Seite hinzu:
<meta name="geo.region" content="DE-NW" /> <meta name="geo.placename" content="Münster" /> <meta name="geo.position" content="51.969234;7.596636" /> <meta name="ICBM" content="51.969234, 7.596636" />
Puh, schon jetzt ist klar: Lang komme ich ohne datenbankgestütztes Content Management nicht aus...
vom 16.10.2009
Es gibt eine schier unüberschaubare Anzahl an Tools, die einem Webdesigner das Leben um eine Vielfaches einfacher machen können. Hier hat sicher jeder seine eigenen Präferenzen. Die folgenden Werkzeuge sind meine absoluten Favoriten:
vom 16.10.2009
Meine erste Webseite ging 1996 online. Seitdem gab es insgesamt 12 Versionen - von einfachem HTML, bis zu einer kompletten Flash-Seite war alles dabei. 13 Jahre später möchte ich jetzt wieder eine neue Version online bringen. Dieses Mal allerdings mit einem neuen Ansatz:
Ich werde keine fertig Version online stellen und diese dann bis zur nächsten Version brach liegen lassen, so war es bisher häufig, sondern die Seite Schritt für Schritt neu aufbauen und den Weg zur neuen Version kommentieren.
Starten werde ich erst einmal mit einer einfachen HTML-Version. Dann soll das Ganze dynamisch werden, dieses Mal mit Ruby on Rails. Bisher war PHP die Entwicklungssprache meiner Wahl, aber man muss sich ja mal weiterbilden. Irgendwann Soll das Ganze hier auch ein Design bekommen. Dabei wird dann sicherlich CSS eine große Rolle spielen.