Ruby on Rails: Seitengenerierung mit nanoc

Als ich angefangen Webseiten zu generieren, war die Dokumentation von nanoc für mich klarer und leichter zu lesen als die won, webgen und webby, und webgen konte alles was ich benötigt habe. Im Moment kenne ich mich nur bei Webgen aus und kann keine Vergleiche zu den anderen Generatoren ziehen. --ovhaag 01:06, 24. Nov. 2008 (CET)

Deshalb will ich im Folgenden nanoc beschreiben und erklären, wie man nanoc mit einer Rails-Applikation fernsteuert.

Ok, was schauen wir uns zuerst an? Klar, die Webseite des nanoc Projekts: http://nanoc.stoneship.org/ Und dann kommt die Installation. Nanoc gibt's als gem. Also

sudo gem install nanoc

Im Moment (24.11.2008) sind wir bei nanoc 2.1.4. Auf der Help-Seite von nanoc (http://nanoc.stoneship.org/help/) finden wir ein Tutorial und ein Manual (Handbuch). Fürs Erste folgen wir grob dem Tutorial.


Hinweis: Nanoc 3

Bearbeiten

Achtung, wer hier neu ist, sollte gleich einen Blick auf nanoc 3 werfen. Dieses ist nicht vollständig kompatibel zu nanoc 2 und wird hier nicht beschrieben!

Webauftritt anlegen

Bearbeiten

Nanoc wird über die Kommandozeile bedient. Also bleiben wir in der Konsole und wechseln in das Verzeichnis in dem wir einen Webauftritt (site) anlegen wollen. Das tun wir dann mit:

nanoc create_site demo_01

Schaun wir uns das Ergebnis an. Ähnlich wie Rails das auch macht, hat nanoc erst 'mal ein Gerüst angelegt, das wir jetzt benutzen können. Dazu wechseln wir in das Projektverzeichnis, das nanoc soeben angelegt hat:

cd demo_01

Dort erzeugen wir den Webauftritt mit

nanoc compile

Es wird eine index-Seite angelegt, weil nanoc im content-Verzeichnis schon eine Seitenvorlage dafür hat. Weitere Seiten legen wir an mit

nanoc create_page Seite_01
nanoc create_page Seite_02
nanoc create_page Seite_03

usw. Für jede Seite erzeugt nanoc ein Unterverzeichnis im content-Verzeichnis mit einer Textvorlage und einer Datei mit Meta-Informationen.

Schauen wir uns Seite_01 an:

# content/Seite_01/Seite_01.html

Hi, I'm a new page!
# content/Seite_01/Seite_01.yaml

# Built-in

# Custom 
title: A New Page

Wenn wir jetzt wieder

nanoc compile

aufrufen, erzeut nanoc 3 Seiten im output-Verzeichnis:

  • output/Seite_01/index.html
  • output/Seite_03/index.html
  • output/Seite_01/index.html

In der console wird uns das auch angezeigt

Macintosh:demo_01 ovhaag$ nanoc compile
Compiling site...
      create  [0.00s]  output/Seite_01/index.html
      create  [0.00s]  output/Seite_02/index.html
      create  [0.00s]  output/Seite_03/index.html

Die Seiten können wir uns im Browser anschauen. Sie enthalten den vorgegeben Text aus der Vorlage (content/Seite_01/Seite_01.html, ..) und der Titel kommt aus der Datei mit den Meta-Informationen (content/Seite_01/Seite_01.yaml, ..). Außerdem sind sie nach einem einheitlichen Layout gestaltet. Bevor wir klären, wo das Layout herkommt, wollen wir den Text ändern.

Text ändern, ..

Bearbeiten

Da war doch die Textvorlage, content/Seite_01/Seite_01.html. Wenn wir da den Text ändern und den Webauftritt aktualisieren

Macintosh:demo_01 ovhaag$ nanoc compile
Compiling site...
      update  [0.00s]  output/Seite_01/index.html

Mit den Meta-Informationen gehts genauso. Im Moment ist ja nur der Titel vorbereitet. Also verädern wir den in der Vorlage content/Seite_01/Seite_01.yaml, etwa so

# content/Seite_01/Seite_01.yaml

# ..
title: Die neue Seite 1

Hmm, funktioniert, aber nicht ganz. Der Titel ist neu, und er enthält den richtigen Text, aber da steht noch was davor. "A Brand new nanoc Site". Hat das etwas mit der Layoutvorgabe zu tun, die wir sowieso ansehen wollten?

Layout ändern

Bearbeiten

Nun layouts/default/default.html enthält das layout. Wenn wir diese Datei ändern und den Webauftritt mit nanoc compile aktualisieren, werden alle Ausgabedateien neu generiert und das Layout angepasst. Dazu gehört auch der Tiltel.

# layouts/default/default.html
..
<title>Railsbook Demo Site - <%= @page.title %></title>
..

Bei der Gelegenheit lernen wir auch, wie nanoc den eingebetteten Ruby Code wie "<%= @page.title %>" ausgeführt, jedenfalls dann, wenn der Filter in layouts/default/default.yaml richtig gesetzt ist, nämlich zu filter: erb.

Im Grunde können wir jetzt schon Webauftritte mit nanoc bauen. Aber ein paar Dinge lohnt es sich noch anzusehen.

Manchmal wollen wir unsere Texte nicht mit HTML-Auszeichnungen liefern, sondern mit Markdown (BlueCloth, o.ä.) Haml, Textile o.ä. Dafür gibt es eine Reihe vorgefertigter Filter und wir können wir selbst Filter schreiben.

Ein Filter ist eine Klasse, die von Nanoc::Filter abgeleitet ist. Der Filter trägt einen eindeutigen Namen (unique identifier), über den er angesprochen wird. So findet nanoc Filter im Lib-Verzeichnis automatisch.

Und ein Filter muss die run-Methode implementieren, die nanoc dann aufruft.

Mit diesen Informationen können wir einen Filter schreiben, der deutsche Umlaute encoded:

# lib/encode_fiter.rb

class EncodeFilter < Nanoc::Filter
  identifier :encode
  
  def run(content)
      content.gsub( 'ä', '&auml;' ).gsub( 'ö', '&ouml;' ).gsub( 'ü', '&uuml;' ).gsub( 'Ä', '&Auml;' ).gsub( 'Ö', '&Ouml;' ).gsub( 'Ü', '&Uuml;').gsub( 'ß', '&szlig;' )
  end
end

Natürlich wollen wir ihn gleich testen. Dazu schreiben wir eine Seite_02 mit Umlauten.

# content/Seite_02/Seite_02.html

Diese Seite 2 hat deutsche Umlaute: ä, ö, ü und Ä, Ö, Ü und ein scharfes ß.
Schaun wir mal wie sie rüberkommen. (Achtung: rüber mit "ü"!)
Das war's. Finito!

Nun müssen wir nur noch wir nanoc anweisen, den Filter zu benutzen. Dazu ergänzt man die Meta-Datei, also hier Seite_02.yaml um eine filters_pre oder eine filters_post Section. Pre-Filter werden vor dem layouten aufgreufen, Post-Filter danach. Wir wollen nur den Text filtern, das Template ist ok. Deshalb nehmen wir eine Pre-Filter:

# content/Seite_02/Seite_02.yaml

# Built-in
filters_pre:
- "encode"

# Custom
title: Umlaute, die neue Seite 2

Wenn wir jetzt "nanoc compile" aufrufen, werden die Umlaute in Seite_02 encoded. Das was wir wollten.

Normalerweise liegen die fertigen HTML Files in unterschiedlichen Verzeichnissen, hier beispielsweise in Seite_01, Seite_02, usw.. Deshalb müssen wir, wenn wir im Template eine Datei einbinden wollen, z.B. ein Stilesheet, den Pfad absolut angeben. Aber manchmal wollen wir das nicht. Sagen wir der Prototyp, den wir unserem Kunden zeigen, liegt in einem anderen Verzeichnis als die produktive Site. Dann brauchen wir relative Pfade oder eine ganz andere Lösung.

Ein Ansatz ist es mit einem kleinen Ruby-Helper die Dateien ins Rootverzeichnis zu kopieren. Dann funktionieren relative Pfade.

# rearrange.rb

# Helper um index.php Dateien aus benannten Unterverzeichnissen
# mit diesem Namen ins Rootverzeichnis zu kopieren.
# p.S.: Geht analog für index.html Dateien.

require "find"
require "fileutils"

puts "rearrage called"
location = "output"

Find.find(location) do |path|
  next unless File.basename(path) == "index.php"
  next if path == "output/index.php"
  
  newpath = path.gsub(/\//, '_').gsub('output_', 'output/').gsub(/_index.php/, '.php')
  puts newpath
  
  FileUtils.copy_file(path, newpath)
end

Solange die Namen eindeutig sind funktioniert dieser Ansatz. Aber es gibt eine bessere Lösung. Nanoc kennt Router. Und für diesen Zweck liefert nanoc sogar einen mit: "no_dirs"

Wenn wir den in der globalen config.yaml Datei eintragen, ... (TODO)

  • TODO? Router selbst schreiben oder modifizieren.

Fernsteuerung via Rails

Bearbeiten

Zum Abschluss noch ein Schmankerl. Nanoc lässt sich von anderen Programmen aufrufen. Wir können also eine Railsanwendung schreiben, die nanoc benutzt. Macht das Sinn?

Nehmen wir an, wir haben 100 Kunden, einfachstes Hosting ohne Ruby, vielleicht sogar statisch, d.h. ohne Php und MySQL. Damit unsere Kunden die Seiten editieren können, schreiben wir ein Multiuser-CMS in Rails, das statische Seiten mit nanoc erzeugt und per FTP zum Host des jeweiligen Kunden rüberschiebt. Da nur unsere 100 Kunden das CMS benutzen, hat unser Server mit Rails nur wenig Last zu tragen und Hochverfügbarkeit ist wahrscheinlich auch kein Thema. Der Traffic für die Seitenaufrufe fällt beim Host unserer Kunden an.

Ist vielleicht doch eine Überlegung wert.

  • TODO: Demo wie man nanoc via Rails ansteuert.