Archive for the 'Ruby' Category

Lepsza konsola w railsach

niedziela, wrzesień 2nd, 2007 by Paweł Rutkowski

Na pewno większość z Was wie, że RubyOnRails jest wyposażone w tekstową konsolę (./script/console). Po jej uruchomieniu zostaje załadowane środowisko rails a użytkownik dostaje konsolę, na której może interaktywnie wykonywać kod rubiego. Przydaje się to debugowania aplikacji, ręcznego modyfikowania danych czy prostego (i szybkiego) prototypowania.

Aby uczynić pracę z konsolą wygodniejszą, powstał “gem” o nazwie “wirble“. Rozszerza on funkcjonalnosc konsoli o kolorowanie składni oraz stałą historię poleceń (oryginalna konsola posiadała historię tylko w obrębie jednej sesji - po wyjściu z niej nie była zapamiętywana)

Note: akurat historia poleceń nie zawdzięczamy samemu wirble - natywna konsola posiada tą opcję, a wirble dokonuje tylko jej aktywizacji

Instalacja gemu jak zwykle standardowo:


gem install wirble

Przykładowy plik konfiguracyjny ~/.irbrc


1 require 'rubygems'
2 require 'wirble'
3 Wirble.init
4 Wirble.colorize
5 IRB.conf[:AUTO_INDENT] = true
6 IRB.conf[:IRB_RC] = proc do |conf|
7 leader = " " * conf.irb_name.length
8 conf.prompt_i = "#{conf.irb_name} > "
9 conf.prompt_s = leader + ' \" '
10 conf.prompt_c = leader + ' \+ '
11 conf.return_format = leader + " ==> %s\n\n"
12 puts "Welcome to better IRB!"
13 end

PS: plik .irbrc jest normalnym skryptem rubiego, więc można tam umieścić normalny kod - co zresztą widać po lini 12.

Prezentacja z FreeCONa - RubyOnRails Hosting aplikacji w porównaniu z PHP

wtorek, kwiecień 24th, 2007 by Paweł Rutkowski

W dniach 21-22 Kwietnia 2007 odbyła się druga edycja konferencji FreeCON. Podobnie jak poprzednio miejscem konferencji byl Wroclaw ktory ostatnio mocno sie rozwija pod katem biznes IT. Wyklady ciekawe, ale jak zawsze najciekawsze były dyskusje w przerwach oraz afterparty :)

Zgodnie z tradycją zamiescilem prezentacje z konferencji FreeCON 2007 - jak zwykle dostepna w dziale prezentacje.

Czym są szablony HAML ?

piątek, styczeń 19th, 2007 by Paweł Rutkowski

Razem z pojawieniem się nowych railsów o których pisałem w poprzednim poście, pojawiła się także informacja o nowym “formacie” templatów - “Haml”. Generalnie jest to nowy meta-język doskonale wpisujący się w ideologię railsów - DRY i Simplifying - upraszczania.

Odrazu przejdę do przykładu, żeby nie trzymać Was w niepewności. Oto jakby wyglądał kod w standardowym RHTMLu:


<table id="userdata">
<tr>
<td class="label"> Dane użytkownika </td>
<td> <%= @user.name %> </td>
<td> <%= @user.surname %> </td>
</tr>
</table>

Krótkie wyjaśnienie dla osób nie znających szablonów Erb (używanych w RHTMLu). Znacznik <= służy do wykonania kodu ruby i wstawienia wyniku w dane miejsce. Jak widać mamy tabelke z jakimś id i jeden z znaczników <td> ma ustawioną klase “label”. Jak by zatem wyglądał powyższy kod w Hamlu. Moim zdaniem znacznie przyjemniej:


table#userdata
%tr
%td.label Dane użytkownika
%td= @user.name
%td= @user.surname

Dzięki temu skracamy kod i czas jego pisania o mniej więcej połowę (nie trzeba zamykać znaczników). Ponieważ Haml został stworzony z myślą o XHTMLu/XMLu, kod przez niego wygenerowany będzie spełniał założenia tego formatu. Drugą rzeczą która mi się bardzo spodobała to selektory zgodne z formatu CSS - bardzo to ułatwia zapamiętanie składni. Trzecia rzecz - znak “równa się” odrazu uwidacznia podstawienie efektu wykonania kodu w Ruby.

