Od jakiegoś czasu używam na desktopie Ubuntu. Począwszy od chyba 6.06 (jak nie wcześniej, nie pamiętam już) aż do teraz. Wczoraj mnie podkusiło żeby spróbować wersji niestabilnej (tzn każdego Ubuntu próbuję, ale zazwyczaj nie 3 m-ce przed wydaniem wersji stabilnej ;)

Metoda upgrade’u: zmiana wpisów w /etc/apt/sources.list, ja robię zawsze tylko zmianę w nazwie wersji, w tym przypadku: feisty zamieniłem na gutsy. Później na konsoli:

% sudo apt-get update
[...]
% sudo apt-get -d dist-upgrade # tutaj nie byłem jeszcze pewien czy na pewno chcę ryzykować ;)
% sudo apt-get dist-upgrade

Do ściągnięcia było 953MB, ponad 1250 pakietów. Ściągnęło się, jak się już zdecydowałem na ten odważny ( ;) ) krok, to i zainstalowałem wszystkie pakiety, i restart. Wstał - bez problemów. Ale próba zalogowania się powodowała wyłożenie się X-ów. Tu problemem był beryl - wystarczyło zalogować się na “zwykłą” sesję Gnome i poszło. Jeszcze kilka razy się coś wysypało, ale już teraz działa bezproblemowo. Stwierdziłem, że za prosto… no to trzeba dorzucić jakiegoś compiza ;) To już było trudniejsze.

Dodałem repozytoria do sources.list, update, próba instalacji wszystkiego co może być potrzebne - poszło. Próba odpalenia - dupa… I tutaj popełniłem mój największy błąd: postanowiłem odinstalować emerald-a, bo stwierdziłem że może coś nie grać z nim - w końcu to ‘berylowy’ emerald ;) Wywalony, próba instalacji ponowna, i nic z tego - problem z biblioteką libwnck, której beryl potrzebuje w wersji libwnck18 >= 2.1.5, a w GG (Gutsy Gibbon) jest wersja libwnck22. Na nic próby nacisku, walki etc - nie da się zainstalować i koniec ;) Apt-get nie pozwala. A ja nie znam na tyle apt-geta (jak kiedyś poldka) żebym wiedział jak go zmusić do czegoś czego ten nie chce. W ogóle słabo znam te debiany - kiedyś za czasów kiedy bawiłem się admnistrowaniem serwerami poświęcałem czas na naukę danego systemu. Od tego czasu nieco już minęło, a ja wolę używać systemu niż się go uczyć przez 2 miesiące :) Rzycie. Po próbach googlania, czytania, szukania etc, postanowiłem pościągać nieco pakietów ze strony packages.ubuntu.com/gutsy, co było strzałem w dziesiątkę. Po usunięciu wszystkiego compizowo-berylowo-emeraldowego z systemu, ściągnięciu wszystkiego co trzeba (co kojarzyło się z emeraldem i compizem) ręcznie z podanej strony, i zainstalowaniu poprzez % sudo dpkg -i *deb compiz ruszył. Yes!

Jeszcze troszkę walki żeby go uruchomić wraz z logowaniem się do systemu (system >> preferencje >> sesje i trzeba dodać dwie pozycje: compiz --replace cpp i emerald --replace), konfiguracja, żeby animacje były szybsze i kilka innych drobiazgów, i od wczoraj od północy cieszę się działającym compizem na Ubuntu GG :)

Wrażenia z użytkowania GG?

pozytywne:
  • wstaje szybciej
  • działa szybciej
  • w końcu mam osobno na klawiaturze usypianie laptopa, jak i blokowanie klawiatury. Wcześniej działało (z klawiatury) tylko blokowanie wciskając kombinację klawiszy odpowiadającą za usypianie…
  • jest vim 7.1 ;]
