Czytam sobie sporo o Railsach ostatnio. Na tyle mi się framework spodobał (i język Ruby też), że chwilowo nawet myślałem o całkowitej migracji Java -> Ruby & Rails. Ponieważ jednak firm RoR'owych u nas we Wrocławiu jak na lekarstwo (w sumie tylko jedna, pracowników serdecznie pozdrawiam :) ), to ostatecznie pomysł ten zarzuciłem. Nie zmienia to faktu, że czytanie książek i blogów o Railsach sporo mi dało. Inaczej teraz trochę patrze na programowanie, jest też kilka spraw, które w frameworkach Javowych bardzo mi od tego czasu brakuje.
Jednym z takich elementów są migracje (ang. migrations), potężne narządzie frameworka Ruby on Rails.
W Javie pisząc aplikacje wykorzystujące bazę danych przyzwyczailiśmy się już do frameworków tzw. mapujących. To znaczy takich, w których model biznesowy jest mocno zintegrowany z bazą danych. Boli nas trochę fakt, że gdy tworzymy aplikacje z wykorzystaniem jakiegoś narzędzia ORM'owego, musimy wyspecyfikować:
- pole w modelu
- getter
- setter
- mapowanie w kierunku a->b
- mapowanie w kierunku b->a
- definicje kolumny
Oczywiście nie można dramatyzować: model (a wiec odpowiednie fieldy, gettery, settery) pomoże nam stworzyć nasze ulubione IDE, a najnowsze frameworki (z wykorzystaniem adnotacji) umiejscawiają nam definicje bazodanowe w samym modelu. Wszystko robi się niby DRY (Don't Repeat Yourself), wszystko wydaje się być w jednym miejscu i wszelkie zmiany w projekcie można wprowadzać bezboleśnie. Zapominamy tutaj o jednej ważnej sprawie - danych w bazie danych.
Możemy wyobrazić sobie, że po miesiącu czy dwóch naszego developingu, powstaje aplikacja/prototyp, która zostaje wdrożona u klienta. Schemat bazy danych jest oczywiście generowany z naszego modelu na bazie produkcyjnej u klienta. Czysta, nowa, świeżutka baza wraz z aplikacją zostaje uruchomiona. My zabieramy się za kolejną iterację, a klient wdraża się w jej pierwszą wersję. W między czasie dodajemy nowe kolumny, zmieniamy nazwy tabel, łączymy pewne rzeczy w jedną całość - jesteśmy bardzo agile - nie boimy sie zmian. Przychodzimy do klienta, chcemy mu wygenerować nowa bazę a on nam mówi "Hola hola, ja tu już mam pewne dane, nie chce ich tracić". Po chwili namysłu dochodzimy do wniosku, że możemy wygenerować plik SQL, w którym umieścimy wszystkie zmiany w nowej bazie danych względem tej starej (znany wszystkim DDL, czyli alter table i takie tam) i tak gotowy skrypt uruchomimy po stronie bazy produkcyjnej.
Ok, rozwiązanie niby dobre. Spotkałem się z nim w praktyce, pliki takie nazywane były "patchami" (moim zdaniem nazwa dobra) i tworzone były dla każdej kolejnej iteracji przygotowywanej aplikacji. Trzymane wraz z kodem w repozytorium nie miały prawa zaginąć. Teraz tworząc pole String nickname w modelu User musiałem dodatkowo w patchu patch_078.sql umieszczać wpis "alter table add column nickname varchar". W momencie integracji wersji deweloperskiej z produkcyjną, po wprowadzeniu zmian w kodzie uruchamiany zostawał patch i schemat bazy był aktualny z modelem. Wszystko niby fajnie, ale właśnie tu zaczynają się schody. Po pierwsze przestaje mieć pojedyńczej informacji (w tym przypadku informacji o schemacie bazodanowym) w jednym miejscu - co szybko prowadzić może do pomyłek. Druga sprawa to brak powiązania tych zmian z kodem na repozytorium. Wprowadzę zmiany w kodzie, commitne, mnie trochę czasu, update'uje patch na repozytorium. Mijają cztery dni i okazuje się, że muszę cofnąć zmiany w moim modelu. Muszę PAMIĘTAĆ, że odpowiednie zmiany powinny być update'owane w patchu (rollback na repozytorium mi tego nie zapewni). Ból, ból, ból i zgrzytanie zębów.
O wiele ciekawiej rozwiązane jest to w Railsach. Patche (tutaj zwane migracjami) tworzone są za nas automatycznie. Taki patch zawiera nie tylko metodę updatującą schemat bazy, ale również wycofującą te zmiany. W ogólności każda migracja to klasa Ruby'ego implementująca dwie metody up oraz down. Metoda up wprowadza zmiany w schemacie bazy danych, metoda down je wycofuje. Przez co uzyskujemy patch działający w dwie strony. Zestaw operacji jakie możemy wykonać jest dowolny, możemy wstawiać dane, zmieniać strukturę schematu (z użyciem predefiniowanych metod podobnych do tych które znamy z DDL, czyli create/drop table, add/remove column i tak dalej). Każda taka migracja ma przypisany swój numer porządkowy, a sam schemat trzyma informacje o swojej wersji. Jeśli zatem przykładowo mamy schemat bazy danych w wersji 004, a w czasie developingu pojawiły się dwie nowe migracje 005 oraz 006, to wywolanie komendy rake migrate spowoduje, że framework wywoła w kolejności metody up, najpierw z migracji 005, a następnie z migracji 006,a na końcu zmieni wersję schematu na 006. Jeśli kiedykolwiek trzeba będzie sie cofnąć w zmianach schematu, na przykład do wersji 004, wykonane zostaną kolejno metody down w migracji 006, a poźniej w 005. Piękna sprawa!
Klasy migracji są tworzone dla modeli automatycznie, więc nie ma obawy, że do repozytorium nie zostaną przekazane razem. Wtedy cofnięcie się w ramach repozytorium do kodu z przed na przykład kilku dni nie stanowi problemu.
O migracjach w Rails można by mówić wiele, fakt faktem brakuje mi czegoś takiego w frameworkach Javowych. Słyszałem, że jakieś OMR'y generują patche dla konkretnej bazy danych w oparciu o zmiany jakie zaszły w modelu. Jakie? Nie wiem, może wy mi powiecie, jestem bardzo ciekaw. Wtedy byłoby już fajnie, chociaż wciąż nie pozostawalibyśmy DRY, ale przynajmniej były to już jakiś krok w dobrym kierunku.
Orginał tego postu wyglądał ciutkę inaczej od tego co tu przeczytałeś, ale durny siersciuch (kot) gonił muchę po mieszkaniu, przebiegł się po klawiaturze, skasował sporą część zaznaczonego tekstu, a blogspot automatycznie po kilku sekundach zapisał zmiany. 40% tekstu jest odtworzone, a więc podatne na błędy i gorsze jakościowo - nie cierpie pisać czegoś dwa razy.
Z kota zrobie jutro gyros.
update: jeśli ktoś zna jakiś framework, który rzeczywiście na podstawie schematu bazy i modelu, jest w stanie wygenerować skrypt sql ze zmianami jakie należy wprowadzić w bazie, niech da mi prosze znać na maila lub w komentarzach. W JPA szukałem i się nie doszukałem.
poniedziałek, 23 czerwca 2008
Migracje bazodanowe: czego w Javie nie znajdziemy?
niedziela, 1 czerwca 2008
Rails 2.1
Jeszcze niedawno pisałem o problemie jaki stawia przed młodym adeptem Railsów wybór wersji (1.2.6 vs 2.0.2), a już zdążyła wyjść kolejna wersja RoR opatrzona numerkiem 2.1. Z taką prędkością z jaką rozwija się się framework (półroku kolejne wydanie) trudno będzie nadążyć chyba jakiemukolwiek autorowi książki. Nie mamy jeszcze książki na świecie o Rails 2, (bo nawet The Rails Way opisuje wersję edge) a tu już ogłoszono Railsy w wersji 2.1.
Oczywiście wszystko to ma swoją logikę, w Portland odbywa się teraz konferencja RailsConf i jak to bywa na konferencjach, trzeba coś ogłosić - taka już natura chyba każdej większej konferencji :). Ale fakty pozostają mimo wszystko te same: framework się rozwija, co cieszy niezmiernie.
Jeśli ktoś jest żądny wiedzy co nowego w najnowszych railsach, może przeczytać wpis na oficjalnym blogu frameworka. Ja narazie w szczegóły wdawać się nie będę, bo po prostu nie czuję się kompetentny. Gorąco zachęcam natomiast do obejrzenia RailsCast'ów poczynając od #106 do #109 włącznie, gdzie Ryan Bates opisuje najnowsze featury w Rails 2.1. Warto obejrzeć wszystkie, nawet jeśli jesteś takim newbs'em jak ja ;).
sobota, 31 maja 2008
Nauka Rails - 1.2.6 vs 2.0.2
Railsy mają coś w sobie! Kropka. Nikogo przekonywać nie będę. Sam widziałem po prostu kilka prezentacji, poczytałem kilka blogów, widziałem Railsy oraz Rubiego w akcji na videotutorialu, wygenerowałem sobie kilka prostych aplikacji i jestem zdeterminowany aby język Ruby oraz same Railsy poznać dokładnie.
Myślisz jak ja? Chcesz nauczyć się Railsów? Widzisz, mamy problem. Kilka miesięcy temu wyszła nowa wersja frameworka opatrzona numerkiem 2. Rails 2.0.x wprowadza trochę zmian, kilka nowości, trochę usprawnień i nie byłoby w tym nic strasznego, gdyby nie fakt, że parę rzeczy zostaje przeniesionych, inne się dewaluują, jeszcze inne całkowicie znikają. Framework nie do końca wierzy w ideę kompatybilności wstecznej. Problem jest, bo bodaj najlepsza dostępna obecnie na rynku książka "Agile. Programowanie w Rails" traktuje o Railsach w wersji 1.2.6. Początkujący programista może się zdziwić, że przykłady z pierwszych 170 stron książki w ogóle nie działają (dynamiczny scaffolding został usunięty z Railsów 2).
Pytanie pozostaje co zrobić? Na forum http://www.rubyonrails.pl temat był poruszony i większość osób zgodnie stwierdziła, że warto uczyć się Railsów z książek w wersji 1.2.6 i potem doczytać o zmianach, które zaszły w wersji drugiej. Myślę, że podejście to nie jest złe. Aby zainstalować Railsy w starszej wersji należy dodać jedną opcję więcej podczas instalacji:
gem install rails --version 1.2.6
To było proste. Jest jednak jeszcze jeden mały szczegół, o którym można zapomnieć. Tworząc nową aplikację railsową należy uwzględnić wersję. Tworząc przykładową aplikację Składnica (składnica = depot :) ) z książki "Agile. Programowanie w Rails", należy wywołać komendę rails z dodatkową opcją:
rails _1.2.6_ depot
Ja osobiście postanowiłem się uczyć Railsów z książki "Agile. Programowanie w Rails", z zainstalowanymi Railsami w wersji 2.0.2. Z większość zmian, które zaszły i o których mi wiadomo, jestem sobie w stanie poradzić:
* sqlite3 domyślną bazą danych zamiast mysql - wystarczy zainstalować sqlite3 i gotowe, nic strasznego
* brak scaffolding'u - dynamiczny scaffolding nie został tyle usunięty co przeniesiono go jako osobny plugin. Wystarczy uruchomić
script/plugin install scaffolding
aby przykład Składnica z książki APWR działał.
Zmian jest jeszcze zapewne trochę, ale myślę ze z pomocą google, poradzę sobie w trakcie napotykania problemów. Po przerobieniu APWR z pewnością przeczytam "The Ruby Way" autorstwa Obie Fernandez'a. Jedna z niewielu książek wchodząca daleko w szczegóły działania frameworka, a co ważniejsze opisującą Railsy w wersji 2.0.2.
poniedziałek, 5 maja 2008
Ruby on Rails na Glassfish'u
W ostatnim poście wspomniałem o możliwości uruchamiania aplikacji napisanych w Ruby on Rails na javowych serwerach aplikacji (przy pomocy JRuby) oraz idei jaka przyświeca takiej migracji. Jeśli spojrzymy sobie na preview nadchodzącego Glassfis'a V3 dowiemy się, że jedną z nowości (poza takimi oczywistościami jak wsparcie dla JEE6 :) ), będzie możliwość uruchamiania aplikacji napisanych w dynamicznych językach takich jak Groovy czy Ruby.
Ale Glassfish V3 to przecież jeszcze daleka przyszłość i pozostaje pytanie czy obecnie można wrażać już aplikacje railsowe na obecne wydanie serwera. Odpowiedź brzmi tak, na szczęście jest już możliwość uruchamiania aplikacji RoR i wsparcie dla JRuby w Glassfish'u V2. Wprawdzie jest to feature, który nie przychodzi ze standardową instalacją, ale dołączenie go do działającej instancji jest tak na prawdę kwestią kilku kliknięć.
W katalogu glassfisha uruchamiamy narzędzie updatetool, służące do aktualizowania zainstalowanych wtyczek w serwerze aplikacji
./updatecenter/bin/updatetool
W uruchomionym programie zaznaczamy zakładkę 'JRuby on Glassfish', warto zwrócić uwagę czy jest w wersji 3.0.Klikamy Install i gotowe! Teraz w katalogu glassfisha pojawił nam się nowy folder o nazwie jruby, którego zawartość widzimy poniżej
Mamy tutaj zarówno najnowszego JRuby (jruby-1.1.1) jak i katalog z przykładami (samples). Właśnie jednym z zamieszczonych przykładów posłużymy się demonstrując działanie RoR na Glassfish'u. Wchodzimy do katalogu HelloWorldRailsApp, który jest zwykłą aplikacją railsową typu hello-world - czyli jeden kontroler "say", z akcją hello i odpowiadającym mu widokiem. Następnie tworzymy z tej aplikacji war'a przy pomocy narzędzia dostarczonego wraz z JRuby w wersji 1.1.1 o nazwie "warable":
cd jruby/samples/HelloWorldRailsAppTak stworzonego war'a możemy w sposób standardowy zdeployować (wdrożyć) na naszym serwerze
jruby -S warble


