ugrás a tartalomhoz

JRuby on Rails – Az erő legyen veled

yaanno · 2008. Okt. 15. (Sze), 07.29
JRuby on Rails – Az erő legyen veled
Az első budapest.rb Meetupon Bácsi László tartott egy rövid, ám annál összeszedettebb prezentációt arról, hogy mi is az a Ruby. Megemlítette a különböző Ruby implementációk között a JRubyt is, amely szinte minden tekintetben felveszi a versenyt a többi megvalósítással és virtuális géppel (Ruby 1.8, YARV/Ruby 1.9, Ruby.NET, Rubinius). A következőkben a Ruby nyelv Java implementációját és a Rails keretrendszerben történő fejlesztés JRuby alapokra helyezését szeretném bemutatni egy igen egyszerű példán keresztül. Mivel a Rails amúgy is töretlen népszerűségnek örvend és rengeteg információ érhető el vele kapcsolatban, a keretrendszer általános bemutatásától eltekintek.

Miért Java?

Mielőtt kódolni kezdenénk, több kérdés is felmerülhet. Az első kérdés bizonyára az, hogy miért is kéne Java alapokra helyezni akár a fejlesztői környezetünket, akár az alkalmazásunk kiszolgálását.

Véleményem szerint nem kell, de érdemes.

Először is a Java és különösen a Java virtuális gép (JVM) rendkívül sokoldalú támogatást nyújt számos szkriptnyelvhez, így a Rubyhoz is. Lehetőségünk nyílik például arra, hogy Ruby objektumokat elérjünk Javán belül és fordítva, a Java gazdag objektumrendszere is rendelkezésre áll Ruby alkalmazásunk fejlesztéséhez. Különösebb erőfeszítés nélkül tudunk például Ruby nyelven Java Swing alapú widgeteket, alkalmazásfelületeket készíteni.

JRuby programunk továbbá kiválóan alkalmas lehet kódgenerálásra, tesztelésre, 3D vizualizációra és játékfejlesztésre, weboldalak feldolgozására sít.

A JRuby azután – némi túlzással – teljes platformfüggetlenséget ígér: bár a Ruby nyelv implementációi elméletben platformfüggetlenek, Windows környezetben gyakran kell szembesülnünk a különböző (jellemzően C) fordítási nehézségekkel, és ezekre többnyire sem a CygWin, sem a MingW kiterjesztés nem ad gyógyírt. Ettől függetlenül megemlíteném Fabio Akita windowsos Ruby telepítőjét, amely a hivatalos installer jó alternatívája lehet. Ahol van Java, ott működhet a Ruby is, függetlenül attól, hogy milyen operációs rendszeren szeretnénk fejleszteni, illetve kiszolgálni az alkalmazásunkat, de lehetőségünk nyílik Ruby kódunk Java bájtkódra történő fordítására is, így nagyságrendekkel megnövelhetjük a futási, feldolgozási sebességet. Létezik egyébként egy másik, XRuby nevű projekt, amely Ruby forráskódunkat Java classokra fordítja – érdemes megnézni.

JRubyt és Railst vállalati környezetben tudomásom szerint a SUN Mediacast (nyílt állománymegosztás), az Oracle Mix (fejlesztői ötlet- és munkacsoportok), valamint a Jobster (álláskereső portál) alkalmazott. A Railshez kapcsolódó JRubys fejlesztések irányvonalát és a növekvő érdeklődést jól jelzi, hogy a legutóbbi RailsConf egyik fő támogatója volt a SUN és négy előadás is született JRuby témakörben.

Aki a különböző Ruby megvalósítások összevetésére részleteiben is kíváncsi, annak a The Great Ruby Shootout cikket ajánlanám. Ha saját magunk szeretnénk teljesítményvizsgálatot végezni, erre is van lehetőség.

Ugyanakkor fontos megemlíteni, hogy JRuby alatt közvetlenül nincs lehetőség natív Unix fordításra, a C alapú programcsomagok, gemek használatáról le kell mondanunk (némi körülményeskedéssel C függvények azért elérhetők). Ez eléggé nagy érvágás, ha mondjuk a natív Ruby, a PHP vagy a Python beépített képkezelési lehetőségeivel vetjük össze; biztató azonban, hogy a JRuby előnyös tulajdonságai sok fejlesztő figyelmét irányítják moduljaik portolására, keresztplatformosítására. Az említett képkezelési probléma leküzdésére például többféle megoldás is született.

Előkészületek