negatywne:
  • nie działa usypianie :( nie działa budzenie po usypianiu. Wisi w kosmosie, trzeba go wyłączyć (trzymając power kilka sekund) i bootować od nowa. Porażka :(
  • częściej mi opera muli - szczególnie przy flashkach
  • trochę rzeczy gdzie nie ma polskich tekstów (nie przeszkadza mi zbytnio, choć przyzwyczaiłem się do polskiego ;) )
  • nie działał mi truecrypt, ale też to już zwalczyłem (ściągnięcie źródeł, przejście do katalogu Linux, % sudo ./build.sh - trzeba mieć rozpakowane źródła kernela w /usr/src/linux, % sudo cp Kernel/truecrypt.ko /usr/share/truecrypt/kernel/truecrypt-2.6.22.ko i voila!) ;)
  • gajim nie pobierał z keyringa haseł do moich kont - trzeba było wywalić z pliku konfiguracyjnego gdzie i jakie są to hasła, i podac je jeszcze raz - wtedy zaskoczył

Przed chwilą dokonałem jednej korekty w skrypcie: jako że czasem nie mogę się połączyć i pobrać blacklisty, stworzyłem możliwość 5-okrotnej (domyślnie - ustala to zmienna TRIES) próby pobrania blacklisty SBLAM!. Dopiero po pięciu próbach skrypt zakończy działalność. Każda kolejna próba jest opóźniona o domyślnie 5 (zmienna DELAY) sekund, przy czym każda kolejna próba dodaje do wartości samą siebie, czyli po drugim niepowodzeniu DELAY ustawi się na wartość 2*DELAY. Przykład:

  • pierwsza próba - z marszu
  • druga próba - po pięciu sekundach
  • trzecia próba - po dziesięciu sekundach
  • czwarta próba - po dwudziestu sekundach
  • piąta próba - po czterdziestu sekundach

Dodatkowym “fiuczerem” jest wywalenie stamtąd wszystkich polskich znaków - coś mi się wysypywał skrypt przez nie, a nie mam teraz czasu na debugowanie dlaczego.

Skrypt do pobrania z adresu: urzenia.net/wp-content/sblam_blacklist_update.txt.


porneL jakiś czas temu zaczął publikować czarną listę spamerów. Uznałem to za świetny pomysł z jego strony, wart wykorzystania. Chwilę później napisałem skrypcik, który pobiera aktualną blacklistę, i dodaje ją do pliku .htaccess na serwerze, blokującym odwiedzającym z tego adresu IP na dostanie się do naszej witryny. Oczywiście mowa tylko o spamerach :)

Skrypt jest bardzo prosty - pobiera blacklistę, dopisuje ją do .htaccess, robiąc wcześniej kopię zapasową, i kończy pracę. Równie prosta jest jego konfiguracja wewnątrz:

HTACCESS
wskazuje na plik .htaccess który będziemy uaktualniać. W przykładowym skrypcie jest ona generowana, jednak nie widzę przeszkód aby wpisać ją na sztywno, np: HTACCESS = '/home/user/public_html/.htaccess'.
BKPDIR
Katalog w którym zostanie utworzona kopia zapasowa .htaccess. Jeśli puste, przyjmuje że kopia zapasowa będzie tworzona w katalogu macierzystym obecnego .htaccess.
BLACKLIST
url do pliku z blacklistą
DEBUG
Jeśli przyjmie wartość prawdy logicznej (np, True, 1) to na stderr będzie wypisywać komunikaty z pracy skryptu.

Skrypt (jego już druga wersja) jest na licencji GPL v.2. Ja nie biorę odczywiście odpowiedzialności za jego nieprawidłowe działanie :) Ale wszelkie błędy i raporty można zgłaszać do mnie, a ja będę starał się je poprawiać :) Do pobrania stąd: urzenia.net/wp-content/sblam_blacklist_update.txt (warto zmienić rozszerzenie pliku na .py).

Jeszcze słówko o najlepszym użyciu:

Ja osobiście wstawiłem sobie do crona linijkę:

12 6 * * * /home/mysz/bin/sblam_blacklist_update.py

Dzięki czemu o godzinie 6.12 każdego dnia mam uaktualnianą blacklistę spamerów :)


