Nils Haldenwang, M.Sc.
Gastvortrag
Datenbanksysteme:
Ruby on Rails
Kapitel 11
• plattformunabhängige General Purpose Programmiersprache
• Veröffentlichung 1995 in Japan
• außerhalb Japans bekannt geworden um 2000
• Objektorientierung
• dynamische Typisierung (Duck Typing)
• Garbagecollection
Ruby
Ruby
I hope to see Ruby help every programmer in the world to be productive, and to enjoy
programming, and to be happy.
That is the primary purpose of the Ruby language.
- Yukihiro Matsumoto
Ruby
Einführung in die Syntax
Quellen / Literaturempfehlungen
2.2.2 Core API: http://www.ruby-doc.org/core-2.2.2/
Ruby Dokumentation: http://www.ruby-doc.org/
Quellen / Literaturempfehlungen
Dave Thomas Chad Fowler, Andy Hunt,
Programming Ruby 1.9 & 2.0 Pragmatic Bookshelf, 2013
Quellen / Literaturempfehlungen
Paolo Perrotta
Metaprogramming Ruby 2 Pragmatic Bookshelf, 2014
Hello World
puts "Hello World!"
Ausgabe Stringliteral
hello_world.rb
$ ruby hello_world.rb
local_variable = "Lokale Variable"
@instance_variable = "Instanzvariable"
@@class_variable = "Klassenvariable"
$global_variable = "Globale Variable"
SOME_CONSTANT = "Konstante"
SomeClass # Klassenkonstante
Variablen
Basisdatentypen
natural_number = 42
natural_number.class # => Fixnum, konstante Zahl float_number = 42.0
float_number.class # => Float, Fließkommazahl string = "Hello World!"
string.class # => String symbol = :hello_world
symbol.class # => Symbol, konstanter String
Collections: Array
a = []
a = Array.new
a = [42, "foo", nil]
puts a[0] # Ausgabe: 42 puts a.size # Ausgabe: 3 a[0] = 24
Collections: Hash
h = Hash.new h = {}
h = {
:a => "b", :c => 42, "e" => "f",
42 => "The answer,..."
}
puts h[:a] # Ausgabe: b
puts h[42] # Ausgabe: The answer,..
h[:c] = 73 h = {
a: "b",
Hashrocket wenn keine
Symbole
Doppelpunkt bei Symbolen
Demo
Methoden
def say_goodnight(name)
result = "Good night, " + name return result
end
puts( say_goodnight("John-Boy") )
# => Good night, John-Boy
puts say_goodnight "John-Boy"
puts say_goodnight("John-Boy") def say_goodnight(name)
result = "Good night, #{name}"
return result end
def say_goodnight(name)
Klammern optional
Rückgabewert ist der Wert
des letzten Ausdruckes.
Empfohlene Schreibweise
Bedingte Anweisungen
if x == 5
puts "x is 5"
end
puts "x is 5" if x == 5
if x == 5
puts "x is 5"
elsif x == 3
puts "x is 3"
else
puts "x is unknown"
end
Bedingte Anweisungen
if !( x == 5 )
puts "x isn't 5"
end
puts "x isn't 5" if !( x == 5 )
unless x == 5
puts "x is not 5"
end
puts "x is not 5" unless x == 5
Schleifen
i = 42
while i > 0 puts i
i -= 1 end
i = 42
until i == 0 puts i
i -= 1 end
a = [1, 2, 3, 4, 5]
for element in a puts element
end
Collatz
public class Collatz {
public static int value(int x){
int z = 0;
while (x != 1) { if (x % 2 == 0) x = x / 2;
else
x = 3*x+1;
z = z+1;
}
return z;
}
public class Collatz {
public static int value(int x){
int z = 0;
while (x != 1) { if (x % 2 == 0) x = x / 2;
else
x = 3*x+1;
z = z+1;
}
return z;
} }
def collatz(x) iterations = 0 while (x != 1)
if (x % 2 == 0) x = x / 2
else
x = 3*x + 1 end
iterations = iterations + 1 end
return iterations end
Collatz
def collatz(x) iterations = 0 until x == 1 if x.even?
x /= 2 else
x = 3*x + 1 end
iterations += 1 end
iterations end
Collatz
def collatz(x) iterations = 0 while (x != 1)
if (x % 2 == 0) x = x / 2
else
x = 3*x + 1 end
iterations = iterations + 1 end
return iterations end
Blöcke
{ puts "Hello, Block!" } do
puts "Hello, Block!"
puts "Multiple lines."
end
Blöcke übergeben
call_block { puts "Hello, Block!" } call_block do
puts "Hello, Block!"
end
Blöcke aufrufen
def call_block
puts "Calling block:"
yield yield
puts "Block has been called."
end
call_block { puts "Hello" }
# Ausgabe:
# Calling block:
# Hello
# Hello
# Block has been called.
Blöcke mit Parametern
def call_block yield("foo") end
call_block { |param| puts "Param: #{param}" } call_block do |param|
puts "Param: #{param}"
end
# Ausgabe:
# Param: foo
Variablen und Blöcke
def call_block yield
end
var = 42
call_block { var = 73 } puts var
# Ausgabe: 73
Optionale Blöcke
def greet(name) if block_given?
yield name else
puts "Hello, #{name}!"
end end
greet "Joe" # Ausgabe: Hello, Joe!
greet( "Joe" ) { |name| puts "Nice to meet you, #{name}!" }
# Ausgabe: Nice to meet you, Joe!
Iteratoren mit Blöcken
a = ["iterators", "rock"]
a.each { |e| puts e }
# Ausgabe:
# iterators
# rock
h = { a: 42, b: 73 }
h.each { |k, v| puts "key: #{k}, value: #{v}" }
# Ausgabe:
# key: a, value: 42
# key: b, value: 73
Schleifen mit Blöcken
3.times { |i| puts i }
# Ausgabe:
# 0
# 1
# 2
1.upto(3) { |i| puts i }
# Ausgabe:
# 1
# 2
# 3
3.downto(1) { |i| puts i }
# Ausgabe:
# 3
Demo
Ruby
Installation
Ruby installieren
Ruby Version Manager
http://beginrescueend.com/
Ruby Installer
http://rubyinstaller.org/
Ruby installieren mit RVM
$ rvm install 2.2.2
$ rvm use 2.2.2
$ irb
Ruby on Rails
Ruby on Rails
• In Ruby geschriebenes Webapplication Framework
• Forciert Resource-Oriented Architecture
• Erstes Framework seiner Art
• Open Source
• 2005 Veröffentlichung Version 1.0
Ruby on Rails
Rails is about allowing beautiful code to solve the problems most people have most of the time in web-application development. It’s about taking the pain away and making you happy.
This might all sound mighty fluffy, but only until you recognize that the single-most important factor in programmer productivity is
motivation. And, happy programmers are certainly motivated
programmers. Thus, if you optimize for happiness, you’re optimizing for motivation, which ultimately leads to an optimization for
productivity.
Quellen/Literatur
Sam Ruby, Dave Thomas, David Heinmeier Hansson,
Agile Web Development with Rails, Pragmatic Bookshelf, 2014
Konzepte & Design
Patterns
Model-View-Controller (MVC)
Don’t Repeat Yourself
(DRY)
Convention Over Configuration
(COC)
Model-View-Controller (MVC)
• Design Pattern zur Strukturierung von Software-Entwicklung
• Entwicklung 1979 für Oberflächen mit Smalltalk
• De-facto-Standard für Grobentwurf vieler komplexer Softwaresysteme
• Ziel: Reduktion der Komplexität und verbesserte Wiederverwendbarkeit, Wartbarkeit, Flexibilität
• Idee: Isolation von Geschäftslogik und Userinterface und
Controller
Model View
Observer
Geschäftslogik?
Ablaufsteuerung
Anzeige / Userinterface Datenhaltung
Model
Datenfluss und MVC in Rails
Browser Webserver Router
Controller
DB View
Rails-App
Ablauf linear
Representational State Transfer
REST
Resource-Oriented Architecture
ROA
&
Quellen/Literatur
Fielding, Roy Thomas,
Architectural Styles and the Design of Network-based Software Architectures,
Doctoral dissertation,
University of California, Irvine, 2000
Quellen/Literatur
Richardson, L. and Ruby, S., RESTful Web Services
O’Reilly Media, 2007
Dix, P.,
Service-Oriented Design with Ruby on Rails,
REST
• “REST is an architectural style for distributed hypermedia systems.” - Roy Fielding, 2000
• Ziele:
• Skalierbare Interaktion zwischen Komponenten und größtmögliche Unabhängigkeit
• Allgemeine Schnittstellen
• Zwischenschichten zur Verbesserung von Sicherheit, Latenz
ROA
• “The ROA is a way of turning a problem into a RESTful Webservice.” - L. Richardson, S. Ruby, 2007
• Rückbesinnung auf Ideen, die das Web erfolgreich gemacht haben
• Adressability & URIs
• Resources & Representations
• Statelessness
• Connectedness
Was sind Ressourcen?
• Dinge, die als eigene Entität referenziert oder manipuliert werden können
• Beispiele:
• Version 1.3.1 einer Software
• Die aktuelle Version einer Software
• Die nächste Primzahl nach 1024
Adressierbarkeit und URI
• Jede Ressource hat einen URI
• Der URI ist sowohl der Name als auch die Adresse einer Ressource
• Die URI sollte die Ressource beschreiben:
• http://www.example.com/software/releases/1.3.1.tar.gz
• http://www.example.com/software/releases/latest.tar.gz
• http://www.example.com/nextprime/1024
Uniform Resource Identifier
Verschiedene Ressourcen, gleiche Daten
Zustandslosigkeit
• Jeder Request muss in völliger Isolation bearbeitet werden können und alle dafür nötigen Informationen mitliefern
• Der Server speichert keinen Client-Zustand, sondern nur Ressourcen-Zustände
• Die vom Server durchgeführte Aktion hängt nicht vom Client- Zustand ab
• Vorteile:
• Caching und Load Balancing vereinfachen sich
Repräsentationen
Teilmengen von Ressourcen-Zuständen
<person>
<name = “Nils Haldenwang” />
Manipulation von Ressourcen durch Austausch von Repräsentationen
Einheitliche Schnittstelle
“Was soll mit wem getan werden?”
GET /info.txt HTTP/1.1 Host: example.com
Zuordnung einer sinnvollen und einheitlichen Semantik zu den HTTP Verben
Semantik der HTTP Verben
GET Anfordern der Repräsenation einer Ressource
PUT Ersetzt existierende Ressource oder legt neue an übergebener URI an
POST
Annotation existenter Ressourcen, Übergabe von Daten an berechnende Prozesse und Anhängen an vorhandene
Collections
Löscht die angegebene Ressource vom
HTTP Verben in Rails/ROA
URI GET PUT POST DELETE
/users Liste aller
Nutzer - Nutzer
anlegen -
/users/3
Repräsenta- tion von Nutzer 3
Update von
Nutzer 3 - Nutzer 3
löschen
Sicherheit und Idempotenz
Ressource ändert sich nicht Wiederholte Anwedung ändert nichts
42x0 = 42x0x0x0 = 0
Verb sicher idempotent GET
PUT POST
Vorteil:
Zuverlässige Anfragen über unzuverlässiges
Netzwerk
Nils Haldenwang, B.Sc.
Gastvortrag
Datenbanksysteme:
Ruby on Rails
Models mit ActiveRecord
Model: ORM
Idee: Repräsentiere die Tabelle durch eine Klasse und die Zeilen durch einzelne Instanzen.
id first_name last_name age
1 Nils Haldenwang 25 2 Luke Skywalker 42
Tabelle: users Klasse: User
snake_case pluralisierter snake_case
Migrations
Migrations
• Ruby DSL zur Definition und Manipulation des Datenbankschemas
• Datenbankunabhängig (in gewissem Rahmen)
• Inkrementelle Entwicklung der Datenbank
• Kann mit in die Versionskontrolle eingecheckt werden
• Automatische Versionierung
• Gesamtschema in db/schema.rb
Beispiel: Tabelle erstellen
class CreateUsers <
ActiveRecord::Migration
def up
create_table :users do |t|
t.string :first_name t.string :last_name
t.timestamps end
end
def down
drop_table :users
Anpassung des Schemas
Anpassung rückgängig
class User < ActiveRecord::Base end
u = User.new first_name: "Obi-Wan", last_name: "Kenobi"
u.persisted? # => false u.save
u.persisted? # => true
puts "#{u.first_name}, #{u.last_name}"
# => Obi-Wan Kenobi
obi_wan = User.find_by_first_name("Obi-Wan") puts obi_wan.last_name # => Kenobi
Model: User
app/models/user.rb DRY
Query-API
Where-Clauses
User.create name: "Nils", age: 24 User.create name: "Luke", age: 42
User.where(name: "Nils")
User.where("name = ?", "Nils")
User.where("name = :name", name: "Nils")
User.where(name: "Nils", age: 24)
User.where(name: "Nils").where(age: 24)
User.where("name = ? AND age = ?", "Nils", 24) User.where("name = ? OR age = ?", "Nils", 42) User.where(name: ["Nils", "Luke"]).
order("name ASC")
SQL-Injections
User.where("name = #{params[:name]}")
Robert’); DROP TABLE Students; --
Obacht!
Associations
1:1-Beziehung: Definition
class Avatar < ActiveRecord::Base belongs_to :user
end
class User < ActiveRecord::Base has_one :avatar
end
Fremdschlüssel: user_id
1:1-Beziehung: Anwendung
u = User.create name: 'Nils' u.avatar # => nil
a = Avatar.new url: 'foo.jpeg' a.persisted? # => false
u.avatar = a
a.persisted? # => true
u.avatar = Avatar.new url: 'bar.jpeg'
Avatar.all
# => [
# #<Avatar id: 1, url: "foo.jpeg", user_id: nil>,
# #<Avatar id: 2, url: "bar.jpeg", user_id: 1>
# ]
Abhängigkeiten
class Avatar < ActiveRecord::Base belongs_to :user
end
class User < ActiveRecord::Base
has_one :avatar, dependent: :destroy end
1:n-Beziehung: Definition
class Article < ActiveRecord::Base belongs_to :user
end
class User < ActiveRecord::Base has_many :articles
end
Fremdschlüssel: user_id
1:n-Beziehung: Anwendung
u = User.first
u.articles # => nil
u.articles.new title: "Title"
# => #<Article id: nil, title: "Title", user_id: 1>
u.save
u.articles.first
# => => #<Article id: 1, title: "Title", user_id: 1>
u.articles << Article.new( title: "Another Title" ) u.articles.count # => 2
u.articles.delete Article.find(1) u.articles.count # => 1
u.articles.clear
u.articles.count # => 0
n:m-Beziehung: Definition
class Article < ActiveRecord::Base has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :articles end
class CreateArticlesTags < ActiveRecord::Migration def change
create_table :articles_tags, id: false do |t|
t.references :article, null: false t.references :tag, null: false
end end
Validations
Validations: Grundlagen
class User < ActiveRecord::Base validates_numericality_of :age validates_presence_of :name
end
u = User.new age: "foo"
u.valid? # => false
u.errors[:age] # => "is not a number"
u.errors[:name] # => "can't be blank"
Validations: Parameter
validates_presence_of :name, message: "Y u no use name?"
validates_numericality_of :age, on: :create validates_length_of :name, within: 6..42
validates :name, presence: true,
length: { minimum: 6 }, uniqueness: true
Validierung mit Methode
class Feed < ActiveRecord::Base validate :valid_feed_url
protected
def valid_feed_url
result = Feedzirra::Feed.fetch_and_parse(self.url) if result.nil? || result.kind_of?(Fixnum)
errors.add(:url, "The feed url was not valid.") end
end end
Callbacks
Callback-Registrierung
class Beethoven < ActiveRecord::Base before_destroy :last_words
protected
def last_words
logger.info "Friends applaud, the comedy is over."
end end
class Beethoven < ActiveRecord::Base before_destroy do
logger.info "Friends applaud, the comedy is over."
end
before_save(on: create) do logger.info "Here we go."
end
Mögliche Callbacks
• before_validation, before_validation_on_create
• after_validation, after_validation_on_create
• before_save
• before_create, after_create
• before_destroy, after_destroy
Beispiel: Geocoding
class Address < ActiveRecord::Base include GeoKit::Geocoders
before_save :geolocate
validates_presence_of :street, :city, :state, :zip
def to_s
"#{street} #{city} #{state} #{zip}"
end
protected
def geolocate
res = GoogleGeocoder.geocode(to_s) self.lattitude = res.lat
self.longitude = res.lng end
end
Verarbeitung abbrechen
def geolocate
res = GoogleGeocoder.geocode(to_s) if res.success
self.lattitude = res.lat self.longitude = res.lng else
errors[:base] = "Geocoding failed. Please check address."
false end
end
Abbruch der Verarbeitung, wenn
Views mit HTML & ERB
HTML-Gerüst
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Titel der Webseite</title>
<!-- Kommentare werden nicht angezeigt. -->
</head>
<body>
<div id="header" class="container">
<h1>Überschrift</h1>
<p>Inhalt <em>der</em> Webseite</p>
</div>
</body>
Embedded Ruby (ERB)
<div id='user-<%= @user.id %>'>
<h2>
<%= @user.first_name %>
<%= @user.last_name %>
</h2>
<p>Age: <%= @user.age %></p>
</div>
Einbetten des Rückgabewertes eines Ruby-Ausdruckes
ERB und Schleifen
<div id="squares">
<% 1.upto(3) do |i| %>
<p> The square of <%= i %> is <%= i*i %>.</p>
<% end %>
</div>
<div id="squares">
<p> The square of 1 is 1.</p>
<p> The square of 2 is 4.</p>
<p> The square of 3 is 9.</p>
</div>
Ausführung ohne
Einbettung
Controller & Routing
Controller: UsersController
class UsersController < ApplicationController def show
@user = User.find(params[:id]) end
end
app/controllers/users_controller.rb
MyRailsApp::Application.routes.draw do get 'users/:id' => "users#show"
app/config/routes.rb
RESTful Controller Actions
HTTP Verb URI Action
GET /users index
POST /users create
GET /users/:id show
PUT /users/:id update
DELETE /users/:id destroy
GET /users/new new
GET /users/:id/edit edit
Demo: Blog
Anforderungen: Blog
• Ein Blog besteht aus Artikeln
• Artikel gehören zu einer Kategorie
• Ein Artikel kann mit mehreren Schlagworten versehen sein
n 1
Category
Article
Tag
n m