Uproszczono także wstawianie dowolnych atrybutów do kodu - co jest niezwykle ważne w przypadku generowania XMLa:


%person { :name => "Pawel", :surname => @user.surname }
%children/

Powyższy kod zostanie przekształcony na:


<person name="Pawel" surname="Rutkowski" >
<children/>
</person>

Jak widać na powyższym przykładzie dodawanie atrybutów jest bardzo proste i można w nich używać wyrażeń języka. W RHTMLu powyższy szablon wygłądał by tak:


<person name="Pawel" surname="<%= @user.name %>" >
<children/>
</person>

Zagnieżdżanie kodu Ruby wewnątrz tagów zawsze mnie irytowało ze względu na mała czytelność takiego kodu. Natomiast w tym rozwiązaniu wygląda to świetnie.

To jest mała porcja tego co potrafi Haml. Dokumentacja jest obszerna i dość długa - jeszcze nie zapoznałem się z jej całą treścią. Haml jest dostępny dla Rubego w postaci gemów, zaś dla railsów na razie w w postaci pluginu który można pobrać stąd:


./script/plugin install svn://hamptoncatlin.com/haml/tags/stable

Niestety nie miałem jeszcze czasu na przepisanie którejś z aplikacji na te szablony i porównania czasów generowania i ogólnej wydajności, ale jak tylko to zrobię na pewno opublikuję

Strona projektu Haml (razem z dokumentacją i tutorialem) - tutaj

A na koniec jeszcze przykładowa strona w Hamlu :)


!!!
%html
%head
%title= controller.controller_name
= stylesheet_link_tag 'main'
%body
#header
%h1 BoBlog
#content= yield
#footer
%p All content copyright © Bob

Czyż to nie jest piękne ?:)

Prezentacja “Plug me in” - o pluginach słów kilka

niedziela, styczeń 14th, 2007 by Paweł Rutkowski

W sobotę obyło się kolejne spotkanie z cyklu RoR PL. Miałem przyjemność poprowadzić prezentację należącą do technicznej części tego spotkania. Temat był trudny zarówno do przedstawienia, jak i w odbiorze przez osoby nie obeznane z programowaniem obiektowym - ale mam nadzieję że udało mi się przybliżyć Wam aspekty omówione podczas prezentacji.

Jak zwykle po prezentacji, udostępniam materiały tutaj.

Jak zrobić mechanizm pluginów w Ruby

wtorek, grudzień 12th, 2006 by Paweł Rutkowski

Podczas pracy przy ostatnim projekcie napisałem kilka programów w Ruby. Jeden z nich był frameworkiem udostępniającym pewne API. Głównym zadaniem tego frameworku było usprawnienie operacji na bazie SQL które co jakiś czas musieliśmy wykonywać ręcznie - co było bardzo pracochłonne i wymagało wielkiej uwagi.

Założenia dla frameworku były następujące:

  • udostępniać minimalną funkcjonalność - wspólną dla innych operacji
  • obsługiwać pluginy które realizowały by poszczególne operacje
  • pluginy powinny “myśleć” za użytkownika, tzn weryfikować dane ew. podpowiadać dostępne rozwiązania (ale to będzie temat na oddzielny wpis)

Musiałem zatem wymyślić jakiś mechanizm pluginów. Ponieważ Ruby jest językiem bardzo elastycznym nie było z tym większego problemu.

Pierwszą rzeczą było stworzenie klasy Action - z której będą dziedziczyć wszystkie pluginy. Następnie zaimplementowałem dwie metody: self.description i self.run. Pierwsza z nich była odpowiedzialna za wyświetlanie opisu pluginu, druga zaś uruchamiała właściwy plugin. Ponieważ klasa Action sama w sobie nie była wykorzystywana powyższe metody zwracały tylko informacje że należy je zaimplementować w klasie dziedziczącej.