A JRubyban történő fejlesztéshez alapvetően rendelkeznünk kell Javával és Rubyval. Az alábbiakban Windows rendszeren fogom bemutatni az egyes lépéseket, de akár kezdő Unix ismeretekkel is jól el lehet boldogulni az egyes programcsomagok telepítésében és használatában.

Ha még nem lenne Java a gépünkre telepítve, töltsük le a Development Kit-et (JDK) a SUN weblapjáról, majd telepítsük a csomagot. Ezután szerezzük be a JRubyt, akár tömörített bináris formában, vagy forráskód terjesztésből – én az előbbit fogom használni, mert eddig igen stabilnak bizonyult.

Nem árt ha van adatbázisunk is – JDBC segítségével a legtöbb népszerű adatbáziskiszolgálóval össze tudjuk kapcsolni az alkalmazásunkat, de ha már van Java környezetünk, elegendő lehet az Apache Derby adatbáziskiszolgáló telepítése is. Én most a MySQL 5.0-ás kiadását használom.

Ha szeretnénk kipróbálni a Rails futását egy servlet container alatt, érdemes lehet beszerezni az Apache Tomcat valamelyik változatát. Eddig tehát az alábbiakkal rendelkezünk:
  • JDK 1.6
  • JRuby 1.1.4
  • MySQL 5.0
  • Tomcat 6.0

Én a következő fájlrendszert hoztam létre a példához:

c:\jruby\
c:\tomcat\
c:\applications\
Rendszerünk kényelmes működtetéséhez állítsuk be a PATH környezeti változót:

c:\jruby\bin;c:\Program Files\Java\jdk1.6.0_10\bin;c:\tomcat\bin

Első JRuby on Rails alkalmazásunk

A következőkben gyakran használunk majd terminált, ideje előkészítenünk. Érdemes a terminálparancsokat a fejlesztői környezet alkalmazáskönyvtárában kiadni:

cd applications
Ha már ott vagyunk, nézzünk utána, milyen Ruby gemek (programcsomagok) állnak rendelkezésre az alapértelmezett JRuby telepítésünkben:

jruby -S gem list --local
Jellemzően a következő gemeket kapjuk alapból:
  • rake
  • rspec
  • sources

Az -S kapcsolóval jelezzük a JRubynak, hogy rendszerszintű parancsot szeretnénk kiadni, ilyenkor a bin/ könyvtárában keresi a megfelelő futtatható állományokat. Érdemes utánanézni a Rubygems csomagkezelő alapvető parancsainak, mert általában ezzel történik a programcsomagok beszerzése. Az alapértelmezett Rubyforge (a nyílt forrású Ruby programok SourceForge-a) mellé felvehetjük a GitHub gem terjesztését is:

jruby -S gem sources -a http://gems.github.com
Ellenőrizzük és frissítsük a Rubygems csomagkezelőt, illetve az előtelepített csomagokat, amennyiben szükséges:

jruby -S gem update --system
jruby -S gem update
Most már nekiláthatunk Rails környezetünk beszerzéséhez – Rubygems-szel mi sem egyszerűbb:

jruby -S gem install rails mongrel rack warbler activerecord-jdbcmysql-adapter
Sokan eltekintenek a dokumentációk beszerzésétől a telepítés során, szerintem azonban érdemes azokat is feltenni, ha például előfordulhat velünk, hogy kénytelenek vagyunk offline módban rails-ezni és szükségünk van rájuk. A telepített gemek közül a mongrel egy kitűnő webszervert, a rack egy szerver illesztőt takar, míg a warbler gem war állományok előállítására szolgál.

Miután a programcsomagok telepítése megtörtént, takarítsunk egy kicsit:

jruby -S gem cleanup
Ezzel a paranccsal eltávolítjuk az esetleg korábban telepített gemeket, illetve kiürítjük a gem gyorstárat is. Nem érdemes tovább várnunk, adjuk ki a

jruby -S rails railstest -d mysql
parancsot, hogy végre nekiállhassunk a munkának. A fájlrendszer legenerálása után lépjünk be az alkalmazás könyvtárába:

cd railstest
Kedvenc szövegszerkesztőnkkel nyissuk meg a config/database.yml állományt és állítsuk be a szükséges paramétereket. Ügyeljünk, hogy az adatbázis illesztőnk jdbcmysql legyen. Az éles (production) adatbázis beállítását az egyszerűség kedvéért a fejlesztői adatbázisra irányítottam, ez tényleges fejlesztéskor kerülendő.