Jest jeszcze tylko jedno małe ale. Ponieważ warable pakuje całego rubiego i gemy do pojedyńczego war'a (wraz z naszą aplikacją), jest to rozwiązanie dobre tylko na szybkie protypowanie czy zapoznanie się z możliwościami JRuby na Glassfishu. Na dłuższą metę warto jednak przerzucić całą zawartość jruby do katalogu glassfish/lib a deployment robić poprzez uruchamianie komendy create-shared, która w katalogu aplikacji railsowej tworzy WEB-INF/web.xml. Tak przygotowaną aplikacje można już wdrażać na serwer.
JRuby on Glassfish 3.0 dostarcza nam środowisko JRuby w wersji 1.1.1 oraz Ruby on Rails w wersji 2.0.2. Wszystko szybko, łatwo i przyjemnie, dlatego polecam spróbować.
niedziela, 4 maja 2008
Po co nam JRuby?
Kończenie pracy magisterskiej ma jedną poważną wadę - monotonię. Przez ostatnie dwa miesiące nie robiłem nic innego jak próbowałem zakończyć prace nad programem, co wciąż czynie mimo, że termin zbliża się nieubłaganie ku końcowi. Ból jest taki, że nie robi się nic innego, tylko tą pracę magisterską i taka stagnacja i marazm mogą zabić.
Dlatego postanowiłem troszkę rozerwać mój umysł czymś innym i w wolnych chwilach zacząłem przyglądać się językowi Ruby oraz framework'owi Ruby on Rails. I to właśnie prostota i przejrzystość RoR ciekawiła mnie bardziej niż sam język Ruby. W niedługim czasie jednak posmakowałem jednak samego języka i zadowoleniem musze stwierdzić, że coś w Rubym jest jednak takiego nienazwanego, że odczuwa się pewną lekkość i przyjemność w klepaniu kodu.
Jakiś czas temu pojawiła się na horyzoncie implementacja Ruby'iego napisana całkowicie w Javie. JRuby, bo o nim właśnie mowa, to produkt open source, ale powstał pod skrzydłami Sun Microsystems. Ktoś może się zastanawiać czy na prawdę potrzebujemy warstwy pośredniej w postaci maszyny wirtualnej, aby pisać aplikacje w Ruby'm? Oczywiście odpowiedź brzmi nie, nie potrzebujemy. Wykonanie programu typu przeiteruj się po kolekcji, wykonaj na elementach jakieś obliczenia, zwróć wynik, wykona się zdecydowanie szybciej w standardowym ruby niż w jruby, gdyż do tego typu zadań został stworzony właśnie interpreter ruby'ego.Problem zaczyna się, gdy chcemy ruszyć coś większego, używając choćby Ruby on Rails jako frameworka do pisania aplikacji webowych. Jeśli czytaliście kiedyś rozmowy o Ruby on Rails, powtarzającym się bez końca tematem jest skalowalność aplikacji napisanych w frameworku. Problem wydaje się rzeka, ale z drugiej strony trudno się dziwić. Jeśli ktoś chce napisać taki powiedzmy nowej generacji serwis społecznościowy, kwestia wydajności przy dużym obciążeniu serwisu wydaje się problemem istotnym. Wtedy właśnie z pomocą może nam przyjść JRuby. Ponieważ implementacja napisana jest w 100% w Javie, możemy nasze aplikacje railsowe odpalać na serwerach aplikacji, które dotąd przeznaczone były dla rozwiązań JEE5. A te z kolei znane są właśnie ze skalowalności, o która tak bardzo drży świat enterprise. Ogólne założenie jest takie jak na rysunku poniżej
Rozwiązanie logiczne i co więcej sprawdza się na tyle, że przewiduje się, że w niedalekiej przyszłości w świecie RoR, JRuby będzie wiódł prym, powoli wygryzając rozwiązania serwerowe pure-ruby takie Mongrel. Czy będzie tak w rzeczywistości trudno powiedzieć, myślę, że sprawa jest jeszcze świeża. Jeśli jednak dołożymy do całości takie smakołyki jak możliwość zamykania logiki biznesowej w javowe obiekty EJB, które wywoływać można (zdalnie i lokalnie) z poziomu kodu ruby'ego, tworzy nam się całkiem ciekawe rozwiązanie.
O JRuby'm można poczytać na blogu Wiktora Gworka, który chyba na poważnie zajął się ewangelizacją języka. Jeśli starczy mu zapału stworzy nam niedługo cykl, będący wstępem do JRubiego, jak narazie, można poczytać pierwszy odcinek.
Jeśli tematyka kogoś interesuje, a angielski mu nie straszny, warto zajrzeć również tu, tu i tu.
No i to tyle na dziś. Wracam do pracy magisterskiej, zostało tylko 25 dni...
update: jak w prosty sposób zainstalować wsparcie dla JRuby na Glassfishu opisałem tutaj
poniedziałek, 28 kwietnia 2008
O tym jak Rubin walczy z Gozzilą...
Kiedy rzucę w ether hasło "narzędzie webowe będące zarazem doskonałym bugtrackerem, issue-trackerem oraz systemem zarządzania projektem", jaka będzie pierwsza myśl, która przyjdzie Wam do głowy? Trudno mi tu zgadywać, ale czuje (i korzystam tu troszkę z mojego i moich kolegów doświadczenia), że wszyscy (a przynajmniej większość) zgodnym chórem krzykną JIRA! Ale wiecie co, ja Jiry nie lubię!
Większość z Was krzyknie, bluźnierca! I nie ma się co specjalnie tu dziwić, gdyż Jira jest narzędziem doskonale sprawdzającym się jako issue oraz bug tracker. Sam przez kilka miesięcy klepania kodu w pracy miałem z nią do czynienia, a używałem jej również przy tworzeniu pracy magisterskiej. Od dłuższego czasu uważałem, że nie ma sobie równych.
Jeśli nigdy nie używaliście Jiry, polecam obejrzeć krótki filmik przedstawiający niektóre z feature'ów dostarczonych razem z narzędziem. Video jest mocno marketingowe (w końcu ściągnięte jest bezpośrednio ze strony głównej producenta) nie mniej jednak w tym kilku minutowym filmiku, można mniej więcej zobaczyć, co Jira może nam zaproponować.I uwierzcie mi posiadanie takiego narzędzia się opłaca. Zapewne większość z Was nie muszę do tego zbytnio przekonywać, ale jeśli ktoś nie próbował, nie bawił się, nie chciał, to zachęcam. Planowanie, raportowanie, śpiewanie, szacowanie, badanie, zgłaszanie, polepszanie.. wszystko w jednym miejscu. Narzędzie miód, ideał.
Ale zaraz zaraz, czy ja na początku nie wspomniałem przypadkiem, że Jiry nie lubię? No tak, z tym, że musze być tu w tym temacie troszkę, dokładniejszy. Ja Jirę lubie, nawet bardzo, poprstu nie pałam do niej miłością.
No ale czemu (ktoś może rzec), skoro Jira jest taka cudowna, piękna, pyszna i śliczna? Powód jest prosty, nie będę owijał w bawełnę i po prostu od razu wkleję screenshota z broszurki reklamującej Jirę:
Tak tak moi mili, za Jirę trzeba płacić (i to sporo) i właśnie dlatego jej nie lubię. No bo sorry, ale zdążyłem się już przyzwyczaić, że system mam za darmo, środowisko developerskie mam darmowe, języki programowania w których piszę też są for free. To samo tyczy się serwera, na którym wdrażam moje aplikacje, konta mailowego (o pojemności kilku gigabajtów), bloga i wielu inny tego typu cudów. I nagle zong! Za issue trackera mam płacić? I to jeszcze 1200 dolarów minimum? Wolne żarty...
Jakiś czas temu zrobiłem bardzo krótki research, sprawdzający czy jest coś na rynku, co by mogło z Jirą konkurować. Najczęściej natrafiałem na artykuły mówiące czemu Jira jest lepsza od Bugzilly, proste programy typu project managment oraz narzędzia dedykowane danej metodologii wytwarzania oprogramowania (taki choćby XPlanner). Konkurenta Jiry nie znalazłem. Teraz już wiem, że nie szukałem zbyt dokładnie, ale o tym za chwilkę...
Od kilku tygodni zgłębiam tajniki języka Ruby oraz frameworka Ruby on Rails. Przyznać muszę, że to właśnie RoR był moim obiektem zainteresowań z początku, do samego Ruby miałem mieszane uczucia. Teraz moje podejście się trochę zmieniło i sam język nawet do mnie przemawia. Nauka z tego taka, że do niczego nie można podchodzić z uprzedzeniami.
Wracając jednak do głównego wątku, szperając trochę po polskich forach railsowych, natknąłem się na ofertę pracy, zupełnie inną od tych standardowych do których zdążyliśmy się już przezwyczaić (młody zespół, wysokie zarobki, musisz znać Jave - czytaj bierzemy wszystko co dotkneło chociaż raz w życiu klawiatury). Jednym z elementów oferty pracy było wylistowanie projektów opensourcowych, w których pracownicy firmy biorą udział. Wyklikałem wszystkie linki (z czystej ciekawości) i w ten oto sposób poznałem Redmine.Redmine to (cytując ze strony głównej) "flexible project management web application". Czyli innymi słowy - Jira2! Poważnie, nie oszukuję. Ma funkcjonalność Jiry ale i nie tylko. Główna strona projektu Redmine jest uruchomioną instancją redmina, więc de facto wchodząc na stronę www.redmine.org od początku widzimy ją w działaniu. Mamy znany nam z Jiry issue i bug tracker, miejsce na wiki, forum dla developerów. Osobne działy typu Roadmap (budowane na podstawie issuetrackera), Repository (pełny wgląd w repozytorium), dział newsów, autoamtycznie generowany dział Activity (można szybko zobaczyć jak wyglądają postępy prac nad danym projektem) i wiele wiele innych. Polecam po prostu wejść na stronę http://www.redmine.org i zobaczyć aplikację w działaniu.
Dołóżmy do tego bardzo ładny, przejrzysty layout, możliwość rozwoju o pluginy, wspracie dla LDAP'a, Gantt chart oraz kalendarz (menadżerowie to kochają :) ) oraz kilka innych feature'ów i na naszych oczach powstaje już naprawdę niczego sobie narzędzie, które spokojnie może zacząć konkurować z Jirą.
Redmine napisany jest w Ruby on Rails, ruszy z każdą bazą danych (tzn z większością tych znanych) na każdym systemie operacyjnym (tam gdzie uruchomimy railsy). Jest opensourcowy, wydany na licencji GNU General Public License (GPL). Kilku z programistów , tworzących redmine'a to nasi rodacy (oczywiście z Krakowa, bo to kolebka RoR'a w Polsce).
Gorąco zachęcam do korzystania z Redmina. Wiem, że firmy się raczej nie przerzucą, bo za licencję już zapłacili, wydali gro kasy na szkolenia, podpięcie Jiry pod inne systemy (na przykład Hudsona), więc im się to po prostu nie opłaca. Ale jeśli pracujesz w takiej firmie i chciałbyś mieć coś ala Jira, lecz za darmo, wtedy bez dwóch zdań polecam Redmina!
I jeszcze na koniec kilka zdań, co by sprostować:
* Można uzyskać darmową licencję Jirowską dla projektów typu opensource. Ale muszą być to naprawdę projekty tego typu. Napisanie jestem studentem, chciałbym licencje dla własnego użytku (non-profit) nie zadziała - próbowałem :)
* Nazwa Jira wywodzi się tam jakoś bezpośrednio od Gozzila (gdzieś kiedyś na necie przeczytałem)
* Nie wiem od czego wywodzi się nazwa Redmine, może ktoś z Was wie?
* Railsy są fajne :)