Drugą rzeczą było udostępnienie jednego z obiektów dla każdego z pluginów. Tutaj sprawa nie była skomplikowana - wystarczyło dodać parametr do konstruktora, przypisać do zmiennej zaś przy tworzeniu obiektu przekazać odpowiednią zmienną.

Oto jak wygląda w uproszczeniu ta klasa


class Action
def initialize(api_object)
@api = api_object
end
def self.description
puts "desc method have to be overriden"
end
# method execute to run plugin
def run
puts "run method have to be overriden"
end
end

Narazie prosto prawda ? No to teraz trzeba było by jakoś ładować pluginy do aplikacji. Stwierdziłem że najlepszą metodą będzie utworzenie katalogu plugins i wczytywanie wszystkich plików które się tam znajdują (z definicją nowej klasy która de facto jest pluginem). Do tego posłuzyłem się następującym kawałkiem kodu:


def load_actions
Dir["plugins/*.rb"].sort.each { |plugin|
require plugin
}
end

I zaraz potem trafiłem na problem. Jak wyciągnąć klasy które dziedziczą z innej klasy ? Pomimo że ruby ma bardzo dobrze rozwinięte “reflections” (jest to możliwość dynamicznego metaprogramowania - np.: sprawdzanie dostępnych metod w obiektach, informacji o zmiennych, ich typach itp) to nie udało mi się znaleźć funkcji oferującej taką funkcjonalność. Trochę czasu mi zajęło przejrzenie metod dostępnych w klasach Object, ObjectSpace, Class i wyszukanie tych z których można skorzystać. Oto jak wyglądał kod (a pod spodem jego omówienie).


class Class
def self.find_by_super(sclass)
ret = []
ObjectSpace.each_object(Class) { |klass|
ret << klass if klass.superclass == sclass
}
return ret
end
end

Jak widać powyżej do klasy Class została dodana metoda find_by_super która jako parametr przyjmuję klasę nadrzędną w stosunku do klas które szukamy. Następnie przechodzimy przez wszystkie obiekty typu Class (czyli wszystkie klasy) i sprawdzamy które z nich dziedziczą z klasy określonej w parametrze funkcji.

Właśnie podczas pisania tego postu zauważyłem że można zrobić z powyższego kodu “jednolinijkowca”.


def self.find_by_super(sclass)
ObjectSpace.each_object(Class).find_all { |klass| klass.superclass == sclass }
end

Następnie należało już tylko dokonać iteracji po pluginach i wyświetlić ich listę w celu dokonania wyboru. Trick polegał na załadowaniu listy pluginów do tablicy, ich wyświetlenie a następnie zinterpretowanie wejścia użytkownika jako indeksu do konkretnego miejsca w tablicy, wskazującego na plugin który go interesuje.


[...]
# szukamy klass < Action
@actions = Class.find_by_super(Action)
[...]
# licznik na 0
n=0
actions.each { |action|
# dla kazdej akcji wywolujemy jej metode
# description i pokazujemy na ekranie
puts "#{n} - #{action.description}"
# zwiekszamy licznik :)
n=+1
}
[...]
printf("Please choose action: ")
# odczytujemy co podal user
@user_choose = $stdin.gets.to_i
# tworzymy obiekt na podstawie wybranej klasy i
# do konstruktora przekazujemy @api
action = @actions[user_choose].new(@api)
[...]
action.run

Proste ? Pewnie po przeczytaniu tego artykułu tak :) Natomiast dla mnie było to swego rodzaju wyzwanie, które trochę czasu zajęło. Było to jednak ciekawe zagadnienie a dzięki Ruby i mozliwością tego języka bardzo przyjemne do zaimplementowania. Oczywiście cały framework oferował znacznie więcej i pojawiło się wiele innych problemów, ale nie chciałem ich tu zamieszczać żeby nie zaćmić ogólnej ideii.

RT i konwersja SQLite do MySQL

czwartek, wrzesień 28th, 2006 by Paweł Rutkowski

Po dość długiej przerwie, nowy wpis :)

Wstęp