W PHP5 został wprowadzony nowy, zupełnie odmienny, i znacznie pełniejszy model obiektowy (w porównaniu do PHP4). W modelu tym znalazło się miejsce dla kilku magicznych metod, które chciałbym tutaj omówić.

Dwoma z najprzyjemniejszych metod magicznych w PHP5 są __set() i __get(), które pozwalają na przechwytywanie odwołań do nieistniejących (lub niedostępnych z powodu akcesorów) zmiennych obiektu.

Wcześniej, przed wersją piątą PHP, jedynym sposobem na pobranie/ustawienie właściwości w pewien uniwersalny i umożliwiający rozwój sposób było stosowanie znanych m.in. z Javy setterów/getterów, które zwyczajowo mają postać:

class Test1 {
    var $elem1;
    function getElem1();
    function setElem1($value);
}

Używałem tej samej metody, jako że innej nie było. Ale magiczne właściwości __set() i __get() załatwiają dla nas masę pustego klepania kodu. Sposób użycia:

class Test1 {
    private $elem1;
    function __get($var) {
        return $this->$var;
    }
    function __set($var, $value) {
        $this->$var = $value;
    }
}

Oczywiście powyżej można zawrzeć także sprawdzanie czy dana właściwość istnieje w obiekcie, ale to już osobna kwestia. Co nam daje powyższy zapis? To, że dla drugiej właściwości, w metodzie “starej”, trzeba by utworzyć kolejne metody setXXX() i getXXX(), natomiast przy wykorzystaniu metod magicznych pozostaje nam dodać tylko kolejne pole do definicji klasy:

[...]
    private $elem1;
    private $elem2;
[...]

Ktoś może powiedzieć: ale ręczne tworzenie setterów i getterów daje nam możliwość bardziej zaawansowanej walidacji poprawności wprowadzanych danych etc. Owszem, zawsze do bardziej zaawansowanej walidacji potrzebna będzie osobna metoda (no dobrze, można to ominąć kosztem zaciemnienia kodu, ale to nie jest dobre rozwiązanie…), ale to nie powód żeby zrezygnować z magii:

class Test2 {
	private $integer;
	private $string;

	public function __get($var) {
		return $this->$var;
	}
	public function __set($var, $value) {
		if (method_exists($this, '_check__'.$var)) {
			if (call_user_func_array(array($this, '_check__'.$var), $value)) {
				$this->$var = $value;
			} else {
				throw new Exception('zła wartość właściwości '. $var);
			}
		} else {
			$this->$var = $value;
		}
	}
	private function _check__integer($data) {
		return is_int($data);
	}
	private function _check__string($data) {
		return is_string($data);
	}
}

Powyższy kod pozwala na dodawanie dowolnej ilości właściwości klasy, a po dodaniu funkcji sprawdzającej także walidację tejże właściwości. Trzeba tylko pamiętać o właściwej nazwie funkcji sprawdzającej, ale to samo dotyczy tradycyjnych getterów :).

Inną magiczną funkcją w PHP5 jest __call(), która pełni podobną funkcję jak __set() i __get(), tylko że dotyczy metod obiektu: przechwytuje odwołania do nieistniejących metod obiektu (niestety, nie dotyczy to metod o zastrzeżonym dostępie). Przykład zastosowania:

class Test3 {
	public $data;
	public function __call($method, $args) {
		if (preg_match('/^get([A-Z][a-z]+)$/', $method, $matches)) {
			settype($this->data, strtolower($matches[1]));
			return $this->data;
		}
	}
}

Przykład powyżej pozwala na pobranie właściwości o typie takim jaką metodą ją wywołamy. Dla wywołania: $test3->getInteger() otrzymamy wartość o typie integer, dla $test3->getString() - ciąg znaków etc.

Jeśli korzystamy z magicznych __set() i __get() jako funkcji dostępowych do właściwości, trzeba czasem sprawdzać czy dana właściwość jest ustawiona czy też jej nie ma. Nie pomoże tutaj ani property_exists() (oczywiście zakładając tutaj że nasze właściwości obiektu mają ustawiony akcesor private lub protected), ani nawet get_object_vars(). Jedyną możliwością jest dodanie metody __isset(), która będzie zwracała odpowiednio prawdę lub fałsz. Dzięki niej będzie możliwe wykorzystanie wbudowanej instrukcji isset() na właściwości klasy, żeby można było stwierdzić czy dana właściwość istnieje:

class Test4 {
	private $data;
	public function __isset($var) {
		return (property_exists($this, $var));
	}
}

Wystarczy teraz użyć:

isset($test4->data)

żeby można było sprawdzić istnienie właściwości.

Skoro istnieje możliwość sprawdzania istnienia właściwości, powinna być też możliwość jej usunięcia - do tego służy metoda __unset(), której przykład można zobaczyć poniżej:

class Test5 {
	private $_data = array();
	public function __set($var, $value) {
		$this->_data[$var] = $value;
	}
	public function __get($var) {
		return $this->_data[$var];
	}
	public function __isset($var) {
		return array_key_exists($var, $this->_data);
	}
	public function __unset($var) {
		unset($this->_data[$var]);
	}
}

Dzięki zastosowaniu __unset() stało się możliwe usuwanie poszczególnych właściwości obiektu za pomocą wbudowanej funkcji unset():

$o = new Test5();
$o->a = 1;
$o->b = 2;
$o->c = 3;

echo 'właściwość "a": '; var_dump (isset($o->a)); echo '<br />';
echo 'właściwość "b": '; var_dump (isset($o->b)); echo '<br />';
echo 'właściwość "c": '; var_dump (isset($o->c)); echo '<br />';

echo '<br />usuwamy a:<br />';
unset($o->a);

echo 'właściwość "a": '; var_dump (isset($o->a)); echo '<br />';
echo 'właściwość "b": '; var_dump (isset($o->b)); echo '<br />';
echo 'właściwość "c": '; var_dump (isset($o->c)); echo '<br />';

echo '<br />usuwamy c:<br />';
unset($o->c);

echo 'właściwość "a": '; var_dump (isset($o->a)); echo '<br />';
echo 'właściwość "b": '; var_dump (isset($o->b)); echo '<br />';
echo 'właściwość "c": '; var_dump (isset($o->c)); echo '<br />';

Ostatnią z opisywanych przeze mnie magicznych funkcji jest użyteczne szczególnie przy debugowaniu metoda __toString(). Pozwala ona na zdefiniowanie ciągu znaków jaki zostanie wyświetlony lub zwrócony podczas rzutowania naszego obiektu na typ string, czyli np. gdy zechcemy wyświetlić nasz obiekt. Przykład:

class Test6 {
	public function __toString() {
		return 'To moja klasa ' . get_class($this);
	}
}

Gdy teraz chcemy sprawdzić instancję jakiej klasy mamy w zmiennej, wystarczy wyprintować ją za pomocą print() lub echo():

$test6 = new Test6();
echo $test6;

To nie wszystkie magiczne właściwości jakich dostarcza nam PHP5, ale pozostałych nie będę tu omawiał ze względu na rozwlekłość artykułu, a także na nieco bardziej zaawansowaną tematykę. W powyższych przykładach brakuje oczywiście dokładnego sprawdzania błędów i innych udoskonaleń, ale w końcu nie o tym był ten artykuł :)

PS. Kilka przykładów użycia magicznych właściwości znajduje się też w innym moim wpisie: Obiekt jako tablica (wprowadzenie do SPL).


Próbka kodu, z jakim się zmagałem przez dłuższy czas:

[mysz@urzenia ~/p/python/mtalog]% python2.5
Python 2.5.1 (r251:54863, May  2 2007, 16:56:35)
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> db = sqlite3.connect(':memory:')
>>> cur = db.cursor()
>>> cur.execute('BEGIN')
<sqlite3.Cursor object at 0x40201f80>
>>> cur.execute('CREATE TABLE `test` (`id` INTEGER PRIMARY KEY, data TEXT)')
<sqlite3.Cursor object at 0x40201f80>
>>> cur.execute('COMMIT')
<sqlite3.Cursor object at 0x40201f80>
>>> cur.execute('BEGIN')
<sqlite3.Cursor object at 0x40201f80>
>>> cur.execute('INSERT INTO `test` (`data`) VALUES ("asd")')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
sqlite3.OperationalError: SQL logic error or missing database
>>> 