development: &development
  adapter: jdbcmysql
  encoding: utf8
  database: railstest_development
  username: user
  password: passwd
  host: localhost

test:
  <<: *development
  database: railstest_test

production:
  <<: *development
Ha megfelelően állítottuk be a a jelszavunkat, nem is kell külön vesződni az adatbázisok (railstest_development, railstest_test) létrehozásával:

jruby -S rake db:create:all
Kihasználva a Rails scaffold generátorának lehetőségeit, készítsünk egy meglehetősen egyszerű alkalmazást. Ebben az esetben az -S flag elhagyható hiszen alkalmazásszintű parancsot készülünk kiadni:

jruby script/generate scaffold post title:string body:text public:boolean
Ezután hozzuk létre az adatbázis-szerkezetet:

jruby -S rake db:migrate
Ha türelmetlenek vagyunk és szeretnénk mielőbb megtekinteni Rails alkalmazásunkat működés közben, választhatjuk a nagyszerű Rails konzolt:

jruby script/console
Vagy elindíthatjuk a webkiszolgálót (ebben az esetben egy Mongrel szervert), majd böngészőnkkel meghívhatjuk a http://localhost:3000/posts címet:

jruby script/server
Mostanra tehát van egy végtelenül egyszerű, de működő Rails alkalmazásunk. Jóllehet a JRuby alapból nyújt egy használható szerverkörnyezetet (az említett Mongrel, esetleg clusterezett Mongrel bőségesen megfelel erre a célra), mégis előfordulhat, hogy szeretnénk valamilyen servlet container alatt élesíteni az alkalmazást. Ez többnyire akkor fordul elő, ha egy már meglévő Javás környezetbe kívánjuk integrálni a munkánkat. Bár a példában én Tomcatet használok, érdemes utánanézni mondjuk az igen fürge Jetty containernek, vagy a vállalati környezetben elterjedt Glassfish alkalmazásszervernek, esetleg a hihetetlenül kis méretű és „fogyasztású” Winstone-nak.

A Tomcat automatikus deploy funkcióval bír, amihez egy war archívumot kell létrehoznunk Rails alkalmazásunkból. A Warbler gem segítségével szerencsénkre ez igen egyszerű. Adjuk ki az alábbi parancsot:

jruby -S warble config
A program eredménye egy fájl warble.rb néven, melyet a config/ könyvtárban találunk – ebben a fájlban kell beállítanunk a war állomány létrehozásához szükséges paramétereket. Nyissuk meg a warble.rb állományt kedvenc szövegszerkesztőnkkel, és távolítsuk el a megjegyzést az alábbi sor elől:

config.gems += ["activerecord-jdbcmysql-adapter","rails","rake"]


Természetesen további beállításokat is eszközölhetünk, de egy egyszerű deploy folyamathoz ennyi elegendő. Készítsük el a war állományt:

jruby -S warble war
A programfutás eredménye egy railstest.war állomány, illetve ennek szerkezete a tmp/war/ könyvtárban. Érdemes megnézni, hogy a tmp/war/WEB-INF/gems/gems/ könyvtár tartalmaz-e minden szükséges programcsomagot, különös tekintettel az adatbázis-kapcsolatot biztosító gemre. Ezután már csak át kell másolnunk a railstest.war fájlt a c:\tomcat\webapps\ könyvtárba, mielőtt elindítjuk a Tomcatet. Indítás után a szerver automatikusan kibontja a war állományt, így webböngészőnkkel el tudjuk érni a Rails alkalmazásunkat – ezt jellemzően a http://localhost:8080/railstest/posts címen tehetjük meg. Ha elégedettek vagyunk az eredménnyel, kitakaríthatjuk a container számára generált kódot alkalmazáskönyvtárunkból az alábbi paranccsal:

jruby -S warble war:clean
A Warbler gem további beépített lehetőségeit a warble -T parancs kiadásával listázhatjuk.

Kitekintés

Bár Rails alkalmazásunk kiszolgálására egy servlet container kitűnő választás lehet, statikus állományainkat jobb valamelyik pehelysúlyú webszerverre bízni. Jó választás lehet mondjuk a Lighttpd, vagy a Ruby fejlesztők új kedvence, az nginx, mindkettő könnyen konfigurálható, gyors kiszolgáló.

Ha más Ruby keretrendszereket is szeretnénk kipróbálni JRuby alatt, annak sincs akadálya! Széles körben használt és kedvelt keretrendszer például a Merb, amely elsősorban a Railsben történő fejlesztés kénytelen konvencióit igyekszik elkerülni, de pillanatok alatt telepíthető és futtatható például a kristálytiszta felépítésű Ramaze framework is.