Ostatnio wdrażałem w jednej firmie system obsługi zgłoszeń serwisowych - tzw. ticketów. Mój wybór padł na RT: Request Tracker. Jest to narzędzie open-source, oparte o interface webowy. Zdecydowałem się na nie ze względu na łatwą integrację z mailami, co bardzo ułatwiło wdrożenie (szczególnie ze strony Klientów firmy) i nie narażało na koszty szkoleń. Wkońcu każdy potrafi wysłać maila na określony adres :)

Prognozy… mylne…

Przy instalacjach tego typu ruch zazwyczaj jest mały, jak i ilość danych w systemie nie jest oszołomiająca. Dlatego też zdecydowałem się użycie bazy SQLite co by niepotrzebnie nie instalować nowych usług na serwerze. Niestety… popełniłem błąd. Po miesiącu już można było odczuć dyskomfort przy pracy z systemem, natomiast po trzech była to prawdziwa katorga. Ilość danych która się pojawiła w systemie, całkowicie mnie zaskoczyła.

Trzeba sobie radzić…

Troche czasu mi zajeło wymyślenie sposbu migracji. Nie udało mi się znaleźć żadnych gotowych narzędzi do konwersji tak dużych baz sqlite do mysqla, a po mimio że to SQL to jednak dialekty troche inne. Rozwiązanie było dość trywialne:

  1. Zainstalowanie MySQL i skonfigurowanie go (założenie bazy, usera, itp)
  2. Załadowanie schema.mysql z dystrybucji RT do bazy
  3. Napisanie prostego skryptu w ruby, który dla każdej tabeli w bazie wyciągnął wszystkie rekordy i na ich podstawie stworzył inserty.
  4. Załadowanie insertów do bazy