Co bym nie zrobił, wysypywało się albo z powyższym wiele mówiącym błędem, albo też chodziło czysto, a po zakmnięciu połączenia (oczywiście nie mówię tutaj o bazie testowej założonej w ramie, jak w przykładzie) baza była pusta. Nawalczyłem się z tym nieco… a rozwiązanie, jak to w życiu, jest banalne.

pysqlite działa natywnie w takim pseudo-trybie autocommita. Nieco próba automatyzacji. Generalnie polega to mniej więcej na tym, że sam zaczyna transakcję jeśli trafi na polecenie INSERT/UPDATE/DELETE/REPLACE, ale w sumie nie doczytałem kiedy ją kończy. Mało mnie to interesuje, bo ja chcę ręcznie zaczynać i kończyć transakcje, a automatyzacja w/w zawiodła (jak pisałem: po zakończeniu połączenia danych w bazie niet :/ ).

Aby kontrolować tryb w jakim pysqlite rozpoczyna i kończy transakcje, potrzeba zmienić wartość właściwości isolation_level naszego połączenia z bazą. W moim przypadku najbardziej pasująca wartość to None, w związku z czym nawiązanie połączenia z bazą będzie wyglądało w ten sposób:

[...]
        self.db.connect ( '/path/to/db.sqlite3' )
        self.db.isolation_level = None
[...]

Od tego momentu sqlite czyna tańczyć jak mu każemy, czyli możemy ręcznie rozpoczynać transakcję, wybierać jej rodzaj, kończyć transakcję czy też ją ROLLBACKnąć etc. W końcu…

A niniejszy wpis dedykuję wszystkim którzy podobnie jak ja walczyli przez kilka/kilkanaście godzin nad taką głupotą :/


Przez ostatnie cztery dni robocze walczyliśmy z jedną zagwozdką. Spory kawałek kodu, w teorii działa nieźle, w praktyce troszkę gorzej. Objawy:

  • pierwsze odpalenie procedury testowej - pomyślne
  • drugie odpalenie procedury testowej - failed (w tym samym requeście!)
  • ciekawostka: wystarczy gdzieś po drodze w testowanej funkcji odpalić funkcję zrzucającą zawartość struktury (w tym wypadku ref. do hasza), żeby oba testy zaczęły działać poprawnie
  • ciekawostka2: wystarczy dodać tą samą funkcję w innym miejscu, żeby np. pierwsze odpalenie testu wypadało nieprawidłowe, a drugie już działało OK

W sumie, dla nas to przez jakiś czas wyglądało na magię :( kombinowaliśmy na różne sposoby, już zacząłem prosić o włożenie cgi_dump()‘a (naszej funkcji dumpującej zawartość zmiennej) i przekierowanie jej wyjścia do /dev/null ;) W końcu wczoraj doprowadziliśmy do sytuacji, kiedy funkcja zaczęła się zachowywać prawidłowo, ale jeszcze nie znaleźliśmy konkretnej odpowiedzi: CZEMU?

Dziś, po długich bojach, połapaliśmy się w (chyba) wszystkim. Pomijając burdel w kodzie, tworzenia zmiennych do niczego nie potrzebnych, lub też deklarowaniu tych samych zmiennych po kilka razy w kodzie, i nazwy zmiennych typu: $data, @data, %data, sprawcą okazywała się konstrukcja while (($key, $value) = each %hash). Ta niewinna z pozoru konstrukcja posiada jednego ‘fiuczera’, o którym nie doczytaliśmy w dokumentacji, mianowicie:

There is a single iterator for each hash, shared by all each, keys, and values function calls in the program; it can be reset by reading all the elements from the hash, or by evaluating keys HASH or values HASH .

