Ruby on Rails on erittäin merkittävä framework, johon tutustuminen kannattaa, vaikka se ei ehkä olekaan tänä päivänä uusinta uutta. Rails-framework:n merkitys moniulotteinen. Alunperin se aloitti ns. vastaiskun Java Enterprise Edition:lle uudella filosofiallaan ”Convention over Configuration”. Tämä tarkoittaa käytännössä, että sen sijaan että koodaajalle annetaan rajaton mahdollisuus konfiguroida järjestelmää (esim. XML-tiedostoilla), ohjelmistokehitystä rajoitetaan ja tehostetaan nojautumalla tiettyyn ohjelmointikäytäntöön, johon frameworkin toiminta perustuu. Noudattamalla konventiota koodaajan elämä on helppoa ja tuottavuus on suurta. Java EE:ssä filosofia oli 2000-luvun alussa päinvastainen, eli koodaajalle sallittiin rajaton mahdollisuus konfiguroida järjestelmää kuten haluaa. Ongelmana vain oli että konfiguraatioiden määrä ja monimutkaisuus kasvoi niin suureksi, että kokeneinkaan koodaaja ei enää ottanut selvää miten eri vaihtoehdot tulisi asettaa että saisi järjestelmän toimimaan, eli hyvä tarkoitus kääntyi itseään vastaan. Java2 EE haamu kummittelee edellen Java-maailmassa.
Ruby on Rails:n convention over configuration -filosofia on herättänyt vuosikymmenten aikana runsaasti inspiraatiota muissa ohelmointikielissä, ja satoja Rails:sta ideoita ammentavia (tai lähes suoraan kopioituja) frameworkeja on tullut satoja, kuten Java Grails, Node.js Sails, PHP Symfony/Laravel, Python Django vain muutamia mainitakseni. Näissä jopa työkalujen väritykset, komennot ja kirjastot ovat lähes identtisiä tai ammentavat erittäin vahvasti inspiraatiota alkuperäisestä. Kuitenkaan Python ei ole Ruby eikä Sails ole Rails johtuen muiden ohjelmointikielten rajoituksista, joten pääpiirteittään parempaan alkuperäiseen ohjelmointikokemukseen pääsee kuitenkin käyttämällä aitoa ja alkuperäistä Ruby on Rails:ia.
Seuraavaksi listaan omasta mielestäni muutamia Ruby:n sekä Rails:n hyviä että huonoja ominaisuuksia pikaohjeena PHZ Full Stack:n muille kehittäjille. Meillähän on käytäntönä laajentaa osaamista ja stackkia tutustumalla uusiin ohjelmointikieliin parin kuukauden välein, mistä on hyötynä perspektiivin avartuminen. Tästä on hyötyä etenkin asiakasprojekteissa, joissa ei koskaan tiedä mitä pellin alta löytyy. Nähtyään 20 ohjelmointikieltä ja 50 frameworkkiä, ne kaikki alkavat näyttää samanlaisisilta pienillä syntaksieroilla.
Historia
Ruby:han on historiallisesti olio-ohjelmointilaajennus Perl:lle. Perl:iähän kutsutaan write-only -ohjelmointikieleksi, koska sillä kirjoitettu koodi näyttää yleensä täydeltä heprealta johtuen sisäänrakennetusta ja paljon sitä kautta hyödynnetystä regexp -tulkista. PHZ Full Stack:n ohjelmointikonventiossa regexp:ejä pyritään pääsääntöisesti välttämään niiden vaikean luettavuuden takia, ja toisaalta sen takia, että säännöllisten lausekkeiden ilmaisuvoima on rajallinen ja lopulta ei yleensä riitä varsinaisen ongelman ratkaisemiseen (kuvittele esim. regexp joka parsii sekä Suomen että Ruotsin postinumerot). Regexp:ejä jos käyttää, ne kannattaa toteuttaa vahvasti yksikkötesteihin nojautuen.
Hyvät
Ruby:ssa on samanlainen funktioiden palautusarvon implisiittinen palautustoiminnallisuus kuin Scala:ssa, eli toisin sanoen funktioista palautuu aina viimeisen rivin arvo. Koodaajan ei erikseen tarvitse kirjoittaa return, paitsi jos aikoo palauttaa arvon kesken funktion jostain haarasta. Tämä tekee koodista lyhyempää ja luettavampaa, mutta toisaalta koska valtaosassa muita ohjelmointikieliä tätä ominaisuutta ei ole, Rubyyn perehtymättömältä koodaajalta menee vähän aikaa oppia implisiittinen palautuksien lukeminen että konventio olla kirjoittamatta return:ia viimeiselle riville.
Itseasiassa syy miksi aloin kirjoittamaan tätä artikkelia oli että löysin uudelleen Ruby:n huikean ominaisuuden nimeltä retry. Nykyään kun useimmat järjestelmät juttelevat muiden microserviceiden ja palveluiden kanssa (kuten maksujärjestelmät, taloushallinnon raportoinnit jne), käytännön ongelma on, että kolmannen osapuolen järjestelmien tai minkään oman (yhdellä koneella pyörivän) järjestelmän luetettavuuteen (jos siihenkään) ei voi luottaa 100%:sesti, ja ainakin itse olen oppinut lähtökohtaisesti laittamaan systemaattisesti kaikki ulkopuoliset kutsut try/catch -blokin eli Ruby:ssa begin/rescue -blokin sisälle. Tuotannossa kuitenkin aina jossain kohtaa joku palvelu on nurin, ylikuormittunut, tai token expiroituu 30min päästä, jolloin tarvitaan kutsun lähetyksen uudelleenyritystä. Tässä kohtaa retry paljastaa huikeat ominaisuutensa, joita on vaikea toteuttaa muissa ohjelmointikielissä helposti. Huomasin tämän kun olin taas tekemässä uudelleenyritystä, mutta en ollut koodannut Rubya hetkeen enkä muistanut tätä ominaisuutta kuin vasta myöhemmin kun löysin rakenteen (itse aiemmin kirjoittamastani) koodista. Muissa ohjelmointikielissä uudelleenyrityksen toteuttaminen on kömpelö, eli siihen tarvitaan joko duplikaattikoodia, mikä on vaikeampi ylläpitää (jos sama REST-apia kutsuva koodi on kahteen tai useampaan kertaan samassa try/catch -blokissa), tai rekursiota jota yritin tässä ennen valaistumista:return
– – self.send(method, **args)
+ + retry if retries < 4
Eli tässä alkuperäinen hakkerointini oli yrittää selvittää miten Ruby:ssa voi kutsua rekursiivisesti samaa funktiota uudestaan. Tässä oli useita ongelmia mm. miten välittää argumenttina monesko uudelleenyritys oli kyseessä, jotta rekursion voisi lopettaa esim. 4 uudelleenyrityksen jälkeen. Sitten huomasin toisessa luokassa ”retry if retries < 4” pätkän, joka on elegantti, paljon helpommin luettava ja myös bugivapaampi (nimimerkillä käytin viikon rekursion debuggaamiseen ja luulen että se ei silti toiminut oikein) Rubyn sisäänrakennettu ratkaisu tämän ongelman ratkaisuun. Toivoisin että tämä kopioitaisiin myös muihin ohjelmointikieliin laajemmin.
Pahat
Rubyn ärsyttäviä huonoja ominaisuuksia on tyyppien automaatticastayksien puute, mikä aiheuttaa turhaa hidastusta koodaamiselle etenkin tulkatussa kielessä, sekä tekee koodista vähemmän luettavaa. Käytännön ongelma on, että jos yrittää katenoida kokonaislukua (int) tekstiin (string), se pitää eksplisiittisesti muuttaa tekstiksi tai muuten tulee virheviesti ja koodi kaatuu. Käytännössä siis kaikkiin arvoihin mitä haluaa joko karvalakki-debuggata tai logittaa pitää aina muistaa laittaa .to_s perään, mitä kukaan ei tietysti muista ja kehityssykli hidastuu perusasioiden takia.
Rumat
Ruby on Rails:n alamäki alkoi luultavasti versiopäivityssotkusta 1.8 -> 1.9 -> 2.0, jolloin hyvin lyhyen ajan sisällä ohjelmointikieleen julkaistiin päivityksiä, jotka eivät olleet kovinkaan yhteensopivia edellisen version kanssa, ja lopulta osittain palaten alkuperäiseen. Tämä sekoilu karkoitti paljon kehittäjiä toisiin ohjelmointikieliin.
Rails on myös ns. edellisen sukupolven backend-renderöivä framework, jonka filosofia ei ole kovinkaan yhteensopiva modernin React/Angular/Vue.js -frontend-kehityksen kanssa. Kuitenkaan mikään ei estä REST-apien tekemistä React-frontille, joskin suuri osa Rails:n (fronendin generoimiseen tarkoitetusta) ominaisuuksista jää hyödyttömiksi.
Rails:n debuggerissa näkee että kukin scope on erillinen ulommasta, mikä saa tottumattoman debuggaajan hämmentymään, koska esim. for-loopin sisälle on vaikea päästä paitsi laittamalla sinne erikseen breakpointin. Toinen debugger-vetoista kehitystä hidastava tekijä on Sidekiq -workeristä puuttuva hotreload (tai en ole itse sitä ainakaan onnistunut konffimaan, jos sellainen on), jolloin aina pienimmänkin muutoksen jälkeen koko palvelu (Sidekiq) pitää käynnistää kokonaan uusiksi. Rails (web-server):ssä taas hotreload löytyy, mutta sen hyödyllisyys on rajallinen jos frontend on tehty modernilla stack:llä.