AWE10: Einführung in Ruby

Universität Bremen 2010-01-20

Prof. Dr.-Ing. Carsten Bormann

cabo@tzi.org

Anwendungsentwicklung: Revolution

Dynamische Web-Inhalte (1)

Dynamische Web-Inhalte (2)

Rails (1)

Rails (2)

Typen und Programmiersprachen

Dynamische Programmiersprachen (1)

Dynamische Programmiersprachen (2)

Dynamische Programmiersprachen (3)

Dynamische Programmiersprachen (4)

Dynamische Programmiersprachen (5)

Ruby

Wie entstand Ruby?

Ruby is a language designed in the following steps:

(Yukihiro Matsumoto, Mon, 13 Feb 2006 13:43:02 +0900)

Ruby ist nicht perfekt (1)

Warum Ruby noch nicht das Ende jeder Entwicklung ist:

Ruby ist nicht perfekt (2)

Aber…

Warum Ruby die Welt verändern wird (1)

Warum Ruby die Welt verändern wird (2)

Ruby ist angekommen:

TIOBE-Index: Mißt Verfügbarkeit von Programmierern, Kursen, Diensten

Top-Ten:

“Ruby makes me happy”

Ruby makes me happy because it allows me to write beautiful code.

Aesthetics are strongly linked to enjoyment. It’s not just about “getting the job done”. It’s also about being satisfied with the way the job is getting done — being motivated to improve the way the job is getting done, and to take pride in its execution.

I simply can’t do that with ugly code. Ruby allows me to escape the ugliness. Thus, Ruby allows me to be happy about programming. Happiness leads to motivation. Motivation leads to productivity.

—David Heinemeier Hansson (creator of Rails)

“Designed by a programmer”

Ruby’s the only language I’ve ever used that feels like it was designed by a programmer, and not

—William Morgan

Ruby auf einer Folie

variable methode
Konstante Klassenname
@instanzvariablen
false true
nil
"string" 'string'
:symbol
[1, :two, "three"]
{:action => "edit", :id => 3}
/[A-Za-z][_a-z0-9]*/
"Carsten".length     –1962.abs
user = User.new
user.vorname = "Carsten"
user.vorname.length
user = User.new("Carsten")
cart.add_line_item(purchase)
class User < Human
   attr_reader :vorname
   def initialize(vorname)
      @vorname = vorname
   end
   def say_goodnight(niceness = "Dear")
      print "Good night, #{niceness} #{@vorname}!"
   end
end

Arbeiten mit Ruby

Ein Ruby-Programm ist eine Folge von Ausdrücken

Ausdrücke kann man interaktiv auswerten

Ruby und Emacs (1)