jruby -S gem install ramaze
Készítsünk egy példaalkalmazást.

jruby -S ramaze --create ramazetest
Majd indítsuk is el.

jruby ramazetest/start.rb
E pehelysúlyú keretrendszerről továbbiakat megtudhatunk a Ramaze prezentációból.

A cikk végén még egy témát szeretnék megemlíteni, ez pedig az alkalmazás folyamatos integrációja. Mivel igen valószínűtlen, hogy a Ruby fejlesztő egymagában dolgozzon, nem csupán a verziókövetés problémáját kell megoldani, hanem a fejlesztés folyamatában lényegi szerepet játszó tesztelés és deploy kérdését is. Az olvasónak könnyen feltűnhetett például az előző pont végén, hogy míg az alkalmazás generálás–beállítás–indítás része könnyen végrehajtható, a servlet containerre történő deployment további ügyködést igényelt a terminálban. Ráadásul a csapatban együttműködő, Java/JRuby platformra fejlesztő programozók és tesztelők számára elengedhetetlen egy olyan környezet, ahol egymás munkáját képesek nyomonkövetni és segíteni. A fejlesztési folyamat ezen lépéseinek támogatására szerencsére több eszköz is rendelkezésre áll, amelyek közül – bár új fiúnak számít – a Hudson tűnik a leginkább ígéretesnek.

Köszönet

Szeretném megköszönni Bácsi Lászlónak, Török Gábornak és Kurdi Szabolcsnak, hogy a cikk megírásában értékes megjegyzéseikkel segítettek. Gábor kérte, írjak pár szót arról, hogy – frontend fejlesztő lévén – miért is foglalkozom JRubyval, illetve hogy készítettem-e valamilyen éles alkalmazást JRubyval. Jelenlegi munkahelyemen egyelőre nincs lehetőség szkriptnyelveket használni Java környezetben – leszámítva a Rhino futtatókörnyezetet, amit web robotok működtetésére használunk. A Rhino képességeiről egyik kolléga tartott is egy prezentációt a legutóbbi Web Konferencián. Könyvtárszerkezetek, stíluslapok és sablonfájlok generálására azért előveszem :)

Végül néhány hasznos webcím:

 
yaanno arcképe
yaanno
Frontend fejlesztő a Liligo-nál. Szabadidejében főként programnyelveket (most éppen a Ruby és az Erlang érdekli) és UI tervezést tanul. Hektikusan karbantartott blogja a domhackers.blogspot.com címen olvasható.
1

Gyakorlati elony?

zmb · 2008. Okt. 16. (Cs), 10.28
Azert arra kivancsi lennek, hogy milyen gyakorlati elonye van annak, ha java alatt futtatom a rubyt? Max annyit tudok elkepzelni, hogy a java rendszeren belul bizonyos dolgokat ruby script hajt vegre, igy nem kell a rendszert leallitani/ujraforditani, ha csak a scriptben kell modositani.
2

Attól függ

yaanno · 2008. Okt. 16. (Cs), 11.51
Egyrészt azt kell tisztázni, kell-e egyáltalán Ruby :) Ha mindent meg tudsz oldani, és szívesebben oldod meg Javában, akkor felesleges ezzel ügyködni.

Másrészt viszont, ha szeretnél Ruby-t használni valamiért, akkor az integráció előnyei nyilvánvalóak: Java-Ruby kommunikáció, nem kell külön szerver az egyiknek/másiknak, egységes tesztelés/deployment. És valóban, alapból JIT fordítás van, tehát nem kell restart stb.
3

GWT-nél jól jön

Török Gábor · 2008. Okt. 16. (Cs), 12.48
Yaannot kiegészítendő pl. óriási előnye lehet ennek a felállásnak (csak elméletben beszélek, nem volt még lehetőségem ezt használni), ha Rails felett GWT-t szeretnél futtatni, hiszen máris közvetlenül elérik egymás osztályait. Anno a webconfon még a Pyjamas (Python alapú GWT-klón) előnyére írtam, hogy benne felhasználhatók a Django komponensei (és fordítva), viszont mióta Django is fut Jythonon, az egyetlen vonzó ereje is megszűnt a Pyjamasnak.
4

Benchmark frissítés

yaanno · 2008. Dec. 17. (Sze), 09.16
Nemrégiben készült egy frissebb benchmark, hátha érdekel valakit :)