skryt wyglada w następujący sposób

      1 require 'sqlite3'
      2 class String
      3    def escape
      4       x = self.gsub(/\\'/,"' ").gsub(/\\/, "\\\\\\").gsub(/[']/,"' '")
      5    end
      6 end
      7 tabele = ['ACL', 'Attachments', 'Attributes',
      8     'CachedGroupMembers', 'CustomFieldValues',
      9     'CustomFields', 'GroupMembers', 'Groups',
     10     'Links', 'ObjectCustomFieldValues', 'ObjectCustomFields',
     11     'Principals', 'Queues', 'ScripActions', 'ScripConditions',
     12     'Scrips', 'Templates', 'Tickets', 'Transactions', 'Users']
     13
     14 db = SQLite3::Database.new("/usr/local/rt3/var/rt3")
     15    puts "begin;"
     16    tabele.each { |t|
     17    rows = db.execute("select * from #{t}")
     18    rows.each { |row|
     19       d =[]
     20       row.each { |col|
     21          if col.nil?
     22             d << '\'\''
     23          else
     24             d << '\'' + col.escape + '\''
     25          end
     26       }
     27       puts sprintf("insert into #{t} values (%s);\n", d.join(','))
     28    }
     29 }
     30 puts "commit;"

Po tym zabiegu, RT dostało ogromnego przyśpieszenia. Ja natomiast chyba już nigdy więcej nie wybiore SQLite jako bazy do jakiegokolwiek produkcyjnego systemu…

Spotkanie użytkownikow RubyOnRails i prezentacja

niedziela, lipiec 16th, 2006 by Paweł Rutkowski

Zgodnie z tym co pisałem wcześniej, owe spotkanie się odbyło. Chyba wszyscy byli zaskoczeniu dużą liczbą osób przybyłych na spotkanie. Ja niestety mogłem tam gościć ok 1h. Po krótkim wstępie Tomka Meki miałem przyjemność wygłosić prezentacje pod tytułem “RubyOnRails i problemy z hostingiem aplikacji“. Jako wykład “inauguracyjny” stała się zalążkiem zaciekłych dyskusji - co prawda na tematy “około railsowe”, ale mam nadzieje że poźniej się uczestnicy ukierunkowali.

Kolejne spotkanie jest zaplanowane na 18 sierpnia 2006 - prawdopodobnie w tym samym miejscu.

Pierwsze spotkanie polskiej grupy użytkowników Ruby On Rails

czwartek, czerwiec 29th, 2006 by Paweł Rutkowski

14 lipca 2006 roku w sali konferencyjnej firmy Sec-Bos (i dzięki jej uprzejmości zarazem) przy ulicy Łowickiej 39A w Warszawie o godzinie 1800 odbędzie się pierwsze spotkanie polskich użytkowników Ruby On Rails. Zapraszamy wszystkich zainteresowanych - zaawansowany, początkujących oraz osoby chcące dowiedzieć się co nieco o tym frameworku.

W programie przewidujemy:

  • Paweł Rutkowski - “RubyOnRails i problemy z hostingiem aplikacji”
  • Tomasz Meka - “Prototype i script.aculo.us”
  • Dyskusja na temat projektu stworzenia funkcjonalnego forum w RubyOnRails

RubyOnRails i problemy z dziedziczeniem.

środa, czerwiec 21st, 2006 by Paweł Rutkowski

Bawiąc się ostatnio z RubyOnRails podczas pisania pewnego projektu trafiłem na bardzo dziwną przypadłość.

RubyOnRails jest wyposażony w mechanizm o nazwie ActiveRecord który jest odpowiedzialny za mapowanie obiektów użytkownika do bazy danych, ich wyszukiwanie, kasowanie, tworzenie i modyfikowanie.

Wśród wielu właściwości ActiveRecord umożliwia dziedziczenie obiektów z bazy danych. Przykładowo mamy obiekt Klient na bazie którego są zbudowane dwa inne obiekty: KlientKorporacyjny oraz KlientIndywidualny. Ponieważ większość baz danych nie jest w stanie obsłużyć czegoś takiego wprost, ActiveRecord musi wykonać troche “magii” żeby to zadziałało. Używa do specjalnej kolumny (domyślnie “type”) w której przechowuje nazwe klasy do której należy obiekt.

Dodatkową funkcjonalnością udostępnioną przez powyższy mechanizm, jest automatyczne dopisywanie:


where type='KlientKorporacyjny'

do SQL generowanego przez funkcję find. Oczywiście w momencie utworzenia nowego rekordu, jego typ także zostanie odpowiednio ustawiony.

Skuszony takimi oto urokami skorzystałem z takiego mechanizmu. Stworzyłem sobie następujące klasy:


class Category < ActiveRecord::Base
validates_uniqueness_of :name, :scope => :type
end
class ContentCategory < Category end
class WorkshopCategory < Category end

No i niestety przy próbie dodania czy zmodyfikowania rekordu pojawił się Exception. Po chwili grzebania doszedłem do tego że winny jest scope => :type. Nie wiem czym to jest spowodowane, ale type nie może być brany pod uwagę jako scope. Rozwiązaniem (mało eleganckim) okazało się dodanie nowej kolumny do Category o nazwie scope_helper i ustawianiu jej nazwy przez before_save.

Generalnie pomogło, natomiast pojawił się nowy problem. Chciałem dodać możliwość zmiany rodzaju kategorii z Workshop na Content i odwrotnie. Niestety przy tej konfiguracji poległem. Nie udało mi się w żaden sposób zmusić ActiveRecord do wykonania tej operacji. Po kilku godzinach walki dałem sobie spokój. Wyrzuciłem WorkshopCategory i ContentCategory, zastąpiłem je samym Category do którego dodałem kolumnę mytype które ustawiam ręcznie.

RubyOnRails ma wiele zalet i wygodnych rozwiązań - niestety czasem, kiedy chce się je wykorzystać trafia się na ograniczenia które mogą skutecznie zabrać pare godzin z harmonogramu.

Ciekaw jestem czy ktoś z Was także “wpadł” w jakąś pułapkę railsową ?

Grupa dyskusyjna pl.comp.lang.ruby - CFV

niedziela, czerwiec 4th, 2006 by Paweł Rutkowski

Rozpoczeło się głosowanie za utworzeniem grupy dyskusyjnej pl.comp.lang.ruby . Oprócz samego Ruby, grupa dopuszcza tematykę RubyOnRails. Osoby które chcą zagłosować za ( nie biore pod uwagę że ktoś zagłosuje przeciw:) znajdą dokładną instrukcję tutuaj

Mam nadzieję że grupa powstanie - uważam że czytanie postów w ulubionym czytniku jest o wiele wygodniejsze niż na jakimkolwiek forum.