Posted by Saulius Grigaitis
Sat, 30 May 2009 16:48:00 GMT
Žadėtos ir ilgai lauktos skaidrės bei keletas nuotraukų iš RubyConfLT 2009! Nors ir labai spaudė konferencijos laikas, spėjome pristatyti temas ir padiskutuoti. O kas nespėjo, tai galėjo padaryti afterparty’je , kuris pradėjo linksmai ir turiningai. Auditorija šiemet buvo brandesnė, nemažai buvo patyrusių Ruby/Rails programuotojų. Taigi Ruby/Rails po truputi atranda savo vietą lietuvių programuotojų širdyse ;)
Ketiname rengti nuolatinius Ruby meetup’us Vilniuje. Formatas būtų maždaug toks:
- Susirenkame kartą per 1-3 mėnesius
- Susirenkame vakare (po darbo)
- Dalyvių skaičius 20-40
- Pristatome porą geidžiamiausių temų
- Padiskuotuojame, pasidaliname patirtimi
- Viską pratęsiame afterparty’je
Siūlykit kaip gerinti formatą, siūlykit temas!
Kelios RubyConfLT 2009 nuotraukos:



RubyConfLT 2009 skaidrės:
- Saulius Grigaitis:
- Artūras Šlajus:
- Eimantas Vaičiūnas
Konferencijos įrašai čia
Tags cucumber, git, profiling, rails, ruby, rubyconflt, scaling | 4 comments | no trackbacks
Posted by Saulius Grigaitis
Sun, 22 Mar 2009 12:48:00 GMT
Programuoji? Programuoji “web’ui”? Programuoji, tačiau programavimas nesijaučia “fun”? Ko gero tau reikia susipažinti su Ruby, gana neseniai išpopuliarėjusi kalba, kuri tapo mūsų kasdienybe ir mes norime pasidalinti savo žiniomis su jumis. Bandysime apžvelgti Ruby ir Rails pasaulio naujoves bei ekosistemą, o taipogi aptarsime visuomet aktualias temas: greitaveiką, “skalabilitą” (scalability) ir testų rašymą. Laukiami ne vien “web developeriai”, tačiau ir žmonės norintys susipažinti su kalba, jos ideologija ir principais, stovinčiais už jos.
Konferencijos programa:
- Ruby 1.9 (Eimantas Vaičiūnas)
Neseniai išleista stabili Ruby 1.9 versija atnešė nemažai pakeitimų. Vienas iš didžiausių buvo interpretatoriaus pakeitimas iš MRI (Matz Ruby Interpreter, originalaus Ruby interpretatoriaus) į YARV (Yet Another Ruby VM). Aptarsime šį perėjimą, jo naudą, bei kitas Ruby 1.9 naujoves.
- Rails 2.3 & 3 (Artūras Šlajus)
Rails – nepaliaujamai besivystantis projektas, į kurį suplaukia patobulinimai sukurti viso pasaulio programuotojų. Papasakosime kas naujo neseniai išleistoje 2.3 versijoje, bei kokios perspektyvos laukia Rails 3 versijoje, kurioje bus įlietas MERB karkasas. MERB buvo sukurtas, jog pašalintų Rails trūkumus – monolitiškumą, saugaus gijų palaikymo nebuvimą (thread safety) ir kitką. Rails ir MERB suliejimas leis turėti geriausius dalykus iš abiejų pasaulių.
- Git versijų kontrolės sistema (Artūras Šlajus)
Git buvo parašytas valdyti Linux kernelio išeities kodą, tačiau Ruby bendruomenė greitai pamatė jo privalumus. Git yra paskirstyta (distributed), greita ir multiplatforminė versijų kontrolės sistema. Kalbėsime apie jos vidinę struktūrą, naudojimą, palyginsime su Subversion ir kokia Git reikšmė Ruby bendruomenėje.
- Ruby profiliavimas ir greitaveikos testavimas (Eimantas Vaičiūnas)
Užklausos pradėjo stabdyti? Procesoriaus apkrovimas viršijo proto ribas? Kažkur dingo visa atmintis? Gal pats laikas optimizuoti kodą? Aiškinsimės kaip tai padaryti.
- Scaling Rails (Saulius Grigaitis)
Kad ir kaip beoptimizuotum projektą ar kokį galingą serverį benupirktum, galų gale ateis toks laikas, kai vienas serveris projekto jau nebepavilks. Tad ką daryti? Ogi “scalintis”!
- Cucumber (Saulius Grigaitis)
Testai yra gerai, testai, kuriuos supranta klientas, yra dar geriau. Cucumber – karkasas, leidžiantis testus aprašyti natūralia kalba. Žiūrėsime ką daryti, jog tai, ką suprantat jūs ir klientas, suprastų ir Ruby.
Konferencijos pradžia: Balandžio 19 diena, 10:00
Kaina: Nemokama
Vieta: Studentų g. 48a-323, Kaunas
Registracija čia
Tags community, rails, ruby, rubyconflt | 12 comments | 2 trackbacks
Posted by Saulius Grigaitis
Mon, 14 Apr 2008 18:13:00 GMT
Buvo išties “fun” :). Buvo džiugu matyti nemažai entuziastų, kurie aktyviai dalyvavo visą konferencijos laiką, nors pranešimai truko net iki 21-os valandos. Nors tai RubyConf, konferencijos metu daugiausia kalbėta ne vien apie Ruby, bet ir apie Rails, Merb bei Chuck Noriss :). Visą tai artimiausiu metu galėsite pamatyti ir konferencijos vaizdo įrašuose, kurie bus patalpinti KTU distancinių mokymų centro tinklapyje. Dėkui Artūrui už konferencijos organizavimą, pranešėjams, publikai, KTU DMC darbuotojams ir visiems kitiems, prisidėjusiems prie šios konferencijos. Tikiuosi, netruks ilgai iki sekančio susitikimo.
Konferencijos akimirkos:;
Ir pranešimų skaidrės:
Artūras Šlajus:
Eimantas Vaičiūnas:
Saulius Grigaitis:
Fiodor Vereščiaka:
Laurynas Liutkus:
Tags merf, rails, ruby, rubyconf, slides | 3 comments | no trackbacks
Posted by Saulius Grigaitis
Sat, 29 Mar 2008 14:58:00 GMT
Pagaliau išsikraustėm iš hostingo, kuriame negana to, kad downtime’as buvo didesnis už uptime’ą, dar ir neaiškiomis aplinkybėmis dingo duomenys ne tik iš duomenų bazės, bet ir iš atsarginės kopijos. Laimei, straipsnius pavyko atkurti iš kešuotos HTML versijos, o straipsnių komentarai dar laukia savo eilės. Taip pat atnaujinome ir pačią blog’inimo sistemą bei apvilkome pavasarinį rūbą.
Šiek tiek keičiasi ir pačio tinklapio www.rubyonrails.lt koncepsija, daugiau dėmesio skirsime Lietuvos Ruby ir Rails bendruomenei. Straipsniai, kurie aktualūs tik Lietuvos Ruby ir Rails bendruomenei, bus skelbiami lietuvių kalba, o plačiajai auditorijai aktualūs straipsniai dažniausiai bus skelbiami anglų kalba. Artimiausiu metu startuos forumas, skirtas diskusijoms Ruby ir Rails tematika, tad galima bus diskutuoti ir ieškoti problemų sprendimų įvairiomis temomis. Visi kas turite pasiūlymų ar patarimų tiek blog’o, tiek būsimo forumo klausimais, ar norite prisidėti prie šios bendruomenės, parašykite apie tai komentaruose!
Tags community, rails, ruby | no comments | no trackbacks
Posted by Saulius Grigaitis
Thu, 24 Jan 2008 20:37:00 GMT
Nowadays Rails applications are RESTful, so in most cases it avoids any complex workflow(except minor things like account registration). But sometimes workflow becomes important element in application, then the quick way is to use status field, which works well at simple situations. For example Invoice model has status field, which is one of ["new", "pending", "paid", "canceled"] or so. The problem here is that workflow is hardcoded in code. So invoices with status "new" appears in new invoices page, because that action use something like Invoice.find(:all, :conditions => {:status => "new"}), and the same situation is with other pages. Here workflow is hardcoded. One day business process changes and new status of invoice is added, then programmer needs to analyze the app's code, remember the workflow implementation and change a lot of code in tests and models/views/controllers... Usually this is non-effective and painful.
Other issue - business analyst or customer provides the workflow schematics, then programmers use those. Constantly schematics or implementation of that worklow is changed, but usually synchronization between schematics and implementation is lost, because people make mistakes or are too lazy :).
Is there any solution to easer control of workflow and avoid mentioned issues? Yes, serious people call it orchestration:). The main idea is to get workflow defined in some external resource, like XML file. Then even business analyst can easily change the workflow with help of IDE, which allows to edit the workflow files(for example jBPM Process Designer for JBoss jBPM). Workflow and business process machine engine OpenWFE has been ported to Ruby, so Ruby folks can use OpenWFEru. Unfortunately, I couldn't find any usable process designer for this engine, but usually it is not painful to write process definition by hand in XML or Ruby.
There are few things that one must understand before using OpenWFEru: business processes, participants and workitems(or tasks, or tokens, or other name for the same thing).Business processes are defined via trees of expressions named process definitions. Participants are those workers, which starts to do something when get workitem. Participants can be mailer, so it sends email once it gets workitem, or web service client, so it communicates with server via API once it gets workitem, or block of Ruby code, so this code will be executed once it gets workitem, or any other allowed participant. And the most important Gotcha is workitem. Workitem itself doesn't contain any task, it's only token. But one can associate variables with this workitem, so one can pass important data from one participant to other, like the ":translator" participant pass the "translation" to ":reporter" participant in the example. So workitem is created and passed to first participant once business process is initiated, this workitem is passed to next participant according to process definition once first participant completes his task. In same way workitem is passed thought entire process and participants knows when to do their job.
Here is the simple example, which is based on openwferu.rb. There is very simple workflow: sequence of three participants. First one is you, your task is to enter text in english. Once you complete it, the workitem with the "source_text" you entered will be passed to second participant - translator. Translator here is Ruby block again, which calls google translation service(thanks to rbabel). Once text is translated, it is attached to workitem, and workitem is passed to third participant - reporter. This is Ruby block again, it only prints the source text and translated text. As you see, it is very easy to change the translator. What you need to do, is only change the block of current translator, or register new participant and replace old participant's name in process definition with new one. Also, it is very easy to analyze and change workflow, you only need to be familar with XML(actually, workflow can be defined in Ruby too). And final thing is that participants are loosely coupled, the only one thing is the attached variables to workitem. So participants are like small units, which has input and output, so it's very easy to make various combinations of those units in complex sequences.
#first install gems
$ sudo gem install openwferu
$ sudo gem install rbabel
then execute this Ruby script
require 'rubygems'
require 'openwfe/def'
require 'openwfe/workitem'
require 'openwfe/engine/engine'
require 'openwfe/participants/enoparticipants'
require 'rbabel'
# instantiating an engine
engine = OpenWFE::Engine.new
# adding some participants
engine.register_participant :you do |workitem|
puts "========================================="
puts "Task for You"
puts "Please, enter text in english"
workitem.source_text = gets
puts "========================================="
end
engine.register_participant :translator do |workitem|
puts "==============Task for translator========"
puts "translating text..."
workitem.translation = workitem.source_text.translate(:en, :fr)
puts "translated!"
puts "========================================="
end
engine.register_participant :reporter do |workitem|
puts "==============Task for reporter=========="
puts "Text in english:\n#{workitem.source_text}\ntranslated to text in french: \n#{workitem.translation}"
puts "========================================="
end
# a process definition
definition =
'<process-definition revision="1" name="example">
<sequence>
<participant ref="you"></participant>
<participant ref="translator"></participant>
<participant ref="reporter"></participant>
</sequence>
</process-definition>'
# launching the process
li = OpenWFE::LaunchItem.new(definition)
li.initial_comment = "please give your impressions about http://ruby-lang.org"
fei = engine.launch li
# 'fei' means FlowExpressionId, the fei returned here is the
# identifier for the root expression of the newly launched process
puts "started process '#{fei.workflow_instance_id}'"
engine.wait_for fei
Yes, this example is very simple, but that was my way to show you this concept. If you understand the advantage of workflow and business process engine, then you can imagine how easy it is to implement complex workflow in dynamic enterprise, which is usually very painful without such engine.
Tags bpm, rbabel, ruby, workflow | 4 comments | no trackbacks
Posted by Saulius Grigaitis
Wed, 26 Sep 2007 19:35:00 GMT
I've tried Netbeans 6 with Ruby support yesterday. I'm impressed. I'm impressed twice:). I used Netbeans for programming tasks at the university few years ago, but Netbeans was just IDE for Java development, I never thought that they achieved so much in Ruby and Rails support nowadays. First of all, read what Infrid writes on his blog lifeonrails.org. There are some videos on netbeans.org too. That's impressive. Is it time to throw out your favorite IDE or editor even if it is textmate? In most cases - Yes.
Unfortunately, there is no Netbeans precompiled binary or port for my favorite operating system FreeBSD, the same situation is with Java Application Server - GlassFish, which is suitable for deploying JRuby on Rails projects. If you do not use Linux, OS X, Solaris or Windows, then building Netbeans and GlassFish from source is probably the only one way to get it working in your box. This way should work for most of Unix derived operating systems, which has JDK 1.5 and other required tools like ANT. The instructions how to built Netbeans from source is available at Netbeans Wiki. Full Netbeans source code checkout and build takes about few hours if your machine and internet downstream are pretty fast, so be patient. The instructions how to build GlassFish ant the JAR archives are available at GlassFish Community. This JAR is suitable if you use FreeBSD. GlassFish is smaller, so the process takes less time than to build Netbeans.
Great! So much new things comes to Ruby and Rails...
Tags freebsd, glassfish, jruby, netbeans, rails, ruby | no comments | no trackbacks
Posted by Mindaugas Kurlavičius
Tue, 04 Sep 2007 19:34:00 GMT
Tekstinė paieška, tai vienas iš svarbiausių reikalavimų beveik visose tinklo aplikacijose.
Taigi, apie tai, kaip tekstinę paiešką realizuoti, Rails tinklo aplikacijoje.
Ne paslaptis, kad didžioji žiniatinklių pyrago dalis naudoja MySQL DBVS, taigi ir šiame straipsnyje
į tekstinę paiešką pažvelgsime iš MySQL perspektyvos.
Pasižvalgius po interneto platybes, paaiškėjo, kad patys populiariausi tekstinės paieškos realizavimo
metodai Rails aplikacijose yra tokie:
- MYSQL, LIKE sintaksė.
- MYSQL implementuotas teksto indeksavimas.
- Acts_as_ferret pluginas.
- Acts_as_sphinx pluginas.
MYSQL, LIKE sintaksė
Dar ir iki šiandien plačiai naudojamas dėl paprastumo. Tiesiog
į užklausą įterpiame LIKE('%kazkas%') ir gauname norimus rezultatus. Tačiau šio metodo trūkumai -
greitis, galimybės (tarkime norime
rezultatų 'Bill+Gates-Windows' ir nepatogus naudojimas - Rails nėra pagalbinių metodų LIKE naudojimui,
(tarkim norime teksto ieskoti 15-oje modelio atributų, SQL sakinio dalis atrodytų : "attr1 LIKE('%a%') OR attr2 LIKE('%a%') ir.t.t"). Taigi šis principas - tai iš esmės pavyzdys kaip nereikėtų realizuoti tekstinės paieškos
žiniatinkliuose :). Nors, žinoma, niekas to nedraudžia daryti esant mažiems poreikiams.
MySQL teksto indeksavimas.
Tai patobulinta MySQL alternatyva LIKE sintaksei. Implementacija
paprasta. Kurdami lentelę (MyISAM) tiesiog nurodome FULLTEXT INDEX(name) ir MySQL automatiškai
sukurs indeksų lentelę "name" atributui.
CREATE TABLE test (
id INT(5) PRIMARY KEY NOT NULL AUTO_INCREMENT,
data TEXT
FULLTEXT INDEX(data)
);
Paieška:
SELECT * FROM test WHERE MATCH(data) AGAINST('labas')
Nors ir efektyvesnis būdas nei pirmas, tačiau vistiek turi trūkumų, tarp kurių akivaizdžiausias - naudojimo patogumas. Neišvengiamai tenka visur rašyti SQL (mes juk pripažįstam tik RUBY:), neaiški situacija su Rails
migracijomis, ten tenka naudoti šlykštoką 'execute'. Taip pat atsiranda apribojimas, nebegalime indeksuotoje lentelėje naudoti transakcijų, kadangi MySQL indeksavimas reikalauja MyISAM tipo lentelės.
Aišku, šis tekstinės paieškos realizavimo būdas efektyvesnis nei pirmas, tačiau toli gražu iki mums
įprasto grožio:).
Acts_as_ferret pluginas
Tai pakankamai neseniai atsiradęs ir tikrai vertas dėmesio projektas. Jis naudoja "Ruby" kalba implementuotą "Apache Lucene" indeksuotos paieškos variklį. Diegimas paprastas:
gem install ferret
### įkeliam acts_as_ferret pluginą:
script/plugin install svn://projects.jkraemer.net/acts_as_ferret/tags/stable/acts_as_ferret.
### Modelyje, kuriame norime indeksuoti atributus, nurodome:
acts_as_ferret :fields => [:attr1, :attr2]
Perkrovę aplikacijos serverį turime reikalingą funkcionalumą. Galime daryti paiešką naudodami
find_by_contents metodą savo modelyje.
@total, @results = People.find_by_contents('Bill+Gates-Windows')
Acts_as_ferret veikimo principas paremtas tuo, kad nurodytus atributus jis stebi(callbacks) aplikacijos lygmenyje ir po
pasikeitimų indeksuoja, jeigu dirbant "development" rėžimu paprastame faile Rails aplikacijos 'index'
kataloge, jeigu 'production' - atskirame 'Drb' serveryje, kurio konfigūravimas panašus į paprastos DB konfigūravimą
(YAML sintakse). Taigi 'find_by_contents' metodas pagal paieškos užklausą iš indeksų surenka atitikusių įrašų 'id' masyvą, ir iš MySQL lentelės išrenka įrašus pagal tuos 'id'.
Acts_as_ferret galimybės - įspūdingos. Galima paieška pagal tam tikrą žymių ('wildcards') sistemą, rastų atitikmenų
paryškinimas (panašiai į Google), protingo rūšiavimo (kai vieni įrašai labiau atitinka paiešką nei kiti) galimybė
ir t.t. Įdomus acts_as_ferret su MySQL text search palyginimas :
full-text-search-in-ruby-on-rails
.
Vienintelis rimtas trūkumas, kaip ir daugelyje Rails pluginų:
documentation.nil? == true.
Todėl rimtesnių informacijos šaltinių nedaug:
acts-as-ferret-tutorial
full-text-search-in-ruby-on-rails-3-ferret
Acts_as_sphinx pluginas.
Naudoti acts_as_sphinx pluginą rekomenduočiau tiems, kurie kaifuoja dėl gero greičio (performance). Pagal atsiliepimus, šis pluginas naudojantis kaimyno ruso programuotojo Andrew Aksyonoff sphinx variklį, greitas kaip velnias. Galybėmis 'sphinx' panašus su 'ferret', tačiau kaip žinia visur kur norisi įspūdingo greičio, reikia
visai neįspūdingo krapštymosi po žemo lygmens detales. Kruopščios priežiūros reikalaujantis 'sphinx', ne išimtis.
Diegimo ir priežiūros etapai šiek tiek atbaidantys. Keista dar ir plugino kūrėjų pozicija, neindeksuoti įrašų
aplikacijos lygmenyje. Taigi tektų su kokiu nors išoriniu 'cron' automatiniu užduočių paleidimo mechanizmu kas tam tikrus laiko intervalus atnaujinti indeksus. Rezultate - arba nuolat per DB lenteles 'vaikštantis' išorinis procesas, arba ne realaus laiko paieška, nes tik įvedę įrašą jo tam tikrą laiką nerasime.
Taigi paminėti pluginai, labiau tiktų norint tikrai profesionalaus sprendimo,
'pigiam' kodui pakanka ir tiesioginio MySQL DB naudojimo.
Tags acts_as_ferret, full, mysql, rails, ruby, search, sphinx, text | 3 comments | no trackbacks
Posted by Mindaugas Kurlavičius
Tue, 04 Sep 2007 19:32:00 GMT
Beveik kievienoje tinklo aplikacijoje yra reikalingas įrašų puslapiavimas. Kas tai
yra, labai lengva suprasti apsilankius Google puslapyje. Ten visi paieškos rezultatai suskirstyti
po 10 ir pateikiami atskiruose puslapiuose, į kuriuos nuorodos yra tvarkingai sudėliotos
rezultatų apačioje.
Programiniame lygmenyje puslapiavimo algoritmas nėra labai sudėtingas, aplikacijai reikalingi tik du parametrai -
įrašų skaičius vienam puslapiui
(:limit), ir norimas puslapis
(:page).
Su šiais parametrais galime apskaičiuoti trečią parametrą -
(:offset) pagal formulę
offset = limit * (page - 1) ir formuoti efektyvias SQL užklausas kiekvienos
užklausos metu iš DB pasiimdami tik reikalingą kiekį užrašų einamajam puslapiui. Paprasta, tačiau puslapiavimas vis dėl to yra nemenkas galvos skausmas programuotojams, o "Rails" bendruomenėje
ima vyrauti nuomonė, jog pačio paprasčiausio "Rails" puslapiavimo geriau išvis nenaudoti :
Things you shoudnt be doing in Rails.
Dokumentacijoje nurodytas metodas:
@person_pages, @people = paginate(
:people,
:order => 'last_name, first_name'
)
atrodo labai patraukliai,
tačiau praktikoje iš jo gali būti per mažai naudos. Jo trūkumas yra tas, kad negalima perduoti
einamojo puslapio parametro, t.y. jis naudoja params[:page], taigi tokio modelio puslapyje,
kur einamasis puslapis visada turi būti fone, nesvarbu, kokį kitokį veiksmą atlikome, prie kiekvienos
nuorodos turime "prikabinti"
params[:page]. Be, to tradicinis pagalbinis metodas "pagination_links" retai
tinka praktiškai.
Patogus būdas (padedantis išvengti galvos skausmo) realizuoti puslapiavimą ,
tai pasidaryti rankomis pagalbinį metodą (kontrolerio arba bibliotekos) naudojantį žemesnio lygio puslapiavimo
metodus.
def my_pagination(user, limit, page)
@total = Record.count(:conditions => {:user_id => user.id})
@records = Record.find(
:all,
:conditions => {:user_id => user.id },
:order => 'created_at DESC',
:limit => limit,
:offset => limit.to_i * (page.to_i - 1)
)
@record_pages = Paginator.new(self, @total, limit, page)
end
Tokį metodą naudojant kontrolerio veiksmuose (action), gaunami trys kintamieji iš kurių vėliau
nesunku pasidaryti nuorodas į puslapius.
Taip pat, patogu yra
params[:page]
parametrą "prikabinti" tik prie nuorodų į įrašų puslapius, ir gavus aplikacijoje išsaugoti
arba sesijos kintamajame arba kokiuose nors vartotojo nustatymuose, o užklausose, kuriose
params[:page] nėra,
naudoti išsaugotą parametrą.
Tags custom, pagination, rails, ruby | no comments | no trackbacks
Posted by Saulius Grigaitis
Sat, 14 Jul 2007 19:30:00 GMT
Sveiki! Kad ir kaip mėgstame ir žavimės "REST'u" ir "ActiveResource'u", labai daug naudingų WEB servisų naudoją kitus protokolus, pavyzdžiui SOAP. Šįkart trumpai apie tai, kaip lengvai galima pasiekti WEB servisą SOAP protokolu, naudojantis Ruby priemonėmis. Standartinėje Ruby bibliotekoje priemonės SOAP'ui yra gerokai pasenusios, tad verta naudotis "soap4r" "gem'u". Šiuo metu stabili versija yra 1.5.6.
gem install soap4r --source http://dev.ctor.org/download/ --version 1.5.6
Belieka susirasti norimo WEB serviso aprašą WSDL formatu ir pasirašyti paprastą scriptą:
#!/usr/bin/env ruby
require 'soap/rpc/driver'
require 'soap/wsdlDriver'
driver = SOAP::WSDLDriverFactory.new("wsdl_failo_url").create_rpc_driver
#klaidų ieškojimo rėžime(su "-d" raktu ruby interpretoriui) spausdiname visą "dump'ą"
driver.wiredump_dev = STDERR if $DEBUG
#parametrai "hash'e" iš argumentų raktų ir jų reikšmų, informacijos apie tai galima rasti dominančio WEB serviso API dokumentacijoje
@results = driver.lc(:arg1 => "argumentas", :arg2 => "argumentas")
Iki!
Tags ruby, service, SOAP, soap4r | no comments | no trackbacks
Posted by Saulius Grigaitis
Fri, 06 Jul 2007 19:29:00 GMT
Sveiki. Pagaliau baigėsi bakalauriniai, stojamieji ir visi kiti akademiniai reikalai, tad dažniau rašysiu straipsnius. Šį kartą tęsiu pažintį su RSpec ir kontrolerių testavimu. Kontrolerių testavimo techninė dalis yra analogiška jau aptartoms dalims, bei turi keletą naujų dalykų, tačiau koncepciniai dalykai daug įdomesni. Juos ir aptarsiu šiame straipsnyje.
Visų pirma - RSpec'as propaguoja ne klasikinį testavimą, o "mock'inimą". Klasikiniame testavime visi testai surišti tarpusavyje panaudojant kiek galima tikrų objetų, o netikri objektai("mocks", "stubs" ir kita) yra naudojami tik tada, kai nėra kitos galimybės, pvz. dar neturint priėjimo prie WEB serviso, nes jis dar nesukurtas, bet turint jo API, bei darant prielaidą, kad servisas tikrai veiks korektiškai naudojantis pateiktu API. Tuo tarpu "Mock'istai" naudoja netikrus objektus kur tik gali ir tai turi prasmę. Iš esmės tai yra visiškai skirtingos koncepcijos, turinčios savo privalumų ir trūkumų. Pagrindiniai skirtumai yra testų integracija ir programavimo stilius. Klasikiniai testuotojai rašo testus, kurie surišti tarpusavyje, o "mock'istų" testai dažniausiai visiškai nepriklauso vienas nuo kito, tad klasikinių testuotojų testai yra integraciniai, o "mock'istų" - ne. Pvz. klasikinis testuotojas rašydamas kontrolerio testą nusprendžia, kad jam reikia naujo modelio "Book" klasės metodo "pages", jam nelieka nieko kito, kaip imti ir parašyti "Book" klasės metodo "pages" testą ir tada patį metodą "pages", geriausiu atveju - paprašyti kolegos, kad jis tai padarytų:). Gal viskas tuo gerai baigtųsi, jei nepaaiškėtu, kad "pages" metodui reikia dar kito metodo, kuris dar neparašytas, o kolega jau ir taip užimtas:). Taigi Klasikinis testuotojas norėjo parašyti kontrolerio testą ir patį kontrolerį, bet jam teko įsivelti į modelių metodų rašymą. Dažnai šita grandinė išauga iki įspūdingo ilgio. Tuo tarpu "mock'istas" daro prielaidą, kad modelis turi reikiamą metodą ar jį turės ir pasitiki jo veikimo korektiškumu, nes to metodo autorius jį jau ištestavo. Naudojant RSpec, tai atrodytų taip:
...
@book = mock_model("book")
@book.should_receive(:pages).and_return(100)
...
toks testas praeis sėkmingai net jei "Book" klasė ir neturės metodo "pages", tačiau programuotojas gali susikoncentruoti ir galvoti kaip geriau parašyti testą ir patį kontrolerį, o ne šokinėti prie kitų programos dalių.
Iš vienos pusės "mock'istams" lengviau programuoti, tačiau jie praranda testų integraciją, o klasikinimas testuotojams programuoti žymiai sunkiau, tačiau jie turi didesnę kodo kontrolę, nes integruoti testai geriau indikuoja pažeistas vietas po kodo pakeitimų. Klasikiniams testuotojams yra kur kas sunkiau programuoti didesnėse komandose lygiagrečiai, tuo tarpu "mock'istams" tai nedaro įtakos. Tačiau praktikoje kartais "mock'istams" sunku imituoti sudėtingų objektų elgseną, tad priveliama klaidų į pačius testus. Abi technikos turi savų pliusų ir minusų, jei dar neapsisprendei kuri tau tinkamesnė, paskaityk:
Martin Fowler - Mocks Aren't Stubs
RSpec Documentation - Mocks
Iki!
Tags BDD, Mocks, rails, rspec, ruby, Stubs | no comments | no trackbacks
Posted by Saulius Grigaitis
Thu, 14 Jun 2007 19:28:00 GMT
Sveiki! Kaip sekasi ieškoti tinkamo hostingo tinklapiams, sukurtiems su Ruby on Rais? Mums nelabai, tad kilo idėja įkurti savo hostingą. Iš esmės, yra du variantai:
1) rimtas variantas - kiekvienam vartotojui izoliuotas jail'as su administratoriaus teisėmis jame Unix tipo sistemoje(pvz. OS FreeBSD), tad yra galimybė jame šeimininkauti, bet neturėti jokio poveikio kitiems vartotojams, o kartu nenukentėti pačiam. Būtų galimybė susiinstaliuoti norimus gemsus, reikiamus servisus. Trumpiau kalbant, privatus virtualus serveris su paruoštu steku (Apache 2.2, MySQL/PostgreSQL, Mongrel cluster) Ruby on Rais tinklapiams, valdomas per paruoštą Capistrano skriptą. Minusai - neaišku kiek potencialių vartotojų laisvai jaučiasi Unix tipo operacinėse sistemose, kiek iš jų naudoją Subversion ar kitą Capistrano palaikomą versijų kontrolės sistemą, ar, apskritai, turi noro ir galimybių naudoti Capistrano? Privalumai - bene geriausias sprendimas rinkoje, sunku ir sugalvoti ką nors geresnio.
2) paprastesnis - atsisakyti kai kurio Capistrano funkcionalumo ir automatizuoti pagrindines funkcijas, o likusias palikti administratoriui. Tai gal būt būtų izoliuotas jail'as, o gal ir paprastesnis saugumo sprendimas. Tačiau pagrindinis privalumas yra paprastumas, failai būtų įkeliami pasinaudojant FTP ar kuo nors saugesniu, apribotas priėjimas per SSH, o visas diegimas ir valdymas būtų atliekamas per WEB sąsają. Privalumai - lengva pasinaudoti iš praktiškai bet kokios OS be didelių pastangų. Minusai - apkarpytas automatizuotas funkcionalumas, daug likusio darbo administratoriui, o gerai pagalvojus - visai neprofesionalus sprendimas.
Tikslinė rinka - Lietuva ir šalys aplink ją, o vėliau - globali rinka.
Taigi balsuokite komentaruose už norimą variantą, laukiame ir nuomonių, pasiūlymų, diskusijų, ypač aktyviai dalyvaukite tie, kuriems išties aktuali ši paslauga!
Tags hosting, rails, ruby | no comments | no trackbacks
Posted by Saulius Grigaitis
Mon, 07 May 2007 19:20:00 GMT
Sveiki. Pagaliau radau šiek tiek laisvo laiko. Šįkart kiek linksmiau:

