Ruby-Server für den ELUG-Vortrag

Ein normaler mehrzeiliger Kommentar.
Und noch eine Zeile

2. Abschnitt

3. Abschnitt xx bb

require 'socket'

class Server
  def initialize(host = "localhost", port = 8877, root = File.dirname(__FILE__))
    @host = host
    @port = port
    @root = root
    @mimeTypes = { ".html" => "text/html", ".rb" => "text/html", ".wiki" => "text/html",
                   ".jpg" => "image/jpeg", ".png" => "image/png" }
    @mimeTypes.default = "text/plain"
  end

  def start
    server = TCPServer.new(@host, @port)
    puts "serving #{@root} on http://#{@host}:#{@port}"
    while (session = server.accept)
      request = session.gets
      puts %Q(request: #{request})
      header = request.split
      case header[0]
      when "GET"
        get(session, header[1])
      else
        default(session, request)
      end
      session.close
    end
  end

  def get(session, path)
    unless serve(session, path)
      notFound(session, path)
    end
  end

  def notFound(session, path)
    session << "HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n"
    session << <<-EOT
<html>
<head>
<title>File Not Found</title>
</head>
<body>
<h1>File Not Found:</h1>
<a href="#{path}">
  #{path}
</a>
<hr />
#{Time.now}
</body>
</html>
    EOT
  end

  def default(session, request)
    session << "HTTP/1.1 200/OK\r\nContent-type: text/html\r\n\r\n"
    session << <<-EOT
<html>
<head>
<title>Default Response</title>
</head>
<body>
<h1>Request:</h1>
#{request.gsub(" ", "<br />")}
<hr />
#{Time.now}
</body>
</html>
    EOT
  end

  def serve(session, path)
    abs = @root + path.split("?")[0]
    if File.directory?(abs)
      abs << "/" unless abs[-1] == "/"[0]
      abs << "index.html"
    end
    if File.exists?(abs)
      serveFile(session, abs)
      true
    else
      false
    end
  end

  def serveFile(session, absPath)
    writeHeader(session, header(absPath))
    File.open(absPath) do |file|
      session.write file.read
    end
  end

  def writeHeader(session, header)
    hd = ""
    hd << header.join("\r\n") << "\r\n\r\n"
    p hd
    session << hd
  end

  def header(name)
    header = ["HTTP/1.1 200/OK"]
    header << "Content-type: #{contentType(name)}"
  end

  def contentType(name)
    @mimeTypes[suffix(name)]
  end

  def suffix(name)
    name =~ /(\.\w+)/
    $1
  end

  attr_reader :root
end

class RubyServer < Server
  def initialize(*args)
    super
  end

  def serveFile(session, absPath)
    case suffix(absPath)
      when '.rb' then serveRb(session, absPath)
      when '.wiki' then serveWiki(session, absPath)
      else super
    end
  end

  def serveRb(session, absPath)
    writeHeader(session, header(absPath))
    session << RubyReader.new(absPath).html
  end

  def serveWiki(session, absPath)
    writeHeader(session, header(absPath))
    File.open(absPath) do |file|
      session.write file.read
    end
  end

  def head(session, absPath)
    session << <<-EOT
<html>
<head>
<title>#{absPath}</title>
<link type="text/css" rel="stylesheet" href="style.css">
</head>
<body>
    EOT
  end

  def foot(session, absPath)
    session << <<-EOT
</body>
</html>
    EOT
  end
end

class RubyReader
  def initialize(source)
    @source = source
    @comment = false
    @first = true
    @formatted = ""
    @skip = true
    @start = true
    @paragraph = false
    read(source)
  end

  attr_reader :formatted

  def html
    return <<-EOT
<html>
<head>
<title>#{@source}</title>
<link type="text/css" rel="stylesheet" href="style.css">
</head>
<body>
#{@formatted}
</body>
</html>
    EOT
  end

  private

  def read(source)
    File.open(source) do |file|
      file.each_line do |line|
        processLine(line)
      end
    end
  end

  def processLine(line)
    return if @skip and line.strip.empty?
    if @start
      @comment = line =~ /^=begin/
      @start = false
      return if @comment
    end
    if @comment
      @comment = processCommentLine(line)
    else
      @comment = processCodeLine(line)
    end
  end

  def processCommentLine(line)
    if @first
      @first = false
      @skip = false
    end
    case line
    when /^=end/
      paragraphEnd
      @first = true
      @skip = true
      return false
    when /^=result\s+/
      paragraphEnd
      processResult($'.chomp)
    when /^\*\*\s+/
      @formatted << "<h1>#{quote($'.chomp)}</h1>\n"
    when /^==\s+/
      @formatted << "<h2>#{quote($'.chomp)}</h2>\n"
    when /^--\s+/
      @formatted << "<h3>#{quote($'.chomp)}</h3>\n"
    when /^\s*$/
      paragraphEnd
    else
      unless @paragraph
        @formatted << "<p>"
        @paragraph = true
      end
      @formatted << link(quote(line))
    end
    true
  end

  def paragraphEnd
    if @paragraph
      @formatted << "</p>"
      @paragraph = false
    end
  end

  def processCodeLine(line)
    if @first
      @first = false
      @skip = false
      @formatted << %Q(<pre class="code">\n)
    end
    if line =~ /^=begin/
      @formatted << %Q(</pre>\n)
      @first = true
      return true
    else
      @formatted << quoteCode(line)
    end
    false
  end

  def processResult(cmd)
    cmd.gsub!(/self\.rb/, @source)
    result = `#{cmd}`
    @formatted << <<-EOT
<div class="subject">Ergebnis:</div>
<pre class="result">
#{quoteCode(result)}
</pre>
    EOT
  end

  def quoteCode(line)
    line.gsub(/&/, "&amp;").gsub(/</, "&lt;").gsub(/>/, "&gt;")
    # line
  end

  def quote(line)
    line.gsub(/ä/, "&auml;").
         gsub(/ö/, "&ouml;").
         gsub(/ü/, "&uuml;").
         gsub(/Ä/, "&Auml;").
         gsub(/Ö/, "&Ouml;").
         gsub(/Ü/, "&Uuml;").
         gsub(/ß/, "&szlig;")
  end

  def link(line) 
    line.gsub(/(\w+\.rb)/, %q(<a href="\1">\1</a>))
  end
end

def export(dir)
  Dir["*.rb"].each do |file|
    htmlFile = dir + "/" + file.gsub(/(\w+)\.rb/, '\1.html')
    puts "#{file} ==> #{htmlFile}"
    File.open(htmlFile, "w") do |io|
      io.write(RubyReader.new(file).html.gsub(/href="(.+)\.html"/, 'href="\1.html"'))
    end
  end
end

if ARGV.size == 2 && ARGV[0] == "--export"
      export(ARGV[1])
else
  RubyServer.new(*ARGV).start
end

test.rb test.rb test.rb