Next Previous Contents

3. Ruby i Apache: mod_ruby

Ruby kan integreres i Apache-webserveren ved hjelp av mod_ruby. Vi går ikke inn på konfigurering og installering, men fokuserer på den praktiske bruken av mod_ruby.

mod_ruby kan brukes til så mangt, da det egentlig bare:

En vanlig bruk er å sette opp slik at Ruby CGI-skript kjøres i Ruby-fortolkeren som er inne i Apache-prosessen i stedet. I tillegg til at det går fortere, har man da også tilgang til deler av Apaches API.

  1| r = Apache.request # Hent den gjeldende forespørsel.
  2| gammel_innholdstype = r.content_type
  3| r.content_type = 'text/html'
  4| r.sync=true # Slå på synkron utskrift.
  5| puts '<HTML><BODY>'
  6| puts '<H1>Grave litt rundt i mod_ruby APIen.</H1>'
  7| puts '<P>Gammel innholdstype: ' + gammel_innholdstype
  8| puts '<P>Server versjon: ' + Apache.server_version
  9| puts '<UL>'
 10| sleep 3 # Bare for å vise at synkron utskrift er påslått.
 11| [ :filename,     :protocol,     :request_method, 
 12|   :request_time, :server_name,  :server_port, 
 13|   :status,       :uri,
 14| ].each do |symbol|
 15|   print '<LI>', symbol.to_s, ' = ', r.send(symbol), '</LI>'
 16| end
 17| puts '</UL></BODY></HTML>'

(NB: Det er vanlig å bruke .rbx-prefikset når koden skal kjøres direkte i mod_ruby.)

3.1 Apache-prosesser og mod_ruby

For de som er kjent med Apache er dette kanskje en selvfølge, men det er viktig å innse at Apache-webserveren kjører flere prosesser som mottar og betjener HTTP-forespørslene til en webside.

Flere Apache-prosesser.

En påfølgende forespørsel fra en bruker vil ikke nødvendigvis havne hos samme prosess som den forrige forespørselen gjorde. Dermed kan vi ikke stole på at tilstanden vi forlot den interne Ruby-fortolkeren i er den vi møter igjen. (Så tro ikke at globale variable kan brukes for å lagre tilstand mellom forespørsler.)

3.2 Ruby inni HTML: eruby

eruby står for Embedded Ruby og gjør slik at man kan programmere dynamiske websider i Ruby på samme måte som man gjør med f.eks. PHP, JSP eller ASP. "Embedded" betyr her "innbakt" i annen tekst.

  1| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
  2| <html>
  3|   <head><title>Hei, eruby!</title></head>
  4|   <body>
  5|     <!-- utskrift i kode settes inn i HTML-dokumentet. -->
  6|     <% puts "Hei, eruby!" %>
  7| 
  8|     <!-- Med = tegnet kan man også sette inn variabler direkte. -->
  9|     <% dato = Time.now %>
 10|     <%= dato %>       
 11| 
 12|     <!-- Også har man kommentarer -->
 13|     <%# puts "Denne koden kjøres ikke." %>            
 14|   </body>
 15| </html>

Det finnes andre alternativer for å generere HTML og lignende fra templater, f.eks. Amrita, som tilbyr noe 'renere' separering av HTML og Ruby-kode.

3.3 Anonyme moduler i eruby

Dersom du bare skal kjøre en webapplikasjon på en webserver, trenger du ikke bekymre deg om forsøpling av navnerommene til fortolker-instansene i Apache-prosessene. Du kan bare passe på selv at du ikke roter til og lager to metoder med samme navn som gjør litt forskjellige ting. (Lykke til på sinnssykehuset.)

Skal man derimot ha flere webapplikasjoner, er det ønskelig at man beskyttes litt mot navneforurensing. Alle fortolker-instansene deles jo mellom alle webapplikasjoner som kjøres. Derfor utfører eruby koden som skal kjøres for en dynamisk generert HTML-side innpakket i en anonym modul.

Følgende kode fungerer derfor ikke:

  1| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
  2| <html>
  3|   <head><title> ikke-fungerende rot 13 </title></head>
  4|   <body>
  5| <% 
  6| class String  # Dette lager en ny String-klasse i  
  7|   def rot13   # stedet for å utvide den eksisterende.
  8|     self.tr( "A-Za-z", "N-ZA-Mn-za-m" )
  9|   end
 10| end
 11| require 'cgi'
 12| c = CGI.new
 13| inntekst = c['inntekst'][0]
 14| if inntekst then
 15|   uttekst = inntekst.rot13  # Fungerer ikke.
 16| end    
 17| %>
 18|   <b><%=inntekst%></b> rot 13 kryptert blir <b><%=uttekst%></b>.
 19|   <form><input type="text" name="inntekst">
 20|         <input type="submit" value="rot13"></form> 
 21|  </body>
 22| </html>

Ruby er selvsagt dynamisk nok til å komme seg rundt slike stengsler...

  1| # Med litt stygg eval- og klassemagi går det derimot greit.
  2| String.class_eval {
  3|   def rot13
  4|     self.tr( "A-Za-z", "N-ZA-Mn-za-m" )
  5|   end        
  6| }

...men dette vil påvirke alle prosesser som noensinne kjører skriptet, og den forrige ikke-fungerende versjonen vil enten feile eller fungere alt etter hvilken tilstand prosessen som blir valgt ut til å håndtere forespørselen er i.


Next Previous Contents