(autoload 'ruby-mode "ruby-mode" "Ruby editing mode." t)
(add-to-list 'auto-mode-alist '("\.r\\(b\\¦ake\\)$" . ruby-mode))
(add-to-list 'auto-mode-alist '("[Rr]akefile\\'" . ruby-mode))
(add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode))
(add-hook 'ruby-mode-hook
      '(lambda () (ruby-electric-mode)))
(autoload 'ruby-electric-mode "ruby-electric" "Ruby electric editing mode." t)
(autoload 'run-ruby "inf-ruby" "Run an inferior Ruby process")
(autoload 'inf-ruby-keys "inf-ruby" 
  "Set local key defs for inf-ruby in ruby-mode")
(add-hook 'ruby-mode-hook
      '(lambda ()
         (inf-ruby-keys)))
(setq ruby-program-name "irb --inf-ruby-mode -f")

Ruby und Emacs (2)

Ruby-Mode: Wie erwartet

Ruby-Electric-Mode: Erspart etwas Tipp-Arbeit

Inferior-Ruby-Mode:

Basis-Syntax

5
a = 1
b = 2; nil

Programm = Folge von Ausdrücken

d = 4 + 5 +
    6 + 7
e = 8 + 9 \
    + 10

Zeilenende beendet Ausdruck, es sei denn

=begin rdoc
Kaufe eine Cola mit Euros
=end
def buy_coke # parameterlos
...

Kommentare:

Objekte, Instanzvariablen, Methoden (1)

Werte aller Typen sind Objekte.
class User
  def initialize(x)
    @vorname = x
  end
  def vorname
    @vorname
  end
  def vorname=(x)
    @vorname = x
  end
end

Der Inhalt von Objekten ist nur über Methoden erreichbar

Objekte, Instanzvariablen, Methoden (2)

Abkürzung: Attribute (Instanzvariable + gleichnamige Methode(n))

class User
  def initialize(x)
    @vorname = x
  end

  # Schreibbares Attribut (true)
  attr :vorname, true

  # Alternative Schreibweise:
  attr_accessor :vorname

end

Ruby definiert Klassenmethoden, um setter-/getter-Methoden automatisch zu definieren:

class Name
  attr attribute [, writable ]
  attr_reader attribute [, attribute ]...
  attr_writer attribute [, attribute ]...
  attr_accessor attribute [, attribute ]...
end

Numerische Typen (1)

123456 → 123456
0d123456 → 123456
123_456 → 123456
–543 → –543
0xaabb → 43707
0377 → 255
–0b10_1010 → –42
123_456_789_123_456_789
  → 123456789123456789

Numerische Typen:

?a → 97
?\n → 10
?\C-a → 1

(Ruby 1.8:) Einzelne Zeichen sind auch nur Ganzzahlen:

Numerische Typen (2)

Viele Operationen werden wie Methoden aufgerufen:

–1.3.abs → 1.3 53.to_s → “53”
–1.3.zero? → false 53.to_s(16) → “35”
?a.chr → “a”
97.chr → “a”

Manche Methoden haben Blöcke:

3.times { puts "Hurra!" }
3.times do
  puts "Hurra!"
end

Numerische Typen (3)

12.34 → 12.34
–0.1234e2 → –12.34
1234e–2 → 12.34

Numerische Typen (Fortsetzung):

In der Standardbibliothek gibt es noch

require 'bigdecimal'
require 'bigdecimal/math'
include BigMath
BigMath::PI(200)

→ #<BigDecimal:481e8,’0.3141592653 5897932384 6264338327 9502884197 1693993751 0582097494 4592307816 4062862089 9862803482 5342117067 9821480865 1328230664 7093844609 5505822317 2535940812 8481117450 2841027019 3852110555 9644622948 9549303819 6442881097 5665930147 8208315213 4043E1’,240(576)>

Numerische Typen (4)

Operationen auf Zahlen wie in C:

Exponentiation ** 2**74 → 18889465931478580854784
Unäre Operationen ~ + – ~1 → –2; +1 → 1; –1 → –1
Mul/Div/modulo * / 2*2.0 → 4.0; 5/3 → 1;
5/3.3 → 1.5152; 3.05%1.5 → 0.0499
Plus/Minus + – 1+1 → 2; 1.3–3 → –1.7
Shift << >> 2<<12 → 8192; 4711>>2 → 1177
Bitweises Und x%x 4711&4712 → 4704
Bitweises Oder ¦ ^ 4711¦4712 → 4719; 4711^0x200 → 4199

Numerische Typen (5)

Infix-Operatoren sind Abkürzung für Methodenaufrufe:

a = 1
a + 1 → 2
a.+(1) → 2
1 + 1 → 2
1.+(1) → 2

Unäre Operatoren + und – heißen zur Unterscheidung anders:

(Leere Klammern kann man weglassen, allerdings hat der Parser von irb in diesem Fall Probleme damit.)

a = 1
–a → –1
a.–@() → –1
a.–@ → –1

Vergleiche (1)

== 《!=》 gleicher Wert
=== “passt zu” (unsymmetrisch!)
<=> < (–1), == (0), oder > (+1)?
<, <=, >, >= meist definiert auf <=>
=~ 《!~》 RE-Match
eql? gleicher Wert ohne Konvertierung
equal? identisch

!= ist Negation von ==, ! ist Negation von =

Vergleiche (2)

class MyWidget
  attr_reader :str
  def <=>(other)
    str <=> other.str
  end
  include Comparable
end

Vergleiche (3)

module Comparable
  def ==(other)
    self.object_id == other.object_id ¦¦ (self <=> other) == 0
  end

  def >(other); (self <=> other) > 0; end
  def >=(other); (self <=> other) >= 0; end
  def <(other); (self <=> other) < 0; end
  def <=(other); (self <=> other) <= 0; end

  def between?(min, max)
    (min <= self) && (self <= max)
  end
end

Comparable ist ein Mixin: Ein Modul, dessen Konstanten und Methoden mit include Bestandteil der inkludierenden Klasse werden.

Ranges (1)

>> for a in 1..3
>>   puts a
>> end
1
2
3
→ 1..3
>> for a in 1...3
>>   puts a
>> end
1
2
→ 1...3

Anfang und Ende eines Bereiches in einem Datentyp

a..b entspricht mathematisch [a, b]
a...b entspricht mathematisch [a, b[

(1..3).class → Range

Ranges (2)

Operator === prüft, ob der Operand zum Objekt paßt:

(0...18) === –1 → false
(0...18) === 0 → true
(0...18) === 17 → true
(0...18) === 18 → false
(0..18) === –1 → false
(0..18) === 0 → true
(0..18) === 17 → true
(0..18) === 18 → true
(0..18) === 19 → false

Besondere Klassen (1)

Folgende Klassen haben jeweils genau eine (nicht mutierbare) Instanz:

In Abfragen sind false und nil falsch, alles andere wahr!

Besondere Klassen (2)

TRUE = true
class TrueClass
  def to_s;  "true";  end

  def ¦(x);  true;  end

  def &(x)
    (x.nil? or x == false) ? false : true
  end

  def ^(x)
    (x.nil? or x == false) ? true : false
  end
end
FALSE = false
class FalseClass
  def to_s;  "false";  end

  def &(x);  false;  end

  def ¦(x)
    (x == false
     or x.nil?) ? false : true
  end

  alias :^ 
end

Strings (1)

Zeichenketten sind wichtiger Grundtyp (im Gegensatz zu Zeichen!)

Basis: Folge von 8-bit-Bytes

Literale sind entweder einfachquotiert (nur \’ und \\)...

'hello' → hello
'a backslash \'\\\'' → a backslash ’\’
%q/simple string/ → simple string
%q(nesting (really) works) → nesting (really) works
%q no_blanks_here → no_blanks_here

Strings (2)

oder doppelquotiert (sei a = 123):

"\123mile" → Smile
"Say \"Hello\"" → Say “Hello”
%Q!"I said 'nuts'," I said! → “I said ‘nuts’,” I said
%Q["I said 'nuts'," I said] → “I said ‘nuts’,” I said
%["I said 'nuts'," I said] → “I said ‘nuts’,” I said
"Try #{a + 1}, not #{a – 1}" → Try 124, not 122
Q{Try #{a + 1}, not #{a – 1}} → Try 124, not 122
→ Try 124, not 122
%{...#{ a = 1; b = 2; a + b }...} → ...3…

Strings (3)

HERE-Dokumente:

a1:
Double quoted here document.
It is Sun Jan 21 20:16:03 +0100 2007

a2:
   This is single quoted.
   The above used #{Time.now}
a1 = <<HERE
Double quoted \
here document.
It is #{Time.now}
HERE
a2 = <<–'THERE'
   This is single quoted.
   The above used #{Time.now}
   THERE

Konkatenation: 'and "a\123" = ' "a\123" → and “a\123” = “aS”

Strings (4)

Ruby 1.9:

?a → “a”
"a"[0] → “a”
"a"[1] → nil
"ü"[0] → “ü”
"ü"[1] → nil
"a".length → 1
"ü".length → 1
"水".length → 1
"a".encoding → #<Encoding:UTF-8>

Ruby 1.8:

?a → 97
"a"[0] → 97
"a"[1] → nil
"ü"[0] → 195
"ü"[1] → 188
"ü"[2] → nil
"a".length → 1
"ü".length → 2
"水".length → 3

Strings (5)

1.8: UTF-8 scheint durch

s = "Müller"; s[2] = 164; s[4] = ?z; puts s ☞ Mälzer

1.9: Strings sind Zwitter von Zeichenketten und Bytefolgen

"abcü水".bytes.to_a → [97, 98, 99, 195, 188, 230, 176, 180]
"abcü水".codepoints.to_a → [97, 98, 99, 252, 27700]
"a".ord → 97
"水".ord → 27700
97.chr → “a”
27700.chr("UTF-8") → “水”

Strings (6)

Mutieren:

s = "Hello"
s << " "
s << "world"

s[1] = "aa"
#s[7..11] = "Welt"
s[7..-1] = "Welt"

Operationen auf Strings

"a" + "b" → “ab”
"a" + 1
"a" + 1.to_s → “a1”
"a#{1}" → “a1”
"ab" * 3 → “ababab”
"a%sb" % 3 → “a3b”
"a%sb%d" % ["x", 2] → “axb2”

Symbole (1)

Symbole sind spezielle konstante Strings:

Interpolation (sei a = “cat”):

:’catsup’ → :catsup
%s{catsup} → :catsup
:”#{a}sup” → :catsup
:’#{a}sup’ → :”\#{a}sup”
%s{#{a}sup} → :”\#{a}sup”

Schreibweise mit Doppelpunkt:

:Object
:my_variable
:”Ruby rules”

Symbole (2)

people = Hash.new
people[:nickname] = 'Matz'
people[:language] = 'Japanese'

keystring = 'last name'
people[keystring.intern] = 'Matsumoto'

people[:'last name']'Matsumoto'

Reguläre Ausdrücke (1)

Reguläre Ausdrücke dienen zum Vergleichen und Zerlegen von Strings.

/pattern/
/pattern/options
%r{pattern}
%r{pattern}options
Regexp.new( 'pattern' [ , options ] )

Reguläre Ausdrücke wissen über Unicode (UTF-8) Bescheid!

Reguläre Ausdrücke (2)

"eins zwei drei".scan(/./)
→ [“e”, “i”, “n”, “s”, ” ”, “z”, “w”, “e”, “i”, ” ”, “d”, “r”, “e”, “i”]
"eins zwei drei".scan(/\w+/) → [“eins”, “zwei”, “drei”]
"Lieschen Müller, Frank Meyer".split(/,\s+/)
→ [“Lieschen M\303\274ller”, “Frank Meyer”]
md = 134.102.218.46.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
→ #<MatchData:0×4bdc00>

Reguläre Ausdrücke lassen sich auch als Index benutzen:

Ausgabe:
schen
Lieschen Mueller
a = "Lieschen Müller"
puts a[/.ch\S+/]
a[/ü/] = "ue"
puts a

Arrays

Arrays beginnen immer bei Index 0 und können dynamisch wachsen.

Literale:

[]
[1, 2, 3]
[Time.now, Process::Sys.getuid, Process.times.utime]
→ [Sun Jan 21 21:11:30 +0100 2007, 501, 41.51]
a = %w{The rat sat on the mat}
→ [“The”, “rat”, “sat”, “on”, “the”, “mat”]
a[2..–2] → [“sat”, “on”, “the”]
a[4711] → nil

Hashes

Hashes werden mit einem beliebigen Datenobjekt indiziert.
(Andere Namen für Hashes: Assoziative Arrays, Dictionaries.)

Literale:

colors = { "red" => 0xf00,
  "green" => 0x0f0,
  "blue" => 0x00f
}
puts colors["red"].to_s(16)
Ausgabe: f00
{}
{ "red" => 0xf00, "green" => 0x0f0,
  "blue" => 0x00f }

Indexe (Keys) sollten sich nicht ändern.

Spezialfall: Strings als Keys werden beim Anlegen kopiert.

Andere wichtige Datentypen

Bedingte Ausführung (1)

ausrede = if nutzer.alter < 18
  "minderjährig"
elsif nutzer.konto < 1000
  "kein Geld"
else
  nil
end
if nutzer.alter < 18
  puts "minderjährig"
elsif nutzer.konto < 1000
  puts "kein Geld"
else
  auto.kaufen(nutzer)
end

Es gibt keine Anweisungen, nur Ausdrücke!

Bedingte Ausführung (2)

Kurzform für einfache bedingte Ausführung:

Statt:

if User.count == 0
  return false
end

auch:

return false if User.count == 0

case/when (1)

Weniger üblich:

leap = case
when year % 400 == 0
  true
when year % 100 == 0
  false
else
  year % 4 == 0
end

Übliches case:

case input
when 'q'
  quit
when /w (.*)/
  write($1)
else
  error
end

case/when (2)

=== auf Class
(mal nicht o-o):
case shape
when Square, Rectangle
# ...
when Circle
# ...
when Triangle
# ...
else
# ...
end

Nützlich für den Vergleich mit === sind auch Ranges:

text = case age
when 0...18
  "minderjährig"
when 18...21, 65..999
  "Gefahrenzuschlag"
else
  "Normalpreis"
end

Schleifen

Bedingung ist Ausdruck
wie jeder andere:
while line = gets
  puts line
end

while begin
    print "Eingabe: "
    line = gets
  end
  puts line
end
i = 0
while i < 3
  puts "Hurra"
  i += 1
end

until ist Gegenteil von while

Enumeration (1)

3.times do
  puts "Hurra"
end

Blöcke können Argumente haben:

(1..3).each do ¦
  puts "Hurra #{i}"
end

("aa".."zz").each do ¦ix¦
  buchreihe[ix].anlegen
end
3.times do ¦
  puts "Hurra #{i+1}"
end
["Fritz", "Horst", "Ernst"].each do ¦nm¦
  puts "Hallo #{nm}!"
end

Enumeration (2)

class Integer

  def times
    i = 0
    while i < self
      yield i
      i += 1
    end
    self
  end

end

Wie implementiert man eine Methode, die Argumente an Blöcke übergibt?

Enumeration (3)

def mean(array)
  array.inject(0) do ¦sum, 
    sum += x
  end / array.size.to_f
end
class Array
  def mean
    inject(0) do ¦sum, 
      sum += x
    end / size.to_f
  end
end
def mean(array)
  sum = 0
  array.each do ¦
    sum += i
  end
  return sum / array.size.to_f
end

Beispiel: MP3-Tags

 

ARGV.each do ¦fn¦
  if t = parse_id3(fn)
    puts "#{fn}:"
    t.zip(NAMES).each do ¦v, 
      puts "  #{n}: #{v}"
    end
  else
    puts "#{fn}: No tags."
  end
end
NAMES = %w(track artist album year comment genre)
FORMAT = 'Z30Z30Z30Z4Z30C'

def parse_id3(mp3_file)
  open(mp3_file) do ¦
    f.seek(–128, File::SEEK_END)
    if f.read(3) == "TAG"
      return f.read(125).unpack(FORMAT)
    end
  end
  return nil
end

Beispiel: EyeTV-Library

#!/usr/bin/env ruby
require "rexml/document"
ARGV[0] ¦¦= "#{ENV['HOME']}/Library/EyeTV Archive"
ARGV.each do ¦di¦
  puts "#{di}:"
  Dir["#{di}/*/*.eyetvp"].each do ¦pf¦
    d = REXML::Document.new(File.open(pf))
    title = d.elements["//string[preceding-sibling::key[.='title']]"].text
    du = `du -sh "#{File.dirname(pf)}"`.chomp
    du["\t#{di}/"] = '  '
    puts "#{du}: #{title}"
  end
end

Beispiel: Videos aus RSS-Feed laden

#!/usr/bin/env ruby
%w(rubygems hpricot open-uri simple-rss active_support pp).each do ¦
  require x
end
Dir.chdir("/Users/cabo/Movies/es")

feed = SimpleRSS.parse(open("http://www.ehrensenf.de/feed/atom/"))
links = feed.entries.map x.link}
vlinks = links.map li¦
  doc = Hpricot(open(li + "?vid=Quicktime"))
  (doc/"#videolinks a[@title]").map x['href']}
}
vlinks.flatten.select x != ''}.each { ¦vl¦
  pp `wget -N '#{vl}'`
}

Beispiel: Chat-Server (1)

require 'gserver'
class ChatServer < GServer
  def initialize(port=20606, host=GServer::DEFAULT_HOST)
    @clients = []
    super(port, host, Float::MAX, $stderr, true)
  end
  def serve(sock)
    ...
  end
end
server = ChatServer.new(*ARGV[0..1])
server.start(–1)
server.join

Beispiel: Chat-Server (2)

  def bc(m, sock=nil)
    (@clients[sock]).each { ¦ c.puts m }
  end
  def serve(sock)
    @clients << sock
    hostname = sock.peeraddr[2] ¦¦ sock.peeraddr[3]
    bc "#{hostname} has joined the chat.", sock
    while message = sock.gets and message !~ %r(^/q)
      bc "#{hostname}: #{message.chomp}", sock
    end
  ensure
    @clients.delete(sock)
    bc "#{hostname} has left the chat."
  end

Namen

variable methode
Konstante Klassenname
@instanzvariablen
@@klassenvariablen

Klassennamen sind Konstanten:

Range.class → Class

Class.methods – Object.methods → [“nesting”]

Besondere Methodennamen

x[3] ist eine Abkürzung für x.[](3)
x[3, 4, 5] ist eine Abkürzung für x.[](3, 4, 5)
x.a = 5 ist eine Abkürzung für x.a=(5)
x[3] = 5 ist eine Abkürzung für x.[]=(3, 5)
x[3, 4] = 5 ist eine Abkürzung für x.[]=(3, 4, 5)

Methodennamen können auf ! oder ? enden:

Operationen, die keine Methoden sind (1)

Bestimmte Operationen sind aus der Sprache definiert und können nicht als Methoden definiert werden:

Operationen, die keine Methoden sind (2)

¦¦ und && liefern nicht true und false, sondern den ersten Operanden, der ihr Ergebnis bestimmt. Das kann man auch in ¦¦= und &&= ausnutzen!

a = "Peter"
b = nil
a ¦¦ b
b ¦¦ a
b ¦¦= a

Variablen vs. Methoden

Variablenreferenzen und Methodenaufrufe sehen u.U. gleich aus.

a + 1 — Methodenaufruf oder Variablenreferenz?

Methoden und ihre Ergebnisse

def a
  1
  2
end

a   #=> 2

def b
  1
  return 2
  3
end

b   #=> 2

Methoden-Rümpfe sind Folgen von Ausdrücken

Wie bei jeder solchen Folge:
Der letzte Ausdruck wird zurückgeliefert

Ausnahme: return <Wert>

Methoden und ihre Argumente

def a(x, y)
  puts "x=#{x}, y=#{y}"
end

def b(*args)
  p args
end

a(1, 2)
a 1, 2
arr = [3, 4]
a *arr

b 1, 2
b(1, 2)
b(1, 2, *arr)
b 1, 2, 4 => 5, 6 => 7
b 1, 2, 4 => 5, 6 => 7, *arr

Parameter und Argumente: mit oder ohne Klammern

Freischwebende Hash-Bestandteile am Ende:
werden zu einem Hash kombiniert

Ein Sternchen („Splat“) vor einem:

Mehrfachzuweisungen

Für Zuweisungen gelten ähnliche Regeln wie bei der Parameterübergabe:

a, b = b, a
a, b = *arr
*arr = a, b

Sonderfall: Ein Array auf der rechten Seite einer Mehrfachzuweisung verhält sich wie ein splat:

a, b = arr

Blöcke als Argumente (1)

def a(x, y, &b)
  p b.class
  p b.call(x, y)
  $b = b
end

a(1, 2) do ¦m, 
  p [m, n]
  "na fein"
end

$b.call(3, 4)

Blöcke als Argumente (2)

repeat(2) do
  puts "Hello."
end
# Hello.
# Hello.
def repeat(n)
  n.times { yield } if block_given?
end

def repeat(n, &block)
  n.times { block.call } if block
end

def repeat(n, &block)
  n.times { yield } if block
end

Singleton-Methoden

class A
  attr_accessor :name
  def initialize(name)
    @name = name
  end
  def to_s
    @name
  end
end

a = A.new("fritz")
def a.normalize!
  @name.capitalize!
end
a.normalize!

Methoden können nicht nur für ganze Klassen definiert werden, sondern auch für einzelne Objekte:

class << a
 def normalize!
   @name.capitalize!
 end
end

Klassenmethoden (1)

class Regexp 
  def Regexp.is_valid?(str) 
    begin 
      compile(str) 
      valid = true 
    rescue RegexpError 
      valid = false 
    end 
  end 
end 
   
Regexp.is_valid? "Spitze!" → true
Regexp.is_valid? "Spitze)" → false
   

Klassenmethoden (2)

class A; end  ~  A = Class.new

class Module
  def <; ...superklasse?... end
  def ===; ...instanz von?... end
  def ancestors; ... end
  def attr; ... end
  def include; ... end
end
class Class
  include Module
  def new; ... end
  def superclass; ... end
end

Domänenspezifische Sprachen (DSL)

Beispiel: ActionMailer

require 'rubygems'
require 'action_mailer'
class SimpleMailer < ActionMailer::Base
  def simple_message(recipient)
     from 'leonardr@example.org'
     recipients recipient
     subject 'A single-part message for you'
     body 'This message has a plain text body.'
  end
end

Rake

make: Makefile ist eine DSL

Warum nicht das gleiche in Ruby? ➔ Rakefile

task :default => [:build, :test]

task :build => ["hello", "doc.pdf"]

SRC = FileList["*.c"]
OBJ = SRC.ext("o")

desc "Build hello out of the object files"
file "hello" => OBJ do
  sh "cc -o hello #{OBJ}"
end

file "main.o" => "header.h"

task :test do
  ruby "test/unittest.rb"
end

rule '.o' => '.c' do ¦
  sh "cc -c -o #{t.name} #{t.source}"
end

task "doc.pdf" do
  # ...
end
#

Gems (1)

gem list --remote

gem install rails

gem update
BlueCloth (1.0.0) junebug-wiki (0.0.27) rcodetools (0.4.1.0)
builder (2.0.0) libxml-ruby (0.3.8.4) RedCloth (3.0.4)
camping (1.5.180) libxslt-ruby (0.3.6) rmagick (1.14.1)
capistrano (1.3.1) markaby (0.5) rmail (0.17)
cgi_multipart_eof_fix (2.0.2) memcache-client (1.2.0) rspec (0.7.5.1)
cheat (1.2.1) memcache-client-stats (1.1.0) ruby-prof (0.4.1)
daemons (1.0.4) metaid (1.0) ruby2ruby (1.1.4)
diff-lcs (1.1.2) mime-types (1.15) rubyforge (0.4.0)
dr_nic_magic_models (0.8.1) mofo (0.1.1) rubygems-update (0.9.1)
emp (1.0.0) mongrel (0.3.14) RubyInline (3.6.2)
erubis (2.1.0) ncurses (0.9.1) rubyscript2exe (0.5.1)
facets (1.7.46) needle (1.3.0) simple-rss (1.1)
fastri (0.2.1.1) net-sftp (1.1.0) sources (0.0.1)
fastthread (0.6.2) net-ssh (1.0.10) sqlite3-ruby (1.2.0)
ferret (0.10.14) ParseTree (1.6.4) tar2rubyscript (0.4.8)
gem_plugin (0.2.2) radiant (0.5.2) teius (0.12)
heckle (1.2.0) radius (0.5.1) wirble (0.1.2)
hoe (1.1.7) rails (1.2.1) xhtmldiff (1.0.0)
hpricot (0.4.92) railsbench (0.9.1) xml-simple (1.0.10)
image_science (1.1.0) rake (0.7.1) ZenTest (3.4.3)

Ruby:

Gems (2)

Gems für Systemadministration und Deployment:

capistrano/vlad, deprec/carpet

puppet (facter), chef (ohai), sprinkle

package :ruby do
  description 'Ruby Virtual Machine'
  version '1.8.6'
  source "ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-#{version}-p111.tar.gz"
  requires :ruby_dependencies

  verify do
    has_file '/usr/bin/ruby'
  end
end

package :ruby_dependencies do
  description 'Ruby Virtual Machine Build Dependencies'
  apt %w( bison zlib1g-dev libssl-dev libreadline5-dev libncurses5-dev file )
end

god

integrity

journeta, nanite