Czyli, przekładając na nieco bardziej ludzki opis: jeśli iterowałeś po haszu za pomocą each()‘a, w międzyczasie tą iterację przerwałeś, to wewnętrzny wskaźnik hasza znajdował się w miejscu w którym została przerwana iteracja. Następne wywołanie each()‘a zacznie się nie od początku hasza, tylko od miejsca w którym zostało przerwane. Oczywiście w niektórych przyapadkach może to być dobre, w innych - w tym naszym - niespecjalnie. Położenie tego wewnętrznego wskaźnika można zresetować np. poprzez użycie funkcji keys() lub values() na haszu, ale czyste (samodzielne) wywołanie tej funkcji jest jednak mało czytelne (a “samowyjaśniający” się kod jest lepszy od dodatkowych komentarzy). Jest jednak czytelniejszy sposób aby osiągnąć nasz cel, a którego nie omieszkaliśmy wykorzystać: zastąpiliśmy wszystkie wywołania konstrukcji while-each, konstrukcją foreach-keys, która według przeprowadzonych przeze mnie jakiś czas temu tesów, jest nawet nieznacznie szybsza! Co prawda traci się tutaj odrobinę na czytelności kodu, jednak przeskoczenie pewnych problemów, których rozwiązanie zajęło nam 4 dni, jest zdecydowanie tego warte.


Od jakiegoś czasu męczyłem się z Ubuntu, ponieważ na laptopie usypianie działało bez problemu, ale po wybudzeniu się jakieś 4/10 razy niestety nie działała klawiatura ani touchpad. Za to działała np. podłączona poprzez USB myszka. Strasznie irytowało, i niestety powodowało że nie dało się z tego korzystać na dłuższą metę. Dzisiaj, szukając czegoś na necie, niechcący trafiłem do Launchpada Ubuntu, gdzie ktoś oczywiście ten błąd ponownie zgłosił. I nie dość tego, znalazłem też diffa do pakietu ubuntu, który tak naprawdę dodaje dwa pliki, dzięki którym nie ma problemu z działaniem klawiatury/touchpada po wybudzeniu laptopa!

Patch znajduje się pod adresem: librarian.launchpad.net/6787628/i8042-keyboard.patch, natomiast jeśli ma ktoś problem z odczytaniem co on robi, to już tłumaczę:

  1. najpierw trzeba utworzyć plik /etc/acpi/resume.d/40-i8042-input.sh i wpisać tam:
    
    #!/bin/sh
    
    # Rebind the AT keyboard interface.
    if [ -f /sys/bus/platform/drivers/i8042/bind ]; then
      echo -n "i8042" > /sys/bus/platform/drivers/i8042/bind
    fi
  2. następnie tworzymy plik /etc/acpi/suspend.d/20-i8042-input.sh z zawartością:
    
    #!/bin/sh
    
    # Unbind the AT keyboard interface.
    if [ -f /sys/bus/platform/drivers/i8042/unbind ]; then
      echo -n "i8042" > /sys/bus/platform/drivers/i8042/unbind
    fi
  3. trzeba jeszcze nadać prawa wykonalności dla obydwu plików:
    % sudo chmod 755 /etc/acpi/{resume.d/40-i8042-input.sh,suspend.d/20-i8042-input.sh}

Od tego momentu usypianie i wybudzanie laptopa stało się czystą przyjemnością! ;]

PS. Testowane na Feisty Fawn Beta.


Jak byc może kilka osób zdążyło zauważyć, wczoraj wyszła nowa wersja tej strony :) Zmieniła się nazwa (obecnie: myGeeBlog), zmieniła się też skórka, ponieważ na poprzednią nie mogłem już patrzeć ;) Obecna skórka to moja wariacja skórki Emire zaprojektowanej i wykonanej przez Phu Ly.

Mam nadzieję, że troszkę lepiej teraz tu wygląda, ale nie obiecuje super doznań estetycznych :P Jeśli zauważycie gdzieś jakieś niedoróbki, sypiące się miejsca or whatever, to proszę dać znać, postaram się szybko poprawić :)