RSpec!
BDD yra gana jauna metodika, evoliucionavusi iš gerai žinomo TDD(angl. Test Driven Development). TDD pagrindinė idėja yra kodą rašyti tik po to, kai parašomas tą kodą testuojantis testas. BDD perima šią metodiką, tačiau testo struktūra labiau panaši į specifikaciją ir ją iliustruojantį kodą, nei į sausus testus. Taigi naudojant BDD parašomas dokumentas, panašus į specifikaciją. Bene vienintelis patogus ir naudingas BDD įrankis Rails'ams yra
RSpec. Jis leidžia testuoti modelius, kontrolerius, view'us ir helper'ius. Taigi pradedam nuo modelių, paprastai, ten būna daugiausia logikos, kuri tiesiog prašosi ištestuojama.
./script/generate rspec_model notebook
mėgstamu editoriumi parašome pirmą spec'ą - užrašų knygutės turi turėti 100 puslapių
# spec/models/notebook.rb<br/>
require File.dirname(__FILE__) + '/../spec_helper'
describe "New notebook" do
fixtures :notebooks
before(:each) do
@notebook = Notebook(:my)
end
it "should have 100 pages" do
@notebook.pages.should eql(100)
end
end
ir fixture:
# spec/fixtures/notebook.yml
my:
id: 1
pages: 3
paleidžiame testą:
# ruby spec/models/notebook_spec.rb F
1)#ActiveRecord::StatementInvalid in "New notebook should have at least 2 pages"
Mysql::Error: Table 'project_test.notebooks' doesn't exist: SHOW FIELDS FROM notebooks
spec/models/notebook_spec.rb:5:in `new'
spec/models/notebook_spec.rb:5:
spec/models/notebook_spec.rb:3:
Finished in 0.482204 seconds
1 example, 1 failure
priminė, kad neturime notebooks lentelės. Perleidžiam migracijas ir klonuojame development duombazės struktūrą į test duombazę.
rake db:migrate
rake db:test:clone_structure
Vėl perleidžiame testą:
#ruby spec/models/notebook_spec.rb
F
1)#ActiveRecord::StatementInvalid in 'New notebook should have at least 2 pages'
Mysql::Error: Unknown column 'pages' in 'field list': INSERT INTO notebooks (`id`, `pages`) VALUES (1, 3)
spec/models/notebook_spec.rb:3:
Finished in 0.074569 seconds
1 example, 1 failure#
Hmm...neturime lauko "pages" , tad sugeneruojame migraciją
./script/generate migration AddColumnPagesToNotebook
ir ją užpildome:
class AddColumnPagesToNotebook < ActiveRecord::Migration
def self.up
add_column :notebooks, :pages, :integer
end
def self.down
end
end
perleidžiame migraciją ir klonuojame sturktūrą:
rake db:migrate
rake db:test:clone_structure
vėl testas
#ruby spec/models/notebook_spec.rb
F
1)#'New notebook should have 100 pages' FAILED
expected 100, got 3 (using .eql?)
spec/models/notebook_spec.rb:11:
spec/models/notebook_spec.rb:3:
Finished in 0.093053 seconds
1 example, 1 failure
Testas puikiai indikuoja, kad yra klaida naujos užrašų knygelės puslapių skaičiuje, tad pataisome fixture į:
# spec/fixtures/notebook.yml
my:
id: 1
pages: 100
perleidžiam testą:
#ruby spec/models/notebook_spec.rb
.
Finished in 0.140526 seconds
1 example, 0 failures#
pagaliau praėjome testą!!!Tvarkoj, dabar mums norime, kad išplėšus lapą iš užrašų knygutės, puslapių skaičius sumažėtų vienu. Pridedam:
#spec/models/notebook_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
describe "New notebook" do
fixtures :notebooks
before(:each) do
@notebook = notebooks(:my)
end
it "should have a 100 pages" do
@notebook.pages.should eql(100)
end
it "should have one page less after removing one page" do
pages_before = @notebook.pages
@notebook.remove_page
@notebook.pages.should eql(pages_before - 1)
end
end
perleidžiam testą:
ruby spec/models/notebook_spec.rb
.F
1)
NoMethodError in 'New notebook should have one page less after removing one page'
undefined method `remove_page' for #<Notebook:0x9a85aa8 @attributes={ id => 1 , pages => 100 }>
spec/models/notebook_spec.rb:16:
spec/models/notebook_spec.rb:3:
Finished in 0.101506 seconds
2 examples, 1 failure
Nepasiskė...neturime tokio metodo, tad sukuriame jį:
#app/models/notebook.rb
class Notebook < ActiveRecord::Base
def remove_page
end
end
Testuojam:
ruby spec/models/notebook_spec.rb
.F
1)
'New notebook should have one page less after removing one page' FAILED
expected 99, got 100 (using .eql?)
spec/models/notebook_spec.rb:17:
spec/models/notebook_spec.rb:3:
Finished in 0.120552 seconds
2 examples, 1 failure
Hmm...puslapių skaičius nesumažėjo, tad ištaisome klaidą:
#app/models/notebook.rb
class Notebook < ActiveRecord::Base
def remove_page
self.pages -= 1
end
end
Testuojam:
# ruby spec/models/notebook_spec.rb
..
Finished in 0.106724 seconds
2 examples, 0 failures
Pagaliau, po ilgos darbo dienos kodas praeina testus, tad galime ramiai eiti namo:) O jei rimtai, tai idėja yra labai paprasta - rašome specifikaciją, o testas pats indikuoja ką programuotuoju reikia padaryti, kad kodas atitiktų specifikaciją. Be to, išlieka ir visi TDD privalumai: užaugus sistemai, gana paprasta daryti pakeitimus, nes testai indikuoja vietas, kurios yra paveikiamos pakeitimo. Nereikia galvoti apie visą sistemą, o spręsti tik mažą specifikacijos reikalavimą, jei tai paliestų kitas sistemos dalis, testai tai parodytų, ir kita. Beje, ar pastebėjote, kad nebuvo naudota naršyklė. Būtent, naudojant šią metodiką nebereikia "mėgėjiškai" ieškoti klaidų.
Tags BDD, rails, rspec, ruby | no comments | no trackbacks
Posted by Saulius Grigaitis
Sat, 28 Apr 2007 19:17:00 GMT
Sveiki. Vienas dalykas, kuris nuolat mane žavi dirbant su Ruby ir Rails, tai kalnai kokybiško ir naudingo kodo, kuriuo gana lengva pasinaudoti. Šįkart trumpai apie centralizuotą autentikacijos servisą(CAS).
CAS - servisas, suteikiantis "Single Sign-On" mechanizmą. Praktiškas pavyzdys - google.com. Turite vieną paskyrą (angl. "account"), prisijungiate prie sistemos vieną kartą ir galite naudotis visais servisais, nors jie yra visiškai skirtinguose serveriuose, stovi po atskirais domenais ir išsibarstę po visą pasaulį. Trumpai apie patį paprasčiausią CAS protokolą, kuris leidžia tai įgyvendinti paprastose sistemose. Reikalingas CAS serveris ir klientas, įsimylėjusiems Ruby -
rubycas-server ir
rubycas-client. Be abejo, reikalinga šiek tiek pasiskaityti dokumentaciją ir sukonfigūruoti juos, tai gana paprasta, tereikia išsirinkti CAS servisą aptarnaujantį serverį, surašyti domenus ir panašiai. Dabar įdomiausia dalis - autentikacijos procesas:
1. Jungiatės prie serverio klientodomenas.lt, kuris jus automatiškai nukreipia į CAS serverį
https://serveriodomenas.lt/login?service=http://klientodomenas.lt
2. Atlikus sėkmingą autentikaciją, jus nukreipia atgal, bet jau su prikabintu bilietu
http://klientodomenas.lt/?ticket=ST-956-Lyg0BdLkgdrBO9W17bXS
3. O dabar iškeičiame bilietą į vartotojo vardą
https://serveriodomenas.lt/serviceValidate?ticket=ST-956-Lyg0BdLkgdrBO9W17bXS&service=http://klientodomenas.lt
4. Gauname vartotojo vardą:
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>vartotojo_vardas</cas:user>
</cas:authenticationSuccess>
</cas:serviceResponse>
O jungiantis iš kito CAS kliento, jūs atpažįstamas pagal sausainiuką, tad nebereikia atlikti autentifikaciją dar kartą.
Be abejo, visu tuo mechanizmu rūpinasi
rubycas-server ir
rubycas-client, jums tereikia juos susikonfigūruoti ir naudoti.
Tags authentication, cas, client, rails, ruby, rubycas, server | no comments | no trackbacks
Posted by Saulius Grigaitis
Tue, 24 Apr 2007 19:10:00 GMT
Sveiki, tęsiam globalizacijos/lokalizacijos problemą. Šiandien prireikė korektiško laiko nustatymo mechanizmo skirtingose laiko juostose esantiems vartotojams. Deja, standartinės Rails priemonės nesusitvarko su lygiadieniais, t.y. apie pusmetį kasmet turėsite neteisingą laiką :). Klientų tai visai nenudžiugintų, tad teko rasti sprendimą. Bene vienintelis patogus būdas tinkantis 1.2 versijos Rails karkasui yra
tzinfo_timezone įskiepis, kuriam taip pat reikia tzinfo gem'o. Nurodom Rails'ų Active Record'ui naudoti UTC laiką, nustatydami config.active_record.default_timezone = :utc config/environment.rb faile. Pridedame vartotojų lentelėje stulpelį "time_zone". Na ir belieka pasirašyti helperius laiko konvertavimui iš UTC į vartotojo lokalų laiką ir atvirkščiai, metodo "utc_to_local" pagalba. Iki!
Tags rails, ruby, timezone | no comments | no trackbacks
Posted by Mindaugas Kurlavičius
Mon, 23 Apr 2007 19:08:00 GMT
Tinklapis be paveikslėlių? Na, nebent kokioje dokumentacijoje jie nereikalingi, visur kitur jau beveik nebeišsiverčiama be vaizdo ar garso klipų, ką jau kalbėti apie paveikslėlius. Logotipai, nuotraukos šiaip visumą pagyvinantys elementai... Ir svarbiausia, turi būti galimybė visą tai įkelti paprastam nepriviligijuotam vartotojui. Be to, vartotojui neturi rūpėti įkeliamo paveikslėlio dydis, t.y. tuo turi pasirūpinti tinklapio serveris.
Taigi, išties aktualus klausimas praktiškai kiekvieno tinklapio kūrėjui. Kaipgi į jį atsako "Rails"?
Deja, pats "Rails" karkasas kol kas nieko nesiūlo. Tenka kapstytis tarp trečių šalių deimančiukų (gems) ir įskiepių (plugins). Siūlomos dvi dėmesio vertos alternatyvos:
"Acts as attachment"
ir
"File Column".
Abu įskiepiai dėl manipuliavimo paveikslėlių parametrais reikalauja "RMagick" bibliotekos, kurią man teko matyti tik "Unix" ir "Linux"
platformose, bet atrodo nesunkiai galima įdiegti ir į visų taip mėgiamus "Windows".
Acts as attachment - galingas ir lankstus, sakyčiau netgi šiek tiek griozdiškas įrankis, leidžiantis įkraunamus failus saugoti DB arba failų sistemoje ir patį įkraunamą failą realizuoti kaip modelį saugomą lentelėje, kurį vėliau būtų galima perdengti kitais modeliais. Tarkime:
class File < ActiveRecord::Base
end
class Picture < File
# Paveikslėlio matmenys, leidžiami tipai, max dydis ir t.t
end
class Xxx < File
# ...
end
Žodžiu, per daug nesigilinant, "acts_as_attachement" API leidžia pasireikšti fantazijai, galima išspręsti sudėtingą specifinę problemą, tačiau esminis šio įskiepio trūkumas - pakankamai sudėtingas valdymo mechanizmas, be to dažniausiai tereikia tik 5% jo galimybių, o joms realizuoti nemažai laiko.
File Column - lengvesnis įrankis. Paveikslėliai (galimi ir kitokie failai) saugomi failų sistemoje, nereikia atskiro modelio ir atskiros lentelės, failus tiesiog "prikabiname" per papildomą stulpelį DB, prie jau egzistuojančio modelio. Vaizdo elementuose (rhtml) pasiekiame tuos failiukus, paruoštais pagalbiniais metodais (helpers). Taip pat "File Column" automatiškai saugo tarpinį, jau pakrautą į formą, paveiksliuką. Tad, klientui neteisingai užpildžius tam tikrus formos laukus ir "renderinus" vaizdo elementą atgal, paveikslėlis bus laikinai išsaugomas į katalogą serveryje ir tik priėmus teisingai užpildytą formą, "prikabintas" į failų sistemą ir duomenų bazę.
# Migracija
class CreateBooks < ActiveRecord::Migration
def self.up
create_table "books" do |t|
#t.column :name, :string
#t.column :author, :string
t.column :cover, :string
end
end
def self.down
drop_table "books"
end
end
# Modelis
class Books < ActiveRecord::Base
file_column :cover, :magick => {:geometry => "100x200>"}
end
# Vaizdas
<%= image_tag url_for_file_column(@book, "cover") %>
Tags files, image, picture, rails, ruby | no comments | no trackbacks
Posted by Saulius Grigaitis
Sun, 22 Apr 2007 19:04:00 GMT
Sveiki. Ramus sekmadienio vakaras. Ryt vėl įsuks į darbų verpetą, tad puikus metas apšilti ir parašyti pirmą techninį šio blogo straipsnį.
Šiuo metu vystome projektą, kuriame daugiakalbystė vaidina ne paskutinį vaidmenį, tad draugausime su Unicode'u. Prireiks Ruby on Rails 1.2 ar naujesnės versijos ir keturių žingsnių:
1. Susitvarkome savo redaktorius taip, kad būtų naudojama utf-8 koduotė(dažnas šiuolaikinis redaktorius tai jau padarė pats).
2. Užtikriname, kad kiekvieno view'so antraštėje nurodyta utf-8 koduotė:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
...
</head>
3.Nurodome adapteriui, kad naudotų utf-8 koduotę faile config/database.yml:
development:
adapter: mysql
database: sample
username: root
password:
encoding: utf8
...
Naudojant PostgreSQL, "utf8" keičiame "unicode".
4.Na ir linksmiausia dalis - nurodome duomenų bazių valdymo sistemai(DBVS), kad ji naudoti utf-8. Kiekvienai DBVS tai atliekama skirtingai, parodysiu kaip tai padaryti MySQL DBVS. Susirandame my.cnf failą, paprastai jis guli /etc direktorijoje Unix tipo sistemose. Papildome "[mysqld]" sekciją dviem įrašais:
[mysqld]
character-set-server = utf8
default-collation= utf8_unicode_ci
...
Perkrauname. Viskas. Tolesniam programų globalizavimui rekomenduoju Globalize įskiepį, bet apie jį vėliau. Iki!
Tags mysql, rails, ruby, unicode | no comments | no trackbacks