RubyConfLT 2009 skaidrės, meetup'ai...

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:

Konferencijos įrašai čia

Tags , , , , , ,  | 4 comments | no trackbacks

RubyConfLT 2009 !

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 , , ,  | 12 comments | 2 trackbacks

RubyConfLT 1.2008 įspūdžiai

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:;

RubyConfLT 1.2008

Ir pranešimų skaidrės:

  • Artūras Šlajus:
  • Eimantas Vaičiūnas:
  • Saulius Grigaitis:
  • Fiodor Vereščiaka:
  • Laurynas Liutkus:
  • Tags , , , ,  | 3 comments | no trackbacks

    Pavasaris!

    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 , ,  | no comments | no trackbacks

    OpenWFEru: Workflow and Business Process Machine engine(Introduction)

    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 , , ,  | 4 comments | no trackbacks

    Netbeans 6 - best IDE for Ruby and Rails?

    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 , , , , ,  | no comments | no trackbacks

    Rails tekstinė paieška (Full text search)

    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:

    1. MYSQL, LIKE sintaksė.
    2. MYSQL implementuotas teksto indeksavimas.
    3. Acts_as_ferret pluginas.
    4. 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 , , , , , , ,  | 3 comments | no trackbacks

    "Rails" Rankinis puslapiavimas (Pagination)

    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 , , ,  | no comments | no trackbacks

    WEB servisai - SOAP klientas

    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 , , ,  | no comments | no trackbacks

    "Elgsena paremtas" programavimas(angl. Behaviour Driven Development). III dalis.

    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 , , , , ,  | no comments | no trackbacks

    Ruby on Rails hostingas

    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 , ,  | no comments | no trackbacks

    "Elgsena paremtas" programavimas(angl. Behaviour Driven Development). I dalis.

    Posted by Saulius Grigaitis Mon, 07 May 2007 19:20:00 GMT

    Sveiki. Pagaliau radau šiek tiek laisvo laiko. Šįkart kiek linksmiau:RSPEC!
    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 , , ,  | no comments | no trackbacks

    Centralizuota autentikacija

    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 , , , , , ,  | no comments | no trackbacks

    Laiko juostos

    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 , ,  | no comments | no trackbacks

    Paveikslėlių įkrovimas

    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 , , , ,  | no comments | no trackbacks

    Unicode'as Ruby on Rails programose

    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 , , ,  | no comments | no trackbacks