Monthly Archives: February 2010

Cytat dnia

Ostatnio poczytuję sobie dokumentację PostgreSQL. Rzecz to olbrzymi ebook mający ponad 2000 stron – dzieło dość ciężkie. Ale dla śmiechu – jeden z cytatów mnie rozbroił:

There is no century number 0, you go from -1 century to 1 century. If you disagree with this, please write your complaint to: Pope, Cathedral Saint-Peter of Roma, Vatican.

SVN w Debianie bo ile można nosić kod ze sobą

Posiadanie własnego serwera daje szereg możliwości. Jedną z nich jest jedno miejsce ze wszystkimi usługami, do którego możemy dostać się zewsząd. W przypadku programisty to chyba jego kod jest najbardziej pożądanym dobrem – zatem jego dostępność jest sprawą nie do przecenienia.

By nie komplikować za bardzo postanowiłem postawić repozytorium SVN – bardzo popularnego systemu kontroli wersji, dobrze obsługiwanego zarówno pod Windowsem jak i linuxem. W związku z tym, iż pracuję na tych dwóch systemach możliwość łatwej obsługi jest dla mnie szczególnie istotna. Zatem w konsoli klepiemy:

sudo apt-get install subversion

I po chwili możemy cieszyć się naszym systemem kontroli wersji. Dostęp do niego zapewnimy sobie przez natywny protokół SVN. Teraz musimy umieścić gdzieś nowe zupełnie repozytorium, a także nadać prawa do korzystania z niego. W sumie jestem jedynym użytkownikiem serwera, zatem mógłbym zainstalować repozytorium w swoim katalogu domowym. Jednakże nie jest to dobre rozwiązanie – bezpieczniej byłoby stworzyć oddzielnego użytkownika tylko dla dostępu do repozytorium. Bezpieczniej i w sumie wygodniej. Lecimy zatem:

adduser svn

Automatycznie jest tworzony użytkownik oraz grupa o takiej samej nazwie. Repozytorium zainstalujemy w katalogu domowym usera svn, jednakże to za chwilkę. Należy zastanowić się nad dość istotną rzeczą. Otóż póki co zamierzamy wysłać do repozytorium kod źródłowy, który znajduje się na razie na moim dysku twardym. Czyli nie jest to plik WAR, ani nawet rozpakowane na serwerze pliki – muskmy wrzucić całość projektu Netbeans, który mamy u nas na serwerze. Zasadniczo byłoby najwygodniej po prostu przekopiować zawartość katalogu z projektem na serwer, ale trochę to takie… nieeleganckie 🙂 Zresztą jak się okazuje, nasze IDE z taką sytuacją upora się dość szybko i łatwo.

W katalogu usera svn tworzymy katalog, w którym będziemy trzymać nasz kod.


mkdir repos
cd repos
mkdir programbash
svnadmin create /home/svn/repos/programbash

Rzecz jasna najlepiej tego typu zabiegi dokonywać kiedy jesteśmy zalogowani jako użytkownik user. W przeciwnym razie czeka nas rekursywne nadanie uprawnień do katalogu repos/programbash właśnie temu użytkownikowi. Kiedy mamy katalog, który docelowo ma trzymać nasze (tutaj konkretne dla projektu) repozytorium należałoby je skonfigurować. Przechodzimy do naszego utworzonego repozytorium i zaglądamy do katalogu conf. Należy poddać edycji plik conf/svnserver.conf:

anon-access = none # tylko zalogowani userzy moga laczyc sie z repo
auth-access = write # userzy moga szalec
password-db = passwd # czyli dostep dla par user-haslo

Zgodnie z ostatnim zapisem w tym pliku poddajemy teraz edycji plik passwd w tym samym katalogu:

[svn] = [nasze_tajne_haslo]

I tyle konfiguracji. Należy omówić teraz budowę repozytorium – głównym jego katalogiem jest trunk, gdzie leży główna część naszego kodu. Do tego elementami repozytorium SVN są katalogi branches oraz tags. Przynajmniej taką strukturę zaleca nam dokumentacja SVNa i akurat tutaj zgódźmy się na takie rozwiązanie. Jednakże katalogi te nie są tworzone automatycznie przez SVNa zatem musimy dodać je sami. By to osiągnąć należy przejść do katalogu /tmp (wybrałem go tylko dlatego, że na pewno jest tam dostęp i potem będziemy mogli jego zawartość wyczyścić) i wydać takie polecenie:

svn co file:///home/svn/repos/programbash

I powinniśmy otrzymać następujący komunikat:

Checked out revision 0.

Konkretnie w tej chwili stworzyliśmy obraz repozytorium w wybranej przez nas lokalizacji. Dzięki temu na spokojnie dodamy wskazane katalogi:

mkdir trunk
mkdir tags
mkdir branches
svn add trunk
svn add tags
svn add branches

W ten sposób utworzone katalogi zostały wybrane do włączenia ich do systemu kontroli wersji. Teraz wypadałoby je do niego wysłać:

svn commit -m "Pierwszy commit - dodanie katalogow repozytorium"

I możemy sobie obejrzeć efekt:

Adding branches
Adding tags
Adding trunkCommitted revision 1.

Nasz pierwszy commit zakończył się powodzeniem (katalog w /tmp możemy usunąć). W naszym repozytorium istnieje już odpowiednia struktura katalogów i czeka na wypełnienie treścią. By to osiągnąć musimy udostępnić nasze repozytorium na zewnątrz. Istnieje kilka możliwości – udostępnianie poprzez protokół HTTP (do tego potrzebne jest dokonfigurowanie serwera WWW), posługiwanie się protokołem SSH (również konfiguracja), albo też natywnym protokołem dla SVN i to rozwiązanie zastosujemy. Za komunikację po ww. protokole odpowiada demon (program może być uruchomiony jako demon) o nazwie svnserve. Jest on automatycznie instalowany wraz z SVN, zatem nie trzeba instalować nic dodatkowego. Demon ten nasłuchuje domyślnie na porcie 3690. Uruchamiamy go w ten sposób:

svnserve -d -r /home/svn/repos

Wskazujemy, że działamy jako demon (-d), a także wskazujemy na roboczy katalog. Dzięki wskazaniu na repos nasz demon obsłuży wszystkie repozytoria jakie kiedykolwiek przyjdzie nam do głowy dodać. Rzecz jasna przydałoby się dodać uruchamianie tego demona przy starcie systemu poprzez edycję skryptów startowych. Na razie jednakże skupmy się na zaimportowaniu plików do repozytorium. Uruchamiamy Netbeans (zakładam, że pluginy do obsługi SVN zostały zainstalowane) i klikamy prawym przyciskiem myszy na projekcie, który zamierzamy wysłać do repozytorium, po czym wybieramy Versioning -> Import into Subversion repository tak jak poniżej:

W pojawiąjacym się okienku wpisujemy:

svn://chlebik.pl/programbash/trunk

Wpisujemy też rzecz jasna użytkownika i hasło po czym klikamy na Next i następuje (powinno 🙂 połączenie z serwerem. Przy naszej strukturze repozytorium (każdy projekt będzie posiadał oddzielny katalog) katalog trunk jest głównym, zatem to właśnie jego wskazujemy jako cel importu. Musimy również wpisać komunikat związany z tym importem i klikamy Next.

Po chwili powinniśmy zobaczyć taki ekran:

I ostatecznie nacisnąć na przycisk Finish. Akurat Programbash trochę waży, zatem w zależności od szybkości łącza internetowego wysłanie wszystkich plików na serwer może trochę potrwać. Ostatecznie w zakładce Output otrzymałem taki komunikat:

Transmitting file data ...
Committed revision 2.
update D:/Java/programbash2 -r HEAD --depth=files --force
At revision 2.
==[IDE]== 2010-02-15 01:14:21 Committing... finished.

Czyli zasadniczo wszystko OK. Sprawdźmy czy wszystko jest na pewno w porządku. Użyłem do tego Eclipse, aby nie mieszać w Netbeans. Odpalamy IDE, tworzymy nowy projekt, wybieramy jako typ SVN -> Checkout project , dodajemy nową lokację repozytorium, podajemy stosowne dane i Eclipse uruchamia kreatora projektu. Zakładam rzecz jasna, że nasz Eclipse posiada plugin do obsługi SVNa – polecam Subclipse. U mnie projekt zaimportował się bez najmniejszych przeszkód i tym samym dowiodłem, że repozytorium działa 🙂

Rzecz na koniec dość istotna – do repozytorium dodałem z premedytacją pliki projektu Netbeans. Standardowo nie robi się tego w ten sposób – jakiekolwiek szczegóły deploymentu aplikacji powinny zostać pominięte w repozytorium. Dzięki temu nie śmiecimy w nim plikami potencjalnie różnymi u każdego programisty. Jednakże zasady tutaj panujące powinny być uzgodnione przez użytkowników repozytorium. W tym przypadku ja będę jedynym, zatem pozwoliłem sobie na taką małą frywolność 🙂

Bardziej rozbudowany Hibernate, czyli dodajemy wpisy w ProgramBash

Po zabawach z rejestracją, uploadami plików i innymi sesyjnymi gadżetami przyszedł czas na trochę bardziej zorientowane na bazę rzeczy. Konkretnie chodzi oczywiście o użycie Hibernate i jego mechanizmu relacji. Do dzieła.

Podstawową rzeczą w serwisie będzie pojedynczy wpis. Upraszczając zagadnienie chodzi nam o wpis do bazy z pewną treścią, datą dodania, autorem, oceną (poprzez głosowanie innych użytkowników serwisu), a także by było ciekawiej przyporządkowanymi kategoriami. Mówiąc krótko mamy tutaj szereg relacji pomiędzy tabelami, no i samych tabel też trzeba będzie dorobić. Do tej pory (jak choćby w przypadku obiektu użytkownika), odwzorowania danych w bazie poprzez obiekt było proste. Mieliśmy zwykły obiekt z kilkoma własnościami – dopisaliśmy metody dostępowe i tyle. Podobną rzecz robiliśmy już przy okazji pisania HowToJava, jednakże tam relacje obsługiwał DSL z Grails, zatem rzecz była maksymalnie łatwa i uproszczona. W przypadku Hibernate rzecz jest trochę bardziej skomplikowana. Jednakże zanim weźmiemy się za kodowanie trzeba przedstawić kod SQLa, który posłuży nam do pracy. Do tej pory tabele były tworzone przez ORM, co wymagało nie raz ich poprawiania. Teraz damy Hibernate gotowe tabele do działania.

Istnieje już tabela reprezentująca użytkowników w serwisie. Użytkownicy dodają wpisy, które tym samym posiadają tylko 1 autora (wiele do jednego z perspektywy wpisu). Wpisy są również przypisane do jednej z kategorii (by było łatwiej wyszukiwać i segregować – relacja jeden do jednego). Tworzymy tabele:

-- Tabela dla wpisów
CREATE TABLE `entries` (
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`user_id` BIGINT NOT NULL ,
`category_id` BIGINT NOT NULL ,
`add_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
`content` TEXT NOT NULL ,
`points` INT NOT NULL ,
INDEX ( `user_id` )
) ENGINE = MYISAM ;

-- Tabela dla kategorii
CREATE TABLE `entry_categories` (
`id` BIGINT  NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`category` VARCHAR( 64 ) NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = MYISAM ;


INSERT INTO `entry_categories` (`id`, `category`) VALUES
(1, 'Java'),
(2, 'MySQL'),
(3, 'PostgreSQL'),
(4, 'C#'),
(5, 'PHP'),
(6, 'Groovy'),
(7, 'Python'),
(8, 'Ruby'),
(9, 'RoR'),
(10, 'C++'),
(11, 'C'),
(12, 'Bash'),
(13, 'Life');

Teraz trochę teorii, gdyż zrozumienie relacji w Hibernate wcale nie jest takie proste. Kilka rzeczy, które wypadałoby wiedzieć:

  • relacje jednostronne i dwustronne – relację uznajemy za jednostronną, kiedy tylko jedna z encji ma pojęcie o istnieniu drugiej. Prosty przykład – mam akwarium i w nim rybki. Jestem ich właścicielem i powiedzmy, że posiadam zbiór obiektów rybek i wiem dokładnie, która jest która. Natomiast moje rybki ni cholery nie wiedzą skąd się wzięły, co je to zresztą obchodzi. Relacja dwustronna ma miejsce wówczas, gdy obydwie strony relacji wiedzą o sobie. Idealnym przykładem jest rodzic, który posiada kilkanaście dzieci. Doskonale zdaje sobie sprawę z ich istnienia, potrafi je jednoznacznie rozróżnić, ale także każde z dzieci ma informacje o swoich rodzicach (w sumie jest to relacja wiele-do-wielu).
  • właściciel relacji – posiłkując się przykładami powyżej należy w każdej relacji wskazać jej właściciela. Jest to ta encja, która ma prawo do ustawiania klucza głównego w tabeli po stronie “wiele”. Czyli w przykładzie z rodzicem i dziećmi to rodzic za każdym razem ustawia swoje ID w tabeli dzieci. W naszym przypadku to użytkownik będzie właścicielem relacji z wpisem.
  • kaskadowość – bardzo przydatna rzecz, a konkretniej zapis, który pozwala na wskazanie czy przy zapisywaniu właściciela relacji utrwaleniu w bazie danych mają również być poddane wszystkie zależne encje. Jest to o tyle wygodne, iż wystarczy dodać dzieci do rodzica, zapisać rodzica w bazie i viola – informacje o dzieciach również zostały zapisane.

Rozpatrzmy teraz nasz przykład. Zaczniemy od tej łatwiejszej części, czyli relacji pomiędzy wpisem, a kategorią do której jest przypisany. Kategoria jako taka istnieje, ale nie ma pojęcia do czego jest przypisywana (albo co do niej) i zasadniczo taki stan rzeczy wszystkich zadowala. Zatem będziemy mieli do czynienia z relacją jednostronną, której to właścicielem będzie wpis – bo właśnie w tej klasie będzie zapisana informacja o kategorii. Z drugiej strony mamy użytkownika, który może być autorem pewnej ilości wpisów. Zatem wypadałoby, aby użytkownik miał dostęp do swoich wpisów (by wyświetlić je np. na stronie użytkownika). Z drugiej strony kiedy wyświetlimy jeden konkretny wpis to dobrze byłoby też poznać jego autora – skoro umieścił fajnego loga, to pewnie innego jego wpisy są/mogą być równie ciekawe. Czyli wpis musi znać swojego autora. Tym samym od razu wiadomo, że jest to relacja dwustronna. Kto ma być właścicielem? Z zaprezentowanego schematu bazy danych łatwo wyczytać, iż będzie nim użytkownik. Obiekt uzytkownika wciąż wisi u nas w sesji – i kiedy będziemy dodawać wpis, będziemy tenże obiekt aktualizować, a dzięki konfiguracji zachowania kaskadowego automatycznie aktualizacji dokonamy w tabeli z wpisami.

Dość teorii na razie – rzućmy okiem na kod:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="hibernate.mappings">
  <class name="com.wordpress.chlebik.EntryCategory" table="entry_categories">
    <id column="id" name="id" type="long">
      <generator class="native"/>
    </id>
    <property column="category" name="category" type="string"/>
  </class>
</hibernate-mapping>

A oto i klasa Javy:

package com.wordpress.chlebik;

import java.io.Serializable;
import javax.persistence.Id;


/**
 * Klasa, ktora ma za zadanie byc beanem wystepujacym jako kategoria wpisu
 *
 * @author Michal Piotrowski
 */
public class EntryCategory implements Serializable {

    @Id
    private Long id;
    private String category;
    
    public EntryCategory() {  }

    public EntryCategory( String category ) {
        this.category = category;
    }

    @Override
    public String toString() {
        return category;
    }

    // Gettery
    public String getCategory()
    {
        return category;
    }
    
    protected Long getId()
    {
        return id;
    }

     // Settery - bardziej dla poprawnosci niz potrzeby
    public void setCategory( String category )
    {
        this.category = category;
    }

    protected void setId( Long id )
    {
        this.id = id;
    }
}


Rzecz już bardziej prosta być nie może. Mapowanie tej klasy jest póki co podobne do obecnego obiektu usera. Zajmijmy się teraz wpisem.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="hibernate.mappings">
  <class name="com.wordpress.chlebik.Entry" table="entries">
    <id column="id" name="id" type="long">
      <generator class="native"/>
    </id>

    <!-- relacja -->
    <many-to-one name="category" class="com.wordpress.chlebik.EntryCategory" column="category_id" />

    <!--  zwykle kolumny -->
    <property column="content" name="entry" type="string"/>
    <property column="points" name="points" type="integer"/>
    <property column="add_date" name="adddate" type="timestamp"/>
  </class>
</hibernate-mapping>

No i jej kod:

package com.wordpress.chlebik;

import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Id;


/**
 * Klasa, ktora ma za zadanie byc beanem wystepujacym jako wpis
 *
 * @author Michal Piotrowski
 */
public class Entry implements Serializable {

    @Id
    private Long id;
    private EntryCategory category;
    private Timestamp adddate;
    private String entry;
    private Integer points;

    /**
     * Bezargumentowy konstruktor
     */
    public Entry() {  }

    /**
     * Konstruktor z danymi
     *
     * @param id
     * @param adddate
     * @param entry
     * @param points
     * @param category
     */
    public Entry( Long id, Timestamp adddate, String entry, Integer points, EntryCategory category ) {
        this.id          =   id;
        this.category    =   category;
        this.adddate     =   adddate;
        this.entry       =   entry;
        this.points      =   points;
    }

    @Override
    public String toString() {
       return entry + "\n Kategoria: " + category.toString();
    }

    // Gettery
    public EntryCategory getCategory()  {
        return category;
    }
    
    public Long getId()  {
        return id;
    }

    public String getEntry()  {
        return entry;
    }

    public Timestamp getAdddate()  {
        return adddate;
    }

    public Integer getPoints()  {
        return points;
    }

     // Settery 
    public void setCategory( EntryCategory category )  {
        this.category = category;
    }

    public void setId( Long id )  {
        this.id = id;
    }

    public void setPoints( Integer points )  {
        this.points  = points;
    }

    public void setAdddate( Timestamp adddate ) {
        this.adddate = adddate;
    }

    public void setEntry( String entry ) {
        this.entry  =  entry;
    }

}

Nie zapomnijmy również dodać stosownych wpisów w pliku hibernate.cfg.xml:

    <mapping resource="/com/wordpress/chlebik/mapping/EntryCategory.hbm.xml"/>
    <mapping resource="/com/wordpress/chlebik/mapping/Entry.hbm.xml"/>

Encja Entry posiada ID kategorii, dzięki czemu wrzucając mapowanie do pliku konfiguracyjnego Hibernate bez problemu wie, co i skąd wyciągnąć. Jak widać w encjach nadpisałem metodę toString, dzięki czemu dodając do bazy danych przykładowy wpis mogłem po małej magii otrzymać w widoku takie oto cudo:

Od razu postanowiłem podpiąć formatowanie kodu, o którym pisałem niedawno. Zważywszy na edukacyjny charakter aplikacji,a tym samym małe obciążenie postanowiłem linkować do zewnętrznych źródeł. Podpięcie biblioteki, wrzucamy wyciągnięty z bazy kod i oto taki widok:

Ślicznie. Wróćmy jednakże do głównego wątku naszych rozważań. Nasz wpis posiada przypisaną kategorię. Posiada również autora, którym jest jeden z użytkowników w bazie danych. Zajmiemy się teraz stworzeniem odpowiedniego mapowania dla pokazania tej relacji. Jak napisałem powyżej jest to relacja dwustronna – wpis zna swojego autora, zaś użytkownik również jest świadom swoich wpisów.

W pliku Users.hbm.xml dopisujemy taki oto kod:

<set table="entries" name="entries">
         <key column="user_id"/>
         <one-to-many class="com.wordpress.chlebik.Entry" not-found="ignore" />
</set>

Mówi on tyle, że w przypadku obiektu User mamy dostęp do zbioru unikalnych wpisów, które są składowane w tabeli entries. Identyfikującym kluczem, który pozwoli wyciągnąć tylko te wpisy, które przynależą do naszego użytkownika jest kolumna user_id. No i ostatecznie obiekty, które będą w tej kolekcji są obiekty typu Entry. Rzecz dość czytelna. Oczywiście dopisujemy również metody dostępowe do tej kolekcji w klasie User!!!.

Z kolei by obiekt wpisu zdawał sobie sprawę z istnienia swego autora i mógł się do niego odwoływać należy do pliku Entry.hbm.xml dorzucić taką deklarację:

<many-to-one name="author" class="com.wordpress.chlebik.User" column="user_id"/>

W klasie Entry również trzeba dopisać metody dostępowe dla właściwości author. Uruchamiamy naszą aplikację i testowo gdzie nam wygodnie umieszczamy takie coś:

        try
        {
            org.hibernate.SessionFactory sessionFactory = com.wordpress.chlebik.util.ProgramBashUtil.getSessionFactory();
            Session session = sessionFactory.openSession();
            Entry wpis = (Entry) session.get( Entry.class, new Long(1) );
            return wpis;
        }
        catch( Exception e )
        { };

Używam metody get, gdyż nie rzuca ona wyjątku przy wyciąganiu obiektu z bazy tylko zwraca NULL w przypadku braku poszukiwanego rekordu. Akurat jego istnienia jestem pewny (wklepałem go ręcznie), ale dobrze jest wyrabiać sobie dobre nawyki. Zmodyfikowałem trochę metodę toString w klasie Entry i po uruchomieniu aplikacji pokazał się mym oczom taki widoczek:

Ślicznie. Od strony pojedynczego wpisu działa. Teraz testowo zamieniamy kod wyciągający pojedynczy wpis na taki oto kawałek:

 User author = (User) session.get( User.class, new Long(49) );
 return author;

Zaś w pliku Users.hbm.xml zmieniamy definicję na:

 <bag table="entries" name="entries" order-by="add_date">
         <key column="user_id"/>
         <one-to-many class="com.wordpress.chlebik.Entry" not-found="ignore" />
 </bag>

W widoku wrzuciłem taki kod dla spróbowania:

<rich:dataList var="elem" value="#{testowyBean.author.entries}">
    <h:outputText value="#{elem.entry}" />
</rich:dataList>

I pokazuje się nam piękna lista wpisów, które stworzył nasz użytkownik (na razie jego ID wklepane na sztywno, ale przerobienie tego to za chwilkę). Pytanie czemu użyłem znacznika bag, a nie np. Set lub List. Odpowiedź na to pytanie można znaleźć pod tym adresem – nie będę tworzył dodatkowego taga dla operacji, które powinny być oczywiste i powszechnie dostępne w widoku. Co do List dostawałem błąd parsowania XMLa – pomimo wklapania struktury znacznika prosto z dokumentacji Hibernate. Dopiero ww. konstrukcja z bag dała radę i posłusznie współpracuje ze znacznikiem rich:dataList.

Wpis trochę się zrobił rozwlekły zatem część widoku oraz samej logiki w następnym odcinku.

Zakupów ciąg dalszy – dysk zewnętrzny

Po netbooku przyszedł czas na zajęcie się kwestią pojemności dysku. Mój desktop parametry ma całkiem dobre – dokupiony niedawno RAM ma w tym niemały udział. Tylko ten dysk – raptem 80 GB.

Komputer ma już bodajże 2 lata zatem i taka pojemność nie dziwi. Jednakże ostatnio coraz bardziej zaczęło mi przeszkadzać ograniczanie swej aktywności – choćby takie zdjęcia. Przy nowoczesnych aparatach jedno zdjęcie dobrej jakości potrafi ważyć 1MB lub więcej. Czyli w ciągu roku można naklepać nawet i 1GB zdjęć, plus te, które miałem do tej pory – i robi się krótko. O muzyce nie wspominam już w ogóle – chciałbym przerobić płytkę z muzyką (koncertową, więc by nie tracić jakości chciałbym to zarzucić na FLACa) i kolejny zonk – piosenka średnio po 40MB. No to jeśli na płycie jest 12 utworów łatwo przemnożyć. Coś trzeba było zrobić.

Nie chciało mi się rozkręcać komputera i wkładać kolejnego dysku wewnętrznego, gdyż w związku z posiadnaniem netbooka przydałoby się coś bardziej mobilnego. Ostatecznie zatem padło na dysk zewnętrzny – Seagate FreeAgent 1TB. Podłączyłem, zaszumiał, zainstalował swoje oprogramowanie i działa. Śliczne 1TB przestrzeni do wykorzystania. Ile kodu musiałbym napisać by to zapełnić 🙂

PS. Kupiłem też nową myszkę, stara nie nadawała się już do gry w pasjansa :))

Uniwersytet też dla ludzi

Uniwersytet kończyłem, nawet studia dzienne, problem polega na tym, iż nie kończony kierunek nie był informatyką. Odbije się to pewnie jeszcze nie raz przy procesach rekrutacyjnych, ale cóż począć – tak wyszło. Dlatego cieszy dość ciekawa inicjatywa, jaką podjęły nasze uniwerki.

Konkretnie chodzi o zamieszczanie w sieci materiałów dydaktycznych, których autorami są pracownicy naukowi tychże uczelni. Nie mówię zatem o skryptach pisanych przez studentów, notatkach z wykładów i laborek, ale w pełni opracowanych materiałach dydaktycznych. Od kilku lat w sieci można znaleźć tego typu materiały prezentowane przez ludzi z informatyki UW, zaś w ostatnich dniach doszedł też AGH. Co więcej – w przypadku tej ostatniej uczelni mamy nie tylko dane z informatyki, ale również szeregu innych wydziałów. Bardzo miłe inicjatywy i dla takich osób jak ja – bezcenne.

Formatowanie kodu w aplikacjach webowych w najprostszy mozliwy sposób

Przymierzając się wprowadzenia funkcjonalności dodawania wpisów w ProgramBash natknąłem się na problem wprowadzenia formatowania kodu. No i mały problem.

Domyślnie do aplikacji będzie można wklejać dowolny niemalże język programowania. Oczywiście pojawia się problem formatowania kodu źródłowego – zgodzimy się, iż czyste wskaźniki <pre> to nie jest dobre rozwiązanie. Oczywiście istnieje masa skryptów, które zajmują się tego typu konwersją, jednakże najcześciej są one napisane w PHP, zaś przepisywanie tego typu skryptów to benedyktyńska praca i niezbyt do tego rozwojowa. Java pod tym względem (dostępności takich skryptów) jest o wiele bardziej uboga. Jednakże okazuje się, iż istnieje inny sposób – można do tego typu zadań zaprząc JavaScript!. Jest to najzwyklejszy skrypt w tymże języku, dodane pliki stylowania kodu i viola – po wrzuceniu do skryptu łańcucha z kodem powinniśmy mieć pięknie sformatowany kod.

Narzędzie to nazywa się niezbyt oryginalnie SyntaxHighlighter, zaś jego strona domowa to wiki, gdzie znajdziemy wszystkie możliwe informacje. Na razie wygląda to bardzo obiecująco – jak tylko wezmę się za kolorowanie składni to na pewno skorzystam z tego skryptu.