diff --git a/bin/md2site b/bin/md2site
index cba1178..c8c729b 100755
--- a/bin/md2site
+++ b/bin/md2site
@@ -4,6 +4,10 @@ require 'rubygems'
require 'trollop'
require 'ftools'
require 'rdiscount'
+require 'erb'
+require 'uv'
+require 'hpricot'
+require 'cgi'
opts = Trollop::options do
version "md2site (c) 2010 Dirk Breuer"
@@ -18,8 +22,9 @@ EOS
opt :source_path, "Where to read the Markdown files from", :default => "site"
opt :destination_path, "Where to put the generated files", :default => "htdocs"
- opt :config, "Path to config file with meta information", :default => "config.yml"
opt :nojekyll, "Put a .nojekyll file in destination_path", :default => true
+ opt :template, "Which template to use", :default => "templates/base.html.erb"
+ opt :theme, "Which syntax color scheme to use", :default => "sunburst"
end
class SiteGenerator
@@ -48,7 +53,7 @@ class SiteGenerator
def create_directory_structure
`rm -r #{@options[:destination_path]}`
-
+
@markdown_pages.each do |md_path, html_path|
File.makedirs("#{@options[:destination_path]}/#{File.dirname(html_path)}")
end
@@ -57,8 +62,28 @@ class SiteGenerator
def create_html_files
@markdown_pages.each do |md_path, html_path|
markdown = RDiscount.new(File.read(md_path))
- File.open(File.join(@options[:destination_path], html_path), "w") { |f| f.write markdown.to_html }
+ File.open(File.join(@options[:destination_path], html_path), "w") { |f| f.write trough_template(markdown.to_html) }
+ end
+ Uv.copy_files "xhtml", @options[:destination_path]
+ end
+
+ def highlight_code_in_html(html)
+ html = Hpricot(html)
+ html.search('//pre/code') do |element|
+ element.inner_html =~ /^\!\!\!(\w+)/
+ element.inner_html = element.inner_html.gsub(/^\!\!\!(\w+)/, '')
+ element.inner_html = Uv.parse(element.to_plain_text, "xhtml", "#{$1 || 'ruby'}", false, @options[:theme])
end
+ html.to_html
+ end
+
+ def template
+ @renderer ||= ERB.new(File.read(@options[:template]))
+ end
+
+ def trough_template(content)
+ @content = content
+ highlight_code_in_html(template.result(binding))
end
def create_nojekyll
diff --git a/config.yml b/config.yml
deleted file mode 100644
index 2d84b58..0000000
--- a/config.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-title: "ASS Anfänger Kurs"
-url: "http://galaxycats.github.com/ASS-Beginner"
-description: "Maintains the demo source code, course material, like hand outs and demo-scripts for the ASS courses at the University of Applied Science Cologne."
diff --git a/site/handson/associations.md b/site/handson/associations.md
index 71b9028..be33f3d 100644
--- a/site/handson/associations.md
+++ b/site/handson/associations.md
@@ -52,6 +52,7 @@ ableiten.
1:1-Beziehung (`has_one`) zwischen zwei Objekten: Ein `Picture` hat genau ein
`Thumbnail`.
+ !!!ruby_on_rails
class Picture < ActiveRecord::Base
has_one :thumbnail
end
@@ -69,6 +70,7 @@ ableiten.
1:m-Beziehung (`has_many`) zwischen zwei Objekten: Eine `Company` hat viele
`Client`s.
+ !!!ruby_on_rails
class Company < ActiveRecord::Base
has_many :clients
end
@@ -95,6 +97,7 @@ noch eine Klasse definiert werden.
Beispiel für `has_many_and_belongs_to`:
+ !!!ruby_on_rails
class Developer < ActiveRecord::Base
has_and_belongs_to_many :projects
end
@@ -114,6 +117,7 @@ Beispiel für `has_many_and_belongs_to`:
Beispiel für `has_many :through`:
+ !!!ruby_on_rails
class Developer < ActiveRecord::Base
has_many :assignments
has_many :projects, :through => :assignments
@@ -155,6 +159,7 @@ in den Controllern über die Methode `session` zugreifen. Das `session`-Objekt
ist dabei als Hash realisiert. Die Speicherung der ID eines Benutzers könnte
dementsprechend wie folgt aussehen:
+ !!!ruby_on_rails
user = User.find_by_username(params[:username])
if user
session[:user_id] = user.id
diff --git a/site/handson/controller_und_views.md b/site/handson/controller_und_views.md
index 23f0204..e3c108b 100644
--- a/site/handson/controller_und_views.md
+++ b/site/handson/controller_und_views.md
@@ -30,6 +30,7 @@ Die Aufgabe soll natürlich RESTful realisiert werden. Wir brauchen daher auf
jeden Fall drei Actions in unserem Controller. Hier noch einmal beispielhaft
die Implementierung der Action zu Anzeige aller Statusmitteilungen:
+ !!!ruby_on_rails
class MessagesController < ApplicationController
def index
@@ -43,6 +44,7 @@ Ordner `app/views/messages` verwenden. Im Template muss nicht mehr das HTML
Gerüst definiert werden, dass ist bereits über das Layout geschehen. Wir müssen also
nur noch alle Statusmitteilungen ausgeben. Dazu kann man sich folgenden Code vorstellen:
+ !!!html_rails
<% @messages.each do |message| %>
<% div_for message do %>
<%= message.content %>
@@ -69,6 +71,7 @@ verwendet. Für deren Realisierung zur Eingabe von Daten für ein
`ActiveRecord`-Objekt bietet Rails eine ganze Reihe von Helper-Methoden an. An
folgendem Beispiel seien einige dieser Methoden kurz beschrieben:
+ !!!html_rails
<% form_for :message do |f| %>
<%= f.text_area :content %>
<%= f.submit "Post Status" %>
diff --git a/site/handson/mentions_und_follower.md b/site/handson/mentions_und_follower.md
index 994c8b4..45e7656 100644
--- a/site/handson/mentions_und_follower.md
+++ b/site/handson/mentions_und_follower.md
@@ -50,6 +50,7 @@ aufgerufen, nachdem eine bestimmte Methode ausgeführt wurde, wie z.B. die
`save`-Methode eines Active Record Objekts. Hier ein kleines Beispiel, um das
zu verdeutlichen:
+ !!!ruby_on_rails
class User < ActiveRecord::Base
after_save :send_welcome_email
diff --git a/site/handson/partials.md b/site/handson/partials.md
index da0de18..74e3034 100644
--- a/site/handson/partials.md
+++ b/site/handson/partials.md
@@ -26,6 +26,7 @@ Partials werden in Rails immer durch einen Underscore als Prefix im Dateinamen
identifiziert und werden in der gleichen Verzeichnisstruktur abgelegt wie die eigentlichen
Templates zu einem Controller:
+ !!!plain_text
app/
+-views/
+-messages/
@@ -36,6 +37,7 @@ Templates zu einem Controller:
Anhand des folgenden Beispiels wollen wir kurz erklären, wie Partials in Templates
integriert werden können:
+ !!!html_rails
Statusmitteilungen
<%= render "messages/form" %>
diff --git a/site/handson/rails.md b/site/handson/rails.md
index 895ddf4..3de6652 100644
--- a/site/handson/rails.md
+++ b/site/handson/rails.md
@@ -57,6 +57,7 @@ Datenbank-Tabellen angelegt werden.
### Rails installieren und Projektstruktur initialisieren
+ !!!plain_text
$> gem update --system
$> gem --version
1.3.6
@@ -64,6 +65,7 @@ Datenbank-Tabellen angelegt werden.
Rails-Projekt generieren:
+ !!!plain_text
$> rails
### Generatoren und rake-Tasks
@@ -73,6 +75,7 @@ Zunächst muss eine Resource mit Hilfe des folgenden Befehls generiert werden:
Dabei werden alle relevanten Dateien erzeugt, von der Migration über das
Modell bis hin zum Controller. Hier ein Beispiel:
+ !!!plain_text
rails generate resource message content:string
($> ~/projects/ass/twitter-clone)
invoke active_record
@@ -101,6 +104,7 @@ Jetzt kann man direkt die erzeugte Migration ausführen und den Server starten:
**Aber** wir wollen uns das ganze erstmal ohne Web Server auf der *Console*
ansehen:
+ !!!plain_text
$> rails console
irb> Message
=> Message(id: integer, content: string, created_at: datetime, updated_at: datetime)
@@ -110,6 +114,44 @@ ansehen:
=> true
irb> message
=> #
+
+#### Einrichten der Rails Console
+
+Die Console lässt sich um einige Gimmicks erweitern, wie etwa
+Syntax-Highlighting. Eine weitere sinnvolle Anpassung ist die Umleitung der
+Logausgaben auf STDOUT. So kann man direkt in der Console sehen wie etwa die
+SQL-Query aussieht, die von Rails generiert wird. Um dass zu erreichen, müssen
+zwei kleine Gems (Ruby Bibliotheken) installiert werden:
+
+ !!!plain_text
+ $> gem install wirble
+ $> gem install utility_belt
+
+Danach muss noch eine `.irbrc`-Datei im Home-Verzeihnis angelegt mit folgendem
+Inhalt angelegt werden:
+
+ # load libraries
+ require 'rubygems'
+ require 'wirble'
+ require 'utility_belt'
+
+ # start wirble (with color)
+ Wirble.init
+ Wirble.colorize
+
+ IRB.conf[:SAVE_HISTORY] = 100
+ IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"
+
+ if Object.const_defined?('Rails')
+ Rails.logger.instance_variable_set("@log", STDOUT)
+ end
+
+Anschließend müssen nun noch die zuvor installierten Gems im Rails-Projekt
+hinzugefügt werden. Dazu einfach folgende Zeilen im `Gemfile` des
+Rails-Projektes hinzufügen:
+
+ gem "wirble"
+ gem "utility_belt"
### Konventionen
@@ -131,6 +173,7 @@ In der Routing-Datei (`config/routes.rb`) muss zu diesem Zeitpunkt nichts weiter
`root`-Route anzulegen. Diese beschreibt welche Action mit dazugehörigem Controller beim Aufruf der Haupt-URL (`http://localhost:3000`)
aufgerufen werden soll. Dazu fügt man folgenden Code am Ende der Routing-Definition ein:
+ !!!ruby_on_rails
route :to => "#"
### Migrationen
diff --git a/site/handson/ruby.md b/site/handson/ruby.md
index fa06f1e..93b082f 100644
--- a/site/handson/ruby.md
+++ b/site/handson/ruby.md
@@ -31,11 +31,13 @@ Program"](http://pine.fm/LearnToProgram/ "Learn to Program, by Chris Pine") von
* [Rails Searchable API Doc](http://railsapi.com/ "Rails Searchable API Doc")
* [Ruby Quick Reference Card](http://www.scribd.com/doc/7991776/Refcard-30-Essential-Ruby "Refcard #30: Essential Ruby")
+* [Ruby Essentials](ruby_essentials.html "Ruby Essentials")
## Shortcuts
### Environment Setup
+ !!!plain_text
export PATH=/usr/local/bin:$PATH
### Strings
@@ -64,7 +66,7 @@ Program"](http://pine.fm/LearnToProgram/ "Learn to Program, by Chris Pine") von
4.times { |n| puts n }
- while i < 10
+ while i < 10
# do something
end
diff --git a/site/handson/ruby_essentials.md b/site/handson/ruby_essentials.md
new file mode 100644
index 0000000..5f33fa9
--- /dev/null
+++ b/site/handson/ruby_essentials.md
@@ -0,0 +1,380 @@
+# Tag 1, Hands-On 1.1: Ruby Essentials
+
+## Ziel
+
+Hier zeigen wir ein paar ausführlichere Ruby Beispiel und gehen noch mal auf
+die zentralen Konzepte der Objekt-Orientierung ein.
+
+## Konzepte der OOP
+
+### Abstraktion
+
+Jedes Objekt im System kann als ein abstraktes Modell eines Akteurs
+betrachtet werden, der Aufträge erledigen, seinen Zustand berichten und
+ändern und mit den anderen Objekten im System kommunizieren kann, ohne
+offenlegen zu müssen, wie diese Fähigkeiten implementiert sind (vgl.
+abstrakter Datentyp (ADT)). Solche Abstraktionen sind entweder Klassen (in
+der klassenbasierten Objektorientierung) oder Prototypen (in der
+prototypbasierten Programmierung).
+
+* *Klasse* Die Datenstruktur eines Objekts wird durch die Attribute (auch
+ Eigenschaften) seiner Klassendefinition festgelegt. Das Verhalten des
+ Objekts wird von den Methodern der Klasse bestimmt. Klassen können von
+ anderen Klassen abgeleitet werden (Vererbung). Dabei erbt die Klasse die
+ Datenstruktur (Attribute) und die Methoden von der vererbenden Klasse
+ (Basisklasse).
+* *Prototype* Objekte werden durch das Clonen bereits existierender Objekte
+ erzeugt und können anderen Objekten als Prototypen dienen und damit ihre
+ eigenen Methoden zur Wiederverwendung zur Verfügung stellen, wobei die
+ neuen Objekte nur die Unterschiede zu ihrem Prototypen-Objekt definieren
+ müssen..
+
+### Datenkapselung
+
+Als Datenkapselung bezeichnet man in der Programmierung das Verbergen von
+Implementierungsdetails. Der direkte Zugriff auf die interne Datenstruktur
+wird unterbunden und erfolgt statt dessen über definierte Schnittstellen.
+Objekte können den internen Zustand anderer Objekte nicht in unerwarteter
+Weise lesen oder ändern. Ein Objekt hat eine Schnittstelle, die darüber
+bestimmt, auf welche Weise mit dem Objekt interagiert werden kann. Dies
+verhindert das Umgehen von Invarianten des Programms.
+
+### Polymorphie
+
+Verschiedene Objekte können auf die gleiche Nachricht unterschiedlich
+reagieren. Wird die Zuordnung einer Nachricht zur Reaktion auf die Nachricht
+erst zur Laufzeit aufgelöst, dann wird dies auch späte Bindung genannt.
+
+### Feedback
+
+Verschiedene Objekte kommunizieren über einen
+Nachricht-Antwort-Mechanismus, der zu Veränderungen in den Objekten führt
+und neue Nachrichtenaufrufe erzeugt. Dafür steht die Kopplung als Index
+für den Grad des Feedback.
+
+### Vererbung
+
+Vererbung heißt vereinfacht, dass eine abgeleitete Klasse die Methoden und
+Attribute der Basisklasse ebenfalls besitzt, also „erbt“. Somit kann die
+abgeleitete Klasse auch darauf zugreifen. Neue Arten von Objekten können
+auf der Basis bereits vorhandener Objekt-Definitionen festgelegt werden. Es
+können neue Bestandteile hinzugenommen werden oder vorhandene überlagert
+werden. Wird keine Vererbung zugelassen, so spricht man zur Unterscheidung
+oft auch von objektbasierter Programmierung.
+
+## Everything is an Object!
+
+### Die Basics
+
+ "Alles sind Objekte".class # String
+ 6.class # Fixnum
+ (5.0).class # Float
+
+ # achja ...
+ "test".class.class # Class
+
+ # Alles sind Objekte?! Ja alles!
+ 7.+(7)
+ m = 7.method('+')
+ m.class
+ m.call(7)
+ # ... Wirklich alles ist ein Objekt!
+
+ # Wissenwertes: Call-by-Reference
+ def modify_arg(arg)
+ arg.clone << rand(40)
+ end
+ arg << rand(40)
+ # Kann mit der +clone+ Methode verhindert werden.
+ array = [12,34,54]
+ modify_arg(array)
+ array
+
+### Dynamische Typisierung und starke Bindung
+
+ my_string = "7"
+ my_string.class # String
+
+ number = 7
+ number.class # Fixnum
+
+ number + my_string # TypeError
+ number.to_s + my_string # '7hello World' -> Explizites Casten nötig
+
+ # in der irb ...
+ self.class
+ # -> Es gibt immer ein 'Root'-Objekt
+
+### Strings
+
+ # vielleicht so ...
+ anzahl_schafe = 6
+ new_string = 'Ich habe ' + anzahl_schafe.to_s + ' Schafe.'
+ # ... oder besser so!
+ new_string = "Ich habe #{anzahl_schafe} Schafe."
+
+### Symbole
+
+ # Symbole als andere Repräsentation von Strings
+ jedi1 = {"firstname" => "Anikin", "lastname" => "Skywalker", "rank" => "Jedi Padawan"}
+ jedi2 = {"firstname" => "Obi-Wan", "lastname" => "Kenobi", "rank" => "Jedi High General"}
+ jedi3 = {:firstname => "Qui-Gon", :lastname => "Jinn", :rank => "Jedi Master"}
+ jedi4 = {:firstname => "Mace", :lastname => "Windu", :rank => "Jedi Master"}
+
+ object_ids_of_keys = []
+
+ jedi1.keys.each { |key| object_ids_of_keys << key.object_id }
+ jedi2.keys.each { |key| object_ids_of_keys << key.object_id }
+
+ puts object_ids_of_keys.uniq # => 85690, 85750, 85810, 85470, 85530, 85590
+ object_ids_of_keys = []
+
+ jedi3.keys.each { |key| object_ids_of_keys << key.object_id }
+ jedi4.keys.each { |key| object_ids_of_keys << key.object_id }
+
+ puts object_ids_of_keys.uniq # => 109858, 109938, 110018
+
+### Datenstrukturen
+
+ # In Arrays liegen die Daten sortiert und lassen sich über
+ # ihren Index abrufen
+ an_array = [1, 2, 3, 4]
+ an_array[1]
+ an_array_of_words = %w(Geige Gitarre Violine Bass) # so ...
+ an_array_of_words = ["Geige", "Gitarre", "Violine", "Bass"] # ... oder so
+ an_array_of_words[2]
+
+ # In einem Hash liegen die Daten unsortiert und lassen sich
+ # über ihren +key+ abrufen
+ an_hash = { :author => "Dave Thomas", "title" => "Programming Ruby" } # Keys können sowohl Symbol als auch String sein
+ an_hash[:author]
+ an_hash['title']
+
+#### Arbeiten mit Datenstrukturen
+
+ # Iteratoren über Datenstrukturen (oder einfach alles was +Enumerable+ implementiert)
+ an_hash.each { |k,v| puts "Der Key ist: #{k} und der Value ist #{v}" }
+ an_array_of_words.each { |w| puts w }
+
+### Klassen
+
+ class Rectangle
+
+ attr_accessor :width, :height
+
+ # attr_reader :width
+ # attr_writer :height
+
+ # Konstruktor mit Parametern
+ def initialize(width, height)
+ # Zwei Instanzvariablen
+ @width, @height = width, height # Mehrfachzuweisung ;-)
+ end
+
+ # # Getter/Setter - non-Ruby Style
+ # def width
+ # @width # Es wird IMMER der letzte Wert zurückgegeben, daher oft kein +return+ noetig!
+ # end
+ #
+ # def width=(width)
+ # @width = width
+ # end
+ #
+ # def height
+ # @height
+ # end
+ #
+ # def height=(height)
+ # @height = height
+ # end
+
+ def area
+ height * width
+ end
+
+ def to_s
+ "This is a #{self.class.name} with the dimension: #{width}x#{height}"
+ end
+
+ protected
+
+ def meine_protected_methode
+ puts "protected"
+ end
+
+ private
+
+ def meine_private_methode
+ puts "private"
+ end
+
+ end
+
+ # - Es gibt keine abstrakten Klassen!
+ # - Es gibt aber Vererbung ...
+ class Square < Rectangle
+ def initialize(width)
+ super(width, width)
+ end
+
+ def width=(width)
+ self.height = width
+ self.width = width
+ end
+
+ private
+ attr_writer :height
+ end
+
+### Variablen
+
+ class String
+ def play
+ puts "playing song: #{self} ..."
+ end
+ end
+
+ # Verwendung von Klassen-, Instanzvariablen und Konstanten
+ class MusicPlayer
+
+ VENDOR_ID = "00:23:FE:89:EC:42"
+
+ def initialize(playlist)
+ @playlist = playlist
+ end
+
+ def self.overall_played_tracks
+ @@overall_played_tracks ||= 0
+ end
+
+ def play_track(track_number)
+ @playlist[track_number].play
+ @@overall_played_tracks += 1
+ end
+
+ end
+
+ puts MusicPlayer::VENDOR_ID # => 00:23:FE:89:EC:42
+
+ mp = MusicPlayer.new(["Duality", "B.Y.O.B"])
+
+ puts MusicPlayer.overall_played_tracks # => 0
+ mp.play_track(0) # => playing song: Duality ...
+ puts MusicPlayer.overall_played_tracks # => 1
+ mp.play_track(1) # => playing song: B.Y.O.B ...
+ puts MusicPlayer.overall_played_tracks # => 2
+
+### Mixins
+
+ # Über Modules können zusätzliche Funktionalitäten
+ # in eine Klasse "gemixt" werden.
+ module MultiplierMixin
+ def multiply(times)
+ data * times
+ end
+ end
+
+ class MyString
+ include MultiplierMixin
+
+ def data
+ "foo"
+ end
+ end
+
+ class MyInteger
+ include MultiplierMixin
+
+ def data
+ 23
+ end
+ end
+
+ str = MyString.new
+ puts str.multiply(3) # => foofoofoo
+
+ int = MyInteger.new
+ puts int.multiply(3) # => 69
+
+###
+
+### Schleifen und Bedingungen
+
+ for i in 1...10
+ puts i
+ end
+
+ 10.times {|i| puts i}
+
+ a = rand(2)
+ if a == 0
+ puts "Das war eine Null"
+ else
+ puts "Das war keine Null"
+ end
+
+ puts a == 0 ? "Das war eine Null" : "Das war keine Null"
+
+ while line = gets
+ puts "Ihre Eingabe: #{line}"
+ end
+
+### Blöcke und Procs
+
+Einfach:
+
+ my_proc = Proc.new { puts 'hello world' } # Ohne Uebergabewert
+ my_proc.class
+ my_proc.call
+
+ my_proc = Proc.new { |to| puts "hello #{to}" } # Mit Uebergabewert
+ my_proc.call 'ASS Kurs'
+
+ # Sicherstellen, dass etwas vorher und hinterher gemacht wird.
+
+ do_something = Proc.new { puts "Irgendwas was gemacht wird." }
+
+ def ensure_important_stuff(any_proc)
+ puts "Sehr wichtige Sache, die vorher gemacht werden muss!"
+ any_proc.call
+ puts "Sehr wichtige Sache, die nachher gemacht werden muss!"
+ end
+
+Ein bisschen komplexer:
+
+ # Proc Objekt
+ class Array
+ def my_find(find_proc)
+ for i in 0...size
+ return self[i] if find_proc.call(self[i])
+ end
+ return nil
+ end
+ end
+
+ puts [1,3,5,6,7].my_find Proc.new { |e| e.modulo(2) == 0 }
+
+ # Block to Proc
+ class Array
+ def my_find(&find_block)
+ for i in 0...size
+ return self[i] if find_block.call(self[i])
+ end
+ return nil
+ end
+ end
+
+ puts [1,3,5,6,7].my_find { |e| e.modulo(2) == 0 }
+
+ # Block via yield
+ class Array
+ def my_find
+ for i in 0...size
+ return self[i] if yield self[i]
+ end
+ return nil
+ end
+ end
+
+ puts [1,3,5,6,7].my_find { |e| e.modulo(2) == 0 }
+
\ No newline at end of file
diff --git a/site/handson/testing.md b/site/handson/testing.md
index 5af9076..5408b69 100644
--- a/site/handson/testing.md
+++ b/site/handson/testing.md
@@ -115,6 +115,7 @@ specific Assertions") finden lassen.
Beispiel:
+ !!!yaml
# Dieser Datensatz muss in der test/fixtures/articles.yml Datei stehen
# Auch gelten ganz normale Kommentare
article_for_test: # Name des Datensatzes
@@ -141,6 +142,7 @@ erwähnt sein, sich mit der Thematik näher zu beschäftigen.
Beispiel:
+ !!!ruby_on_rails
class ArticleTest < ActiveSupport::TestCase
test "should have a valid title" do
article = Article.new(:title => '', :author => users(:jessie))
diff --git a/site/handson/validations.md b/site/handson/validations.md
index 3527339..fdc4fbf 100644
--- a/site/handson/validations.md
+++ b/site/handson/validations.md
@@ -35,6 +35,7 @@ dann sollen ihn entsprechende Fehler darauf aufmerksam machen.
### Einige Standardvalidierungen:
+ !!!ruby_on_rails
validates_presence_of :first_name, :last_name # Attribute 'first_name' und 'last_name' dürfen nicht leer sein
validates_uniqueness_of :username # Attribut 'username' muss einzigartig sein
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i # Attribut 'email' muss dem angegebenen Format entsprechen
@@ -47,6 +48,7 @@ dann sollen ihn entsprechende Fehler darauf aufmerksam machen.
### Eigene Validierung:
+ !!!ruby_on_rails
def validate
errors.add(birthday, "Who are you? Methusalem?") unless birthday < Time.parse("01-01-1900")
end
\ No newline at end of file
diff --git a/site/index.md b/site/index.md
index 042be4b..e403a0e 100644
--- a/site/index.md
+++ b/site/index.md
@@ -1,6 +1,8 @@
# ASS Anfängerkurs
-
+Hier stellen wir alle relevanten Kursmaterialien wie Hands-On Aufgaben und
+Musterlösungen für die Teilnehmer des ASS Anfängerkurses "Ruby on Rails"
+bereit. Der Kurs findet statt vom *07.04. – 09.04.2010*.
## Hands-On
@@ -23,4 +25,16 @@
## Musterlösungen
-*Not yet ... ;-)*
\ No newline at end of file
+*Not yet ... ;-)*
+
+## Authors
+
+ * Andreas Bade ([andi@galaxycats.com](mailto:Andreas Bade (andi@galaxycats.com)))
+ * Dirk Breuer ([dirk@galaxycats.com](mailto:Andreas Bade (andi@galaxycats.com)))
+
+## Download
+
+Alle Handouts und Musterlösungen können als
+[ZIP](http://github.com/galaxycats/ASS-Beginner/zipball/master "zip") oder
+[tar](http://github.com/galaxycats/ASS-Beginner/tarball/master "tar") Archive
+runtergeladen werden.
diff --git a/templates/base.html b/templates/base.html.erb
similarity index 65%
rename from templates/base.html
rename to templates/base.html.erb
index 16f1872..06835ad 100644
--- a/templates/base.html
+++ b/templates/base.html.erb
@@ -2,7 +2,9 @@
- <%= @options[:config]["title"] %>
+ ASS Anfänger Kurs – Kursmaterial
+
+