Hi, this is your host Michał Rudnicki short "stronger", and Ye are visiting my jagged border of life and work. Have a nice read, and please feel free to drop me a line or twelve.
Since this is a tri-lingual blog you may find it useful to filter it out by: english | polish | php-ish

Gdzie jest apostrof na klawiaturze telefonu?

28 May 2008

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

On się znowu onanizuje

13 May 2008

Ja już nie mogę z tym człowiekiem. Pisałem mu już wcześniej, że jego TCO jest niebotycznie wysokie, a on nadal, że lepsze, fajniejsze, że się muska a nie łechta i w ogóle super. Wojtek, weź się kura w garść! To właśnie ASUS produkuje notebooki sprzedawane pod markądla wyznawców Apple!

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

8 comments | permalink | trackback | rss

Prawa dostępu oparte o adnotacje - PHP

24 Apr 2008

Adnotacje - to ciekawe podejście do zarządzania metadanymi z poziomu kodu źródłowego. Adnotacje pozwalają na opisanie klas, metod i własności dodatkowymi informacjami, które mogą być wykorzystane w dalszym przetwarzaniu. Najprostszym możliwym przykładem jest rozszerzenie obiektu wartości zwracanego przez DAO o informacje dotyczące schematu tabeli (m.in. typ pola, dozwolona wartość null, klucze obce, itp.).

class Account extends ValueObject {

	/**
	 * @Column int
	 * @NotNull
	 */
	public $id;

	/**
	 * @Column varchar
	 * @NotNull
	 */
	public $login;

	...
	...

}

Wyposażając obiekt DAO w zdolność odczytywania adnotacji możemy zautomatyzować elementarną walidację poszczególnych pól obiektu wartości, a nawet tworzyć brakujące tabele i pola w locie. Ponieważ ostatnio dużo zajmowałem się systemami praw dostępu w aplikacjach webowych, przyszła mi do głowy możliwość ożenienia adnotacji z usługą kontroli dostępu do metod kontrolerów. Zerknijmy na przykładowy kontroler i adnotacje @Auth przy jego metodach:

class Users extends Controller {

	/** @Auth users-browse */
	public function index() { ... }

	/** @Auth users-add */
	public function addUser() { ... }

	/** @Auth users-delete */
	public function deleteUser() { ...}

}

Przykład przedstawia klasę kontrolera z metodami zaszeregowanymi do różnych obszarów dostępu (users-browse, users-add, users-delete). Taki sposób oznaczania obszarów aplikacji ma szereg zalet. Pierwszą z nich jest oddzielenie kodu określającego prawa dostępu od kodu logiki biznesowej. Drugą jest duża czytelność i łatwość wprowadzania zmian. Kolejna zaleta to możliwość automatyzacji procesu zbierania informacji o obszarach aplikacji - wystarczy prosty grep by uzyskać listę takich obszarów.

Pora teraz przygotować usługę weryfikacji dostępu. Na potrzeby przykładu ograniczę się do absolutnego minimum. Przede wszystkim schemat tabeli, w której zapisane będą prawa wygląda następująco:

CREATE TABLE app_access (
	user_id		INT,
	role_id		INT,
	area		VARCHAR(200)
);

Usługa sprawdzania dostępu ma m.in. metodę isUserAuthorized:

class AppAccessService {

	public static function isUserAuthorized(User $user, $area) {
		if ($user->isAdmin()) {
			return true;
		}
		$userId = $user->getId();
		$roleId = $user->getRoleId();
		$sql = "
			SELECT * FROM app_access
			WHERE (user_id = $userId OR role_id = $roleId)
				AND area = '$area'
		";
		// reszta obsługi bazy danych...
	}

}

Mamy już informację o obszarach aplikacji oraz usługę sprawdzania dostępu. Brakuje tylko automatyki, która będzie dokonywała tego sprawdzenia przed każdym wywołaniem metody kontrolera. Jeśli Twój framework nie wspiera filtrów wejścia/wyjścia to pozostaje Ci implementacja w konstruktorze kontrolera i odgadywanie metody na podstawie URLa. Jeżeli wspiera i pozwala dowiedzieć się jaki kontroler i jaka metoda zostaną wywołane zanim się to stanie, to fantastycznie. Na potrzeby przykładu zakładam, że zmienne $className i $methodName zawierają nazwę klasy i metody kontrolera.

$r = new ReflectionMethod($className, $methodName);
$doc = $r->getDocComment();
foreach (explode("\n", $doc) as $ln) {
	$ln = trim($ln, " /*\n\t");
	$c = explode(' ', $ln);
	if ($c[0] == '@Auth') {
		if (!AppAccessService::isUserAuthorized($user, $c[1])) {
			throw new AppAccessException("Access to {$c[1]} denied");
		}
	}
}

Jak widać implementacja jest króciutka, ale niestety mało czytelna. To wina braku natywnej obsługi introspekcji dla adnotacji (Annotation Reflection API ma się pojawić w PHP 5.3). Taki lub podobny kod powinien znaleść się w jednym z filtrów, natomiast cała aplikacja powinna zostać ujęta w blok try-catch chwytający wyjątki typu AppAccessException. W razie wystąpienia wyjątku należy przekierować użytkownika na stronę z informacją o braku dostępu i innymi groźbami.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

7 comments | permalink | trackback | rss

Nowy zasób ludzki w projekcie

21 Apr 2008

I jak tu dbać o atmosferę nieskrępowanej inwencji, gdy już na samym początku trzeba osobnikowi złamać osobowość?

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

Równomierne rozłożenie obrazków w dokumencie

18 Apr 2008

TinyMCE fajnym edytorem jest. Problem leży jednak w tym, że czasem zbyt fajnym (bo nikt nie odważy się przecież powiedzieć klientowi, że program jest fajny, tylko jego użytkownicy są głupi ;-). Przycisk dodawania obrazków do dokumentów przyprawia o ból zębów każdego, kto ma jakiekolwiek poczucie estetyki (innej niż ta z Cepelii). Userzy wrzucają je całkowicie bezmyślnie, bez formatowania i najczęściej w absurdalnych rozdzielczościach i w ogóle robią bydło.

Rozwiązanie jest proste - zabierz userom przycisk obrazka, a w zamian daj własny widget do zarządzania fotografiami i rozkładaj je w dokumencie automatycznie. Pisanie uploadera, thumbnailera i obsługi listy to banał i był wałkowany milion razy, bardziej zagadkowe jest natomiast równomierne rozłożenie obrazków w dokumencie.

Pierwotnie próbowałem rozwiązać ten problem po stronie serwera - zliczać paragrafy i wklejać tagi IMG. Nie mogąc uzyskać zadawalających wyników, przeniosłem kod do klienta. Oto implementacja w Javascripcie:

spreadImages = function (bodyId, imagesId) {
	var bw = document.getElementById(bodyId);
	var iw = document.getElementById(imagesId);
	var ps = bw.getElementsByTagName('P');
	var imgs = iw.getElementsByTagName('IMG');
	var n = imgs.length; // ważne by skopiować rozmiar tablicy
	var left = true;
	var d = ps.length / imgs.length;
	for (var i = 0; i < n; ++i) {
		var tmp = iw.removeChild(imgs[0]);
		var p = ps[Math.round(d * i)];
		p.insertBefore(tmp, p.firstChild);
		tmp.style.cssFloat = left ? 'left' : 'right';
		tmp.style.styleFloat = left ? 'left' : 'right';
		left = !left;
	}
};

Jak widać zadanie nie okazało się wcale takie trudne. W wywołaniu podajemy dwa parametry. Pierwszy to identyfikator elementu otaczającego dokument, drugi to identyfikator elementu zawierającego obrazki. Na dowód, że kod działa zamieszczam poniżej kilka fotografii z wycieczki na Giants Causeway w Północnej Irlandii, oraz magiczny przycisk.

Po przeładowaniu strony kliknij na obrazek by go usunąć i spróbować ponownie dla innej liczby fotografii.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

4 comments | permalink | trackback | rss

Prawa dostępu, the ultimate edition

02 Apr 2008

Prawa dostępu w aplikacjach WWW - dla jednych przekleństwo, dla innych środek nadmiernego wyrazu, za każdym jednak razem całkiem spory kawałek bardzo ważnego kodu. Co ważniejsze, nie kod jest tu najważniejszy, a poprawny projekt oraz łatwość użycia (bo cóż po systemie dostępu kiedy współprogramiści pozostawiają sobie włączenie go na później - czyli nigdy).

Po jednym z bezcelowych i przeraźliwie nudnych spotkań z managementem zaproponowano mi ("ty to zrobisz") wykonanie systemu praw dostępu ze szczegółowością na poziomie paranoi: prawami obwarowany miał być absolutnie każdy element aplikacji, od poszczególnych wierszy i tabel, poprzez podstrony, aż do użytkowników i ról. To wszystko w logice trójstanowej zezwol-zabroń-dziedzicz dla każdego z przedmiotów oraz dodatkowo dla dowolnej czynności związanej z przedmiotem i oczywiście z relacją wiele-do-wielu na linii użytkownik-rola. Ponieważ management nie bardzo rozumie konsekwencje swoich wysokopoziomowych decyzji ani niuanse implementacyjne (to już w ogóle) poprosiłem o rozwiązanie kilku przypadków, które prędzej czy później pojawią się w codziennym używaniu:
- jak rozwiązać konflikt ról - gdy użytkownik w roli A ma dostęp do zasobu, a rola B dostępu zabrania?
- jak rozwiązać konflikt hierarchii zasobów - gdy dozwolony zasób X jest częścią niedostępnego zasobu Y (sytuacja częsta przy udostępnianiu zasobów pomiędzy użytkownikami)
oraz coś, co ich ostatecznie położyło:
- Jeff, skoro to Ty będziesz tym zarządzał, to narysuj mi proszę jak chciałbyś to widzieć na ekranie (podły ze mnie chujek, nie?).

Menedżment się zamknął i dla odmiany zaczął słuchać. Użytkownicy-role będą odwzorowane relacją jeden-do-wielu, prawa do zasobów będą opisane w logice binarnej zgodnie z zasadą "co nie jest dozwolone, jest zabronione". Konflikty hierarchii zasobów rozwiązywane są w sposób jawny dla programisty (poprzez wyrzucenie wyjątku). Ekrany zarządzania prawami narysowałem sobie sam.
Muszę powiedzieć, że mimo początkowych obaw, że tak okrojone prawa będą niewystarczające, warunki bojowe pokazały co następuje:
- użytkownicy olewają konfigurację praw dostępu i jadą na defaultach,
- ci, którzy nie olewają, mają na tyle czytelne ekrany, że nie przybiegają z prośbą o pomoc w konfiguracji praw.
W ten sposób management "poniósł ogromny sukces" a ja mam święty spokój.

Dość przynudzania, pora na mięso. Semantycznie, wszystkie zasoby da się zredukować do dwóch typów: obiekty z unikalnym identyfikatorem oraz klasy i metody kontrolerów. Na potrzeby tych pierwszych wyrzeźbiłem klasę usługową AuthService, drugie są obsługiwane przez AppAccessService. Każdy obiekt o unikalnym identyfikatorze, który może podlegać prawom dostępu musi implementować interfejs IAuthorizable, składający się z dwóch metod: getDistinctiveId zwracającej identyfikator w przestrzeni nazw takiej, jak nazwa klasy, oraz getAccessModes zwracającej tablicę dostępnych typów dostępu (np. 'read', 'write', 'delete'). W ogromnej większości przypadków unikalnym identyfikatorem jest klucz główny odpowiadającego obiektu trwałego. Schemat tabeli do przechowywania praw dostępu jest następujący:

CREATE TABLE access (
	user_id		INT REFERENCES users (id) ON DELETE CASCADE,
	role_id		INT REFERENCES roles (id) ON DELETE CASCADE,
	class_name	VARCHAR(100),
	distinctive_id	INT NOT NULL,
	mode		VARCHAR(20) NOT NULL
);
CREATE INDEX access_user_id_idx ON access (user_id);
CREATE INDEX access_role_id_idx ON access (role_id);
CREATE INDEX access_subject_idx ON access (class_name, distinctive_id, mode);

Można jako typ pola distinctive_id zastosować również VARCHAR, ale należy się wówczas liczyć z istotną utratą wydajności bazy.
Implementacja usługi sprawdzania i definiowana dostępu jest dość prosta (część odpowiedzialna za obsługę bazy danych została pominięta):

class AuthService {

	public static function isUserAuthorized(User $user, IAuthorizable $subject, $mode) {
		$distinctiveId = $subject->getDistinctiveId();
		$className = get_class($subject);
		$userId = $user->getId();
		$roleId = $user->getRoleId();
		$sql = "
			SELECT count(*) AS c FROM access
			WHERE distinctive_id = $distinctiveId
				AND class_name = '$className'
				AND (user_id = '$userId' OR role_id = '$roleId')
		";
		// dalej mało ważne pierdoły związane z obsługą bazy
	}

	public static function isRoleAuthorized(Role $role, IAuthorizable $subject, $mode) {
		// to samo co w isUserAuthorized, tylko bez sprawdzania user_id = '$userId'
	}

	public static function authorizeUser(User $user, IAuthorizable $subject, $mode) {
		$distinctiveId = $subject->getDistinctiveId();
		$className = get_class($subject);
		$userId = $user->getId();
		$sql = "
			INSERT INTO access (user_id, class_name, distinctive_id, mode)
				VALUES ($userId, '$className', $distinctiveId, '$mode');
		";
	}

	public static function authorizeRole(Role $role, IAuthorizable $subject, $mode) {
		// to samo do w authorizeUser, tylko z role_id zamiast user_id
	}

}

Dodajemy jeszcze odpowiednie metody do odbierania praw dostępu i klasa jest prawie gotowa. Prawie używalna. Mamy już kompletną implementację bardzo szczegółowych praw dostępu, która jest łatwa w używaniu. Tak łatwa jak:

	if (AuthService::isUserAuthorized($user, $resource, 'read')) {
		// zapraszamy
	} else {
		// wypad
	}

To, co łatwo przegapić w tym momencie, to słaba wydajność takiego rozwiązania podczas operacji na dużych zestawach danych. Wyobraź sobie iterowanie przez 1000 obiektów pobranych jednym selektem. W rezultacie otrzymasz 1001 zapytań do bazy - jedno szybkie, zwracające ogromną ilość danych i 1000 kompletnie nieoptymalnych, zwracających pojedynczą liczbę. Jest i na to sposób: wystarczy pobrać tylko te wiersze, które mają dostęp w podanym trybie dla konkretnego użytkownika/roli. Zaimplementowałem to za pomocą delegata praw dostępu używanego przy wybieraniu wierszy z bazy. Obiekt delegata tworzony jest przez klasę AuthService

class AuthService {

	...
	...

	public static function getUserAuthorizationDelegate(User $user, $mode) {
		return new UserAuthorizationDelegate($user, $mode);
	}

	public static function getRoleAuthorizationDelegate(Role $role, $mode) {
		return new RoleAuthorizationDelegate($role, $mode);
	}

}

Natomiast implementacja obiektu delegata wywodzi się z klasy o abstrakcyjnej metodzie getExpression:

class UserAuthorizationDelegate extends AuthorizationDelegate {

	protected $userId;
	protected $roleId;
	protected $mode;

	public function __construct(User $user, $mode) {
		$this->userId = $user->getId();
		$this->roleId = $user->getRoleId();
		$this->mode = $mode;
	}

	public function getExpression($className) {
		$sql = "
			SELECT DISTINCT distinctive_id FROM access
			WHERE (user_id = '{$this->userId}' OR role_id = '{$this->roleId}')
				AND class_name = '$className'
				AND mode = '$mode'
		";
		return $sql; // tym razem zwracamy czysty SQL
	}

}

Implementacja delegata dla ról wygląda podobnie. Teraz możemy zająć się dostosowaniem kodu, który wybiera z bazy wiersze, by współpracował z delegatem. Zwyczajowo najczęściej korzystam z kombinacji wzorców DAO i ValueObject, ale po drobnych modyfikacjach możliwe jest skorzystanie ze wzorców ActiveRecord, TableGateway oraz prawdopodobnie innych dziwactw.

class FooDAO {

	...
	...

	public static function getAll(AuthorizationDelegate $delegate = null) {
		$condition = $delegate
			? 'id IN (' . $delegate->getExpression('Foo') . ')'
			: 'TRUE';
		$sql = "
			SELECT * FROM foos
			WHERE $condition
			ORDER BY id
		";
		//  obsługa bazy
	}

}

Jeśli preferujesz wybranie wszystkich wierszy i oznaczenie ich dostępności w dodatkowej kolumnie, zmodyfikuj zapytanie w ten sposób:

SELECT f.*, $condition AS is_authorized FROM foos AS f
ORDER BY id

Na koniec coś, co otwiera oczy niektórych niedowiarków na piękno i siłę technik obiektowych. Wspomniana powyżej klasa AppAccessService może również zwracać swoich delegatów. Ich konstruktory zapewne będą różne od tych zwracanych przez AuthService ale interfejsy zostaną zachowane, dzięki czemu możemy ich używać wymiennie i w takim samym kontekście (zarówno delegatów jak i klas usług dostępu). Co więcej, możemy bez problemu łączyć delegaty w überdelegaty za pomocą wzorca Composite i nadal używać ich w ten sam sposób.

class UberDelegate extends AuthorizationDelegate {

	protected $delegates = array();

	public function addDelegate(AuthorizationDelegate $delegate) {
		$this->delegates[] = $delegate;
	}

	public function getExpression($className) {
		$out = array();
		foreach ($this->delegates as $delegate) {
			$out[] = $delegate->getExpression($className);
		}
		return empty($out) ? 'TRUE' : implode(' UNION ', $out);
	}

}

W zrozumieniu wszystkiego co napisałem powyżej wydatnie pomoże znajomość UML-a oraz papier i ołówek. Będzie mi miło usłyszeć o wykorzystaniu tego rozwiązania w praktyce, jak również o alternatywnych sposobach implementacji praw dostępu. Acha, jeszcze tylko legal bullshit: kod jest wolną reimplementacją moich pomysłów z pracy z poprawionym nazewnictwem i usuniętymi niepotrzebnymi zależnościami. Używaj do woli w zgodzie z LGPL 2 lub nowszej.

Update 1207701709: Dodałem diagram klas dla lepszej czytelności rozwiązania.

diagram klas systemu praw dostępu

Update 1207781421: Jest i diagram sekwencji w dwóch wydaniach. Pierwszy obrazuje proste sprawdzenie prawa do zasobu (Product), drugi przedstawia użycie delegata praw.

diagram sekwencji sprawdzania praw dostępu

Klient tworzy obiekt zasobu i odpytuje AuthService o prawo dostępu. Usługa pobiera z produktu jego identyfikator (ufając, że ma doczynienia z obiektem implementującym interfejs IAuthorizable). Zapytanie do bazy odpowiada czy user (pominięty w tym diagramie) ma dostęp w żądanym trybie.

diagram sekwencji wybierania dostępnych obiektów

Klient pobiera z AuthService delegata właściwego dla podanego użytkownika (pominięty w diagramie). Delegat jest tworzony i przekazywany do statycznego wywołania getAll klasy zasobów. Klasa ta odpytuje odpowieni obiekt DAO i przekazuje mu delegata. Obiekt dostępu do danych (DAO) pobiera z delegata wyrażenie ograniczające SQL, następnie włącza je do swojego SQLa i odpytuje bazę. Na podstawie zwróconych rekordów tworzy kolekcję zasobów, która wraca do klienta.

Przy okazji: Umbrello nie zna diagramu sekwencji, więc na szybko użyłem Boumla. Jeden gorszy od drugiego, ale do szkiców wystarczające.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

4 comments | permalink | trackback | rss

Traits w PHP

22 Feb 2008

Kilka dni temu dostałem z PHP Ireland powiadomienie o propozycji włączenia do standardu języka konstrukcji Traits. Traits to mechanizm obchodzący ograniczenia dziedziczenia jednobazowego, umożliwiający zaimportowanie metod do klasy oraz jawne oznaczenie z jakich Traitsów klasa korzysta - jak przy interfejsach. Dla znających UML, Traits to jak Implementation Classes, które są abstrakcyjne tak jak interfejsy, ale posiadają implementację jak zwykłe klasy.

Polecam zapoznać się ze wspomnianym RFC oraz przykładami zanim go obsobaczę. Do czytania marsz.

Na pierwszy rzut oka wydaje się, że Traits to dopust boży dla wszystkich, którzy kiedykolwiek potrzebowali wyposażyć szereg klas we wspólną funkcjonalność. Przyjazna konstrukcja językowa przypominająca nieco CSS również może się podobać. Po chwili zastanowienia przychodzi refleksja, że przecież cały czas radzę sobie z podobnymi problemami bez użycia Traitsów!
Wyobraźmy sobie problem prawie rzeczywisty - potrzeba kompleksowego wyszukiwania po zadanej frazie wśród rozmaitych obiektów trwałych: użytkowników, dokumentów, produktów, itp. Z punktu widzenia projektanta obiektowego, klasa implementująca przede wszystkim trwałość nie jest właściwym miejscem dla systemu szukania. Traits przychodzi z rozwiązaniem: zrób implementację wyszukiwania osobno i włącz ją do klasy. Efekt końcowy jest taki, że mamy implementację szukania w klasie realizującej trwałość, czyli niepoprawnie. Jak zatem zrobić to właściwie?
Można wykorzystując interfejs i kompozycję oraz pewną sprytną technikę programistyczną:

class SearchEngine {

    protected $table;
    protected $columns;

    public function __construct($table, $columns) {
        $this->table = $table;
        $this->columns = $columns;
    }

    public search($term) {
	$sql = "SELECT * FROM {$this->table} WHERE FALSE";
        foreach ($this->columns as $column) {
            $sql .= " OR $column ILIKE '%$term%'";
        }
        // database stuff here
    }

}
interface ISearchEngineProvider {

    public static function getSearchEngine();

}
class User implements ISearchEngineProvider {

    // class implementation here

    public static function getSearchEngine() {
        return new SearchEngine('users', array('login', 'name'));
    }

}
class Product implements ISearchEngineProvider {

    // class implementation here

    public static function getSearchEngine() {
        return new SearchEngine('products_view', array('name', 'category_name', 'make'));
    }

}

Widzimy tu czterech uczestników tego teatrzyku (ha, rym!): prosta implementacja wyszukiwania SearchEngine, interfejs dostarczyciela wyszukiwarki ISearchEngineProvider oraz dwie przykładowe klasy użytkownika i produktu.
Klasy User i Product dostarczają informację o implementowanym interfejsie, można więc na nich wywołać statyczną (statyczną dla wygody) metodę getSearchEngine(), która zwróci nam skonfigurowaną wyszukiwarkę. Technika ta nazywa się Dependency Injection i jest niezwykle skuteczna w czynieniu złożonych problemów prostymi - jak w powyższym problemie, gdzie klasy użytkownika i produktu nie mają wspólnego przodka. Element różnicujący zostaje "wstrzyknięty" do kontenera SearchEngine w konstruktorze, ale typ kontenera pozostaje niezmienny.
Możliwa jest dalsza wariacja na temat zwracanego wyniku wyszukiwania. Klasy User i Product mogą zwracać swoje własne wyszukiwarki wyprowadzone z klasy SearchEngine. I tak User::getSearchEngine() może zwracać obiekt klasy UsersSearchEngine, który z kolei zwróci gotowe intancje User zamiast zwyczajnych wierszy z bazy danych, a Product::getSearchEngine() zrobi to analogicznie dla produktów. Typ kontenera nadal pozostaje niezmienny, gdyż zróżnicowane wyszukiwarki są pochodnymi klasy SearchEngine.

Z powyższego rodzi mi się parę luźnych spostrzeżeń:

Dodatkowo gdy czytam o rozwiązywaniu konfliktów w przestrzeni nazw klasy lub Traitsów to wydaje się to dość czytelne, ale gdy wyobrażam sobie kod stworzony w ten sposób przez jakiegoś żółtodzioba to przechodzą mi ciarry.

Wuj Dobra Rada: nie używaj tego gada.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

9 comments | permalink | trackback | rss

masz akwarium? nie? toś pedał

17 Nov 2007

Dawno nie zaglądałem na bloga redaktora sybaryty. Popełnił był on dywagację o Total Cost of Ownership samochodu ilustrując zagadnienie porównaniem do systemów operacyjnych.

Ja się tam za bardzo na samochodach nie znam, wiem tylko, że Impreza STI kolegi z pracy fajnie wgniata w fotel, a Lanos mojego ojca nie. Ale WO nie byłby sobą, gdyby nie zechciał dowalić tym paskudnym linuksiarzom. Dowalił oczywiście od czapy, powiedziałbym że na poziomie błyskotliwego milicjanta ze starego peerelowskiego dowcipu (stąd tytuł).

Drogi felietonisto, chemiku, sybaryto, blogerze Orliński!
Słowa te piszę na notebooku Della napędzanym Ubuntu GNU/Linuksem. Pomijając sam koszt początkowy tego zestawu, moje TCO jest niższe od Twojego o niepoddającą się łatwej wycenie konieczność publicznego onanizowania się na prawo i lewo posiadaniem Maka i MacOS X-a. Jak udaje Ci się pracować na komputerze, który wymaga od użytkownika tak wiele poświęcenia?
Btw, może szepniesz dyrektorowi JWB, żeby do każdego Appla dołączali gratisowego PR-owca?

Łączę pozdrowienia, etc, etc, nieczytelny.

Wow, idę na piwo, pochwalić się komplom, że mam mały...
wskaźnik TCO ;-)

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

Tigermouse DR1.6 - taki Ajax to ja rozumiem! Mordo ty moja.

11 Nov 2007

Tigermouse osiągnął dziś wiek przedszkolny i został opatrzony dopiskiem DR1.6. DR oznacza, że jest to w dalszym ciągu wydanie dla developerów projektu niż użytkowników produktu. Możliwości i jakość natomiast prezentuje ono całkiem przyzwoitą.

Strona projektu | Aplikacja demonstracyjna | Download | Wykop

Pojawił się mechanizm SynCron, czyli Javascriptowy cron wywołujący Ajaksowe akcje w określonych odstępach czasu. Wprowadzony został podsystem tłumaczenia danych pomiędzy modelem a widokiem, w tym m.in. WikiTranslator, który dokonuje w locie tłumaczenia formatu Wikipedii na HTML. Pojawił się też mechanizm dostępu do wierszy tablic baz danych za pomocą operatora indeksu [ ] (interfejs ArrayAccess). Poszły wreszcie precz wszędobylskie tagi span, a kilka widoków zostało przebudowanych. Prawdziwą perełką jest listener akcji nieaktywności klawiatury StopTypingListener, który pozwala wywołać zapytanie Ajaksowe gdy użytkownik przestanie wprowadzać tekst. Pozwala to np. wyświetlić nadesłane przez serwer podpowiedzi. Pojawiły się przykłady użycia WikiTranslatora, SynCrona oraz demonstracja działania Drag and Drop.

Wśród frameworków dla PHP Tigermouse na pewno wyróżnia się innowacyjną obsługą żądań asynchronicznych. Nie jest również kolejnym portem Railsów. Nie jest również biblioteką typu disco polo (dla każdego coś miłego) - do jego używania przydaje się czasem trochę oleju w głowie ;-)

Zapraszam do testów, póki ciepłe!

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

gee, one comment! | permalink | trackback | rss

dobrana para, na 80%

16 Oct 2007

Ibanez G SA-60 i Marshall AVT100 - do mocnej, szorstkiej, staccato muzy. To bardzo agresywne połączenie. Brzmienie SA-60 potrafi być bardzo surowe, może nieco prostackie, ale nie banalne. Gitara jest bardzo dobrze reprezentowana w jasnych barwach. Humbuck przy mostku ma atomową moc i strzela laserami. Przy umiarkowanym do mocnego przesterze wrzeszczy nie pozostawiając wątpliwości do kogo należy solo. Jednocześnie brakuje nieco regulacji w ciemniejszych, pełniejszych brzmieniach. Dwa single w pozycjach przy gryfie i w środku sprawiają, że gitara ładnie opowiada, ale nie ciągnie - nie ryczy, bo i nie od tego są single. Akompaniament bez zniekształceń udaje się na SA-60 znakomicie, ale trudno przejść z niego do czegoś innego niż ostry krzyk (choć sam krzyk jest znakomitej jakości). Sprawą bardzo indywidualną jest wygoda gry. Ten model jest niezwykle kompaktowy - nie ma mowy o przypadkowych dźwiękach, mechanika działa nienagannie (choć uwaga: mostek z wibratorem + główka bez blokad = szybkie rozstrojenie instrumentu). W dodatku ubarwienie sunburst jest po prostu piękne!

AVT100 to piec przede wszystkim niezwykle głośny. Nie byłem w stanie go przetestować w pokoju, bo zaczęła mnie najzwyczajniej boleć głowa. Dźwięk produkowany przez pojedynczą lampę ECC83 jest przyjemny, ale nie bardzo miękki. To raczej piec wyczynowy. Ma cudowny dzwoneczek w wysokich rejestrach i bardzo szeroką regulację konturu (od płaskiego brzmienia kiepskiego mono do feelingu a'la Stratocaster). Problem z dzwoneczkiem jest taki, że sama SA-60 ma go całkiem sporo, więc jeśli już chciałbym coś regulować to raczej ujmując wysokich tonów niż dodając. Poza tym, dźwięk jest w większości przypadków ładnie ciągnięty przez piecyk, co można jeszcze podkręcić cyfrowym efektem pogłosu w różnych trybach pracy. Efektów innych niż pogłos nie używam, bo są po prostu wątpliwej jakości, może za wyłączeniem chorusu. Piec ma jeden kanał czysty i dwa przesterowane. Czysty i pierwszy przester są bardzo ładne. Bardzo, a bardzo ładne. Drugi przester jest dla deathmetallowców i jest po prostu nudny. Pomijając wrażenia z odsłuchu, AVT100 jest bardzo ładną rzeczą - wygląda rasowo i pozwala się jarać własnym klimatem.

Para złożona z G SA-60 i AVT100 jest udana, pod warunkiem, że odpowiadają nam ostre i potężne brzmienia. Ci, którzy poszukują zbrudzonego soundu, z pewnością nie będą zadowoleni. Bluesmanom zamiast tego pieca doradzę, podpierając się opinią kolegi z pracy, wzmaki Mesa Boogie. Jeśli miałbym cokolwiek zmieniać w tym duecie to byłby to piec właśnie. Zdaję sobie sprawę, że z Ibaneza z niższej-średniej półki trudno będzie mi wycisnąć taki feeling jak z Fendera, czy taki ryk jak z Washburna, ale jestem niemal pewien, że dobry piec potrafi skompensować niedobory gitary i podciągnąć jej słabsze strony.

Z zakupu SA-60 jestem bardzo zadowolony. Ta gitara mi leży. Oczywiście wolałbym drugi humbucker przy gryfie zamiast singla, ale mogę zrozumieć, że producent pożądał od tej gitary bycia bardziej wszechstronną. Z zakupu AVT100 jestem tylko zadowolony, bez "bardzo". Trudno jest wysterować to urządzenie tak, by przy przejściu w przester nie popadał w nadgorliwość. Oznacza to również, że dość prosto daje się uzyskać dźwięk a'la Gibson ES10 bez dokonywania 340 trywialnych przeróbek konstrukcyjnych instrumentu ;-)

A na koniec pocieszne odkrycie z kraju lat dziecinnych, czyli Kombi w utworze Bez ograniczeń. Klikamy klikamy, fatalną jakość wybaczamy!

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

30 Jul 2007

71% GeekMingle2

Update 1189429928:

NerdTests.com says I'm a Cool Nerd God.  What are you?  Click here!

Dziwne, w liceum z maty byłem pomiędzy dostateczny a dopuszczający ;-)

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

2 comments | permalink | trackback | rss

Hemelrich, Garsed

06 Jul 2007

TJ Hemelrich - to nie jest solówka jego życia, ale za to Brett Garsed robi mi dzień. Uważam, że jest wirtuozem.

Podobało się?

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

5 comments | permalink | trackback | rss

w dzień taki jak wczoraj, kiedy zajebistometr spada poniżej zera

28 Jun 2007

Jak na ironię był to pierwszy od ponad miesiąca dzień, kiedy nie padało. Kupiłem sobie też roślinkę na biurko w pracy (bo mam nowe biurko) i od razu przesadziłem do większej doniczki. Ponieważ z nikim nie mogłem umówić się na oglądanie mieszkania na wynajem, dopadły mnie wspomnienia i oglądanie się za siebie. Odgrzewać wczorajszych wymiocin nie zamierzam, zanotuję jednak kilka ogólników dla własnej pamięci.

Straciłem chyba cierpliwość do mozolnego wykuwania swojej przyszłości, wytężania się w imię przyszłych, mglistych, a najczęściej w ogóle niewidocznych wygód, powtarzania sobie jeszcze pół roku i poszukiwania jasnych stron każdej z tych sytuacji. Wyjazd na baranią wyspę był próbą przełamania tego frustrującego rytmu, nie było jednak za nim planu innego niż pół roku, bo co później to miało się dopiero okazać. Mógłbym powiedzieć, że przyjechałem tu dla pieniędzy, a gdybym zostawał, to zostawałbym dla godności. Mógłbym, gdyby nie to, że trochę do tej godności brakuje. Trudno ukryć, że wszystko rozbija się w tej chwili o rozłąkę. O euforyczno-histeryczne, comiesięczne spotkania. Tym trudniej odbierając telefon i słysząc z wakacji nici czy przyjadę na krócej zachować ogólny spokój ducha i w ogóle kurwa zen. Bo w gruncie rzeczy nie jestem aż tak cierpliwy jak mi się wydawało i mam większe wymagania co do iluzorycznego przyszłego sukcesu, na który pracuję - chcę go teraz. Przed trzydziestką chyba już pora choćby na jego znamiona.

Jutro przyjeżdża inżynier Greg. Stara polska morda. Strasznie się spijemy i będziemy tego bardzo żałować. Zdaje się też, że autoterapeutyczna funkcja bloga zadziałała jak zwykle bez zarzutu.

Dzisiaj znowu pada.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

comments disabled | permalink | trackback | rss

Tigermouse DR1.5

24 Jun 2007

Tigermouse dojrzewa. Dla przypomnienia (i dla crawlerów) - Tigermouse to oparty w całości na Ajaksie framework dla PHP 5. Służy to budowania aplikacji webowych w sposób, jaki jest znany z np SWT, Swinga, Qt, PyGTK czy GTK#. Od wersji DR1.4 rozrósł się on o system kontroli dostępu oparty na rolach użytkowników, wparcie dla lokalizacji, obsługę skrótów klawiszowych, obsługę RSS, obsługę danych w modelu ActiveRecord ze wsparciem dla kolekcji (1 do wielu, wiele do wiele) oraz narzędzia developerskie: konsola błędów, sprawdzanie unikalności atrybutów id w DOM, inteligentny autoloader klas. To jedynie donioślejsze zmiany, poza tym jest jeszcze tona mniejszych poprawek.

Moment, w którym opis na Sourceforge zacznie wskazywać etap Beta zbliża się nieuchronnie. Do tego czasu mam nadzieję zastąpić obecną stronę projektu jej nową wersją a dokumentację użytkownika uzupełnić i wtłoczyć do bazy danych.

Wszystkich chętnych zapraszam do testów - z opinii uczestników listy dyskusyjnej wynika, że Tigermouse wprowadza nową jakość do (zatęchłego z lekka) świata developerów aplikacji webowych w PHP.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

Ibanez GSA-60 BS

31 May 2007

Przyszła do mnie niedawno. Opakowana w gigantyczne pudło (takie jak na kaloryfery do naszego mieszkania), owinięta folią z puduszkami powietrznymi, jeszcze jednym pudłem w środku, mnóstwem styropianu i nią samą w środeczku.

BS to skrót od brown sunburst, więc gitara wygląda po prostu pięknie. Do spółki z dokładnym wykonaniem i eleganckim wykończeniem pozwala to cieszyć się nią jeszcze przed wyciśnięciem jakiegokolwiek dźwięku. Spiking of dźwięku. Nie mam na razie możliwości odsłuchania jej na porządnym piecu, więc powiem tylko tyle, że w sklepie, na tranzystorze brzmiała wyjątkowo głęboko i dźwięcznie. Może to za sprawą aż dwóch pojedynczych przetworników. Humbucker za to jest bardziej płaski, ale świetnie "ryczy" w dużych przesterach. Jego położenie przy mostku sprawia, że dźwięk jest na prawdę agresywny. Na początku zastanawiałem się, czy taki układ przetworników będzie mi odpowiadał, ale myślę, że sprzyja on szerokiej palecie zastosowań GSA-60 - w jedną chcwilę można przezbroić ją z pozytywki w dragstera :-)

Wykończenie jest jak napisałem nienaganne. Ładnie matowiony metal, gustowna oprawka humbuckera, bardzo wygodne mocowanie gryfu, wibrator typu "nie używać" (ale z tym się akurat liczyłem). Dodatkowo gryf wydaje mi się jeszcze bardziej płaski niż w Ibanezie EX230 (no i nareszcie jednoczęściowy). W komplecie był jeszcze pokrowiec i taśma - jakiści takiejsobie. Acha, i kabel - jakości poniżej wszelkiej jakości.

Wrażenia z jazdy pod prądem opiszę gdy tylko przyjdzie do mnie AVT100X :-) Tym czasem polecam wizytę na stronie produktu oraz review.

PS. Thomann do spółki z DHLem uwinęli się z przesłaniem giratki z Niemiec do Irlandii w 2 dni. Irlandzkiej poczcie dostarczenie jej z centrum Dublina do South Dublin County - 2 tygodnie. I wszystko tu tak kurwa działa ;-)

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

4 comments | permalink | trackback | rss

31 Jan 2007

Na dwudzieste osme urodziny dostalem pryszczy na nosie. W sumie calkiem fajnie czuc sie nastolatkiem w tym wieku :-) Mmmm, i zasmakowal mi gines.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

gee, one comment! | permalink | trackback | rss

balia aha klija

28 Jan 2007

Dublin, dn. 27.01.2k7

Zaczyna sie moj trzeci tydzien w Dublinie. Jak dotad - calkiem niezle. Doskwiera troche nuda - nie mam komputera i dostepu do sieci poza praca. A w pracy, wiadomo, skype nie uzyje. Ludzie z devteamu i sa bardzo mili i przyjazni, z reszta wiekszosc irlandczykow taka jest. Z jednym z nich oraz z jego dziwczyna z ChRL i ich 3-miesiecznym dzieckiem mieszkam w Tallaght. Wsi spokojna, rzeklbym. Mimo, ze wszystko toczy sie raczej gladko, mam poczucie ogromnej tymczasowosci. Odkrywam, ku swojemu wielkiemu zdziwieniu, ze tesknie za byciem potrzebnym. Potrzebnym p, potrzebnym w ogole. Najgorsze trzy godziny dnia to czas wolny po pracy. Wygladam z wielkimi oczekiwaniami dnia wyplaty, wowczas wlasny komputer pozwoli mi zagospodarowac czas we wlasciwy sposob ;-).

Zdaje sie, ze odmiana w zyciu na razie sluzy mi niezle. Zobaczy sie w dalszej perspektywie jaki jest bilans zyskow i strat. Jak na razie, spodziewam sie, ze dodatni. Wysoce dodatni.

Tesknie, p, oj tak.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

2 comments | permalink | trackback | rss

12 Dec 2006

Rzuciłem robotę. Spędziłem dwa lata przy robieniu na prawdę ciekawego softu. Teraz czas zacząć zarabiać.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

3 comments | permalink | trackback | rss

Możdżer, Danielsson, Fresco

27 Nov 2006

Wczoraj, godziną wieczorną udaliśmy się z p. i "siostrą" Johaną z cudem zdobytymi biletami na koncert (legendarnego?) trio Możdżer, Danielsson, Fresco. Koncert ten otworzył był trasę promującą ich nowy album - Between us and the light. Muzycy od pierwszych do ostatnich chwil nie pozostawili cienia wątpliwości, że są absolutną elitą światowego jazzu, mistrzami kompozycji, fenomenalnymi technikami i znakomitymi performerami. Słowem - wymiatają. Było to słychać już na ich poprzedniej płytce - Time, ale teraz są 126% bardziej, 96% więcej i 241% lepiej.

Utwory z nowej płyty powalają i zniewalają. Leszek wreszcie dał pograć i kolegom ;-), Lars pokazał takie rzeczy na kontrabasie, wiolonczeli i efektach, że nie można przejść obojętnie, Zohar na swoich dzwoneczkach, rureczkach, talerzach, łańcuszkach i szczotkach zmniejszał i powiększał salę koncertową. W ich muzyce jest teraz więcej egzotyki, transu, ale i ciszy. Artyści live są niesamowicie ekspresyjni i sugestywni. W trzecim (może czwartym) wałku, jaki zagrali przeżyłem chwile na prawdę głębokiego poruszenia - absolutnie cudowny odlot. Fresco chlapał morską pianą i sypał w oczy piasek plaży. Możdżer spuścił lodowaty deszcz o wielkich, ciężkich kroplach. Danielsson napełnił powietrze niepokojem, niepewnością i natrętną, nieprzyjemną myślą. Jezuu! Co się działo! Oni nie mają serca - tak człowieka sponiewierać!
Solowe partie Leszka Możdżera są tak daleko odjechane w jazz, że można się zastanawiać, czy się pomylił i wypadł ze skali, czy może jest geniuszem improwizacji. Znacznie rzadziej używa on natomiast innego instrumentu - głosu ludzkiego. Nie tekstuje już z publiką, na koncercie oprócz rzeczowych zapowiedzi kawałków powiedział tylko "no" po pierwszym i "taaa" po drugim bisie. Za oba wykwity ęlokwęncji otrzymał olbrzymie brawa ;-)
p. po koncercie stwierdziła, że nie powinni improwizować w Incognitorze - "ten utwór jest tak piękny, że po prostu nie należy". Dokładnie wiem co miała na myśli. Są takie wałki, które godzi się jedynie pokornie wysłuchać, a wszelki komentarz jest zbędny.

* * *

W dalszej części wieczoru udaliśmy się jeszcze do sąsiadującej przez ścianę Hipnozy. Dzięki wadzie wzroku całej naszej trójki (no, mojej akurat urojonej ;-) udało nam się przegapić możliwość zaproszenia do stolika Larsa i Zohara, którzy niespodziewanie zjawili się w klubie (szzzzzz - powiedziała kurtyna milczenia litościwie opadając).

Trasa koncertowa nadal w toku. Polecam gorąco i serdecznie. Ci ludzie to na prawdę artyści z najściślejszej światowej czołówki jazzu. To słychać.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

co???

02 Nov 2006

Weather applet: jebana breja

s/Łagodny śnieg/Jebana breja, nawet nie myśl co będzie jutro/g

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

5 comments | permalink | trackback | rss

Garsed

03 Oct 2006

Garsed, Brett (Victoria, AU)
"We're not gonna sit in silence" na pewno każdy zna (prawda? ;-)
Garsed to moje wielkie odkrycie ostatnich dni. Wyraźnie wybijał się na tle innych sweepers na filmikach z cyklu Guitars Suck na video.google.com. Atomowe brzmienie jego gitary oraz niebanalne popisy solowe sprawiają, że jego muzykę czuję w końcach palców, uszu, w kolanach, wszędzie. To, co Garsed tworzy to muzyka fusion, ale zdecydowanie wykraczająca poza kanon; jego empetrójki mam opisane jako Progressive Rock - coś w tym jest.

1. Od klipu wmiksowanego w jeden z tych filmów warto zacząć - Quid Pro Quo, grany do spółki z TJ Helmerichem. Fusionowe i jazzujące odloty Bretta i TJa powracają w refrenie do konserwatywnej harmonii rocka (czytaj, jak w C-dur to gramy tylko po białych ;-), niejako przywołując ich do porządku wobec stylu dominującego. Niestety panowie w połowie utworu dostają autentycznej szajby i zaczyna się zwariowana gonitwa po strunach.
Jeśli się podobało to przechodzimy do punktu drugiego. Jeśli nie - też przechodzimy.

2. Następny wałek to prawdziwa bomba wodorowa. Undoing otwiera płytę Big Sky. Ponieważ Garsed dość otwarcie prezentuje się w Internecie tego kawałka możemy posłuchać na jego stronie lub majspejsie. Od razu zwraca uwagę połamane metrum utworu (6/4 6/4 5/4 5/4), dalej jest tylko lepiej. Ten numer niesie ze sobą niesamowitą energię, której uwolnienie następuje w okolicach 1:50s (zwróćcie uwagę na sekcję rytmiczną - perkusista przeistacza się z rockowego rympały w jazzowego mistrza) Myślę, że odwołanie do orgazmu będzie tu na miejscu, zwłaszcza, że minutę później jest już spokojnie i coraz ciszej.

3. Kolejnym wartym odsłuchania kawałkiem jest Brothers z tej samej płyty. Próbka - YouTube. Jest to spokojna balladka, jednak nie ma w niej drażniącej pretensjonalności - w środku utworu, gdzie inni widzieliby miejsce na popisową solówkę muzyk wycofuje się ustępując pola basowi. A robi to wszystko, by pod koniec - excusez les mots - pierdolnąć ostro po bandzie. Ot, kontrapunkt. Robi to wszystko jadąc konsekwentnie na bardzo ładnych tercjach i kwartach, przez co utwór zapada w pamięć na długo, długo.
Dalsza część płyta przynosi coraz to kolejne urzeczenia i zachwyty. Garsed zdaje się mieć niewyczerpany zapas pomysłów. Szczególnie ciekawymi kompozycjami są Got The Horn, The Myth, Friend Or Foe.

4. Płytę zamyka utwór tytułowy. Gdy po raz pierwszy go/ją usłyszałem byłem przekonany, że jednak atomowy Brett stracił parę i wyszło mu takie rozmemłane coś. Naturalnie byłem w błędzie. Naturalnie. To, co dzieje się w Big Sky przyprawia o zawrót głowy. Delikatne tremolo gitary przywodzi na myśl rozmarzony świat Echoes Pink Floyd po to, by przejść w solówkę przesyconą brudnym przesterem. Garsed poleciał w niej daleko, wysoko. O ile lubię sobie podgrywać razem z nim to w tym momencie jestem zmuszony przestać i po prostu słuchać. Dalej jest jeszcze dziwniej - utwór przechodzi w rytmikę latynoamerykańską. Płyta się kończy, nastaje cisza i jedyna na co mam ochotę to ochłonąć.

Teraz, muszę się przyznać miało nastąpić słowo na zakończenie, ale ponieważ podczas pisania tej notki słuchałem płyty Big Sky nie jestem w stanie już nic skreślić. Polecam natomiast nabyć ją w drodze dowolnej, bo jest po prostu tego warta.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

ready or not, here I come (DR1)

17 Sep 2006

Tigermouse jest ajaksowym frameworkiem MVC dla języka PHP. Kolejnym. Nie jest on (i nie ma być) najlepszy do tworzenie po-prostu-stronek, dość dobrze jednak nadaje się (w zamierzeniu) do budowy aplikacji WWW. Ma kilka cech wspierających takie właśnie jego zastosowanie.

Lekki i szybki kontroler czołowy, który przyjmuje żądania klienta, odnajduje właściwy kontroler aplikacji i odbiera od niego odpowiedź. Potrafi również przepuszczać komunikację klienta i kontrolera przez zestaw filtrów.

Filtry wejścia i wyjścia wspierają zachowania wspólne dla całej aplikacji. Przykładem takich zachowań może być ochrona przed session hijacking, profilowanie, logowanie żądań czy ochrona dostępu do części aplikacji.

Kontrolery aplikacji są również lekkie. W odróżnieniu od ajaksowych wymysłów z PEARa kontrolery aplikacji nie muszą być nigdzie rejestrowane. Ich lokalizacja w aplikacji odbywa się za pomocą prostych stringów, którymi można się posługiwać tak po stronie klienta jak i serwera.

Dwa kanały komunikacji w kierunku od klienta do serwera umożliwiają przekazywanie parametrów do kontrolera aplikacji poprzez zwykłą listę parametrów funkcji PHP lub przez obiekt kontekstu formularza.

Kanał zwrotny pozwala na przenoszenie widoku do klienta. Gdy widok jest już wyświetlany zostanie on zaktualizowany/podmieniony. Możliwe jest również przesłanie w jednej odpowiedzi wielu widoków.

Oprócz zwracania widoków możliwe jest róznież wywołanie funkcji JavaScriptu po stronie klienta. Tigermouse pośrenio wiąże wywoływanie funkcji na obiekcie odpowiedzi z wywołaniem funkcji w środowisku klienta. Znów - bez uprzedniej rejestracji wywołań.

Warstwa abstrakcji źródła danych hermetyzuje różnorodność pochodzenia danych. Dodatkowo, w fazie oksperymentu DAO i ActiveRecord z możliwością odwzorowywania w jednym obiekcie wielu tabel, a nawet źródeł danych.

Framework pozwala uniknąć podawania ajaksowego callbacku zanim kontroler aplikacji odeśle odpowiedź. Decyzja o tym co ma się wydarzyć w wyniku akcji dokonuje się po stronie serwera, a nie klienta.

I wreszcie, jest spora szansa, że pisząc aplikację w oparciu o Tigermouse nie będziesz musiał napisać ani bajtu JavaScriptu. Większość potrzebnych procedur uruchamianych po stronie klienta jest hermetyzowana przez obiekty lub metody po stronie serwera.

Tigermouse to w chwili obecnej ścisła beta. Testy niektórych przypadków użycia upewniają mnie, że architektura frameworka jest niezła (a w porównaniu z tym, z czym mam do czynienia w pracy wręcz arcyniezła).
Wszystkich zainteresowanych tworzeniem aplikacji WWW serdecznie zapraszam do zapoznania się z frameworkiem. W repo znajduje się trywialny przykład ustawiania listenerów, obługi formularzy i wywoływania handlerów JavaScriptowych. Nie ukrywam, że wszelkie opinie oraz wasze doświadczenia będą dla mnie bardzo cenne. Chętnych zapraszam do współpracy - każda jej forma się przyda, od dyskusji przez kodowanie aż do dokumentowania kodu czy korekty językowej.

Instalacją Tigermouse jest całkiem prosta. Wymaga on biblioteki Smarty w include_path oraz modułu Services_JSON. Przyda się również obsługa PDO, ale nie jest ona niezbędna (ponadto można napisać własny adapter do np. PEAR::DB, ADOdb czy Creole).
Po zaciągnięciu z repozytorium SF (svn co https://svn.sourceforge.net/svnroot/tigermouse lub tar.gz) należy ustawić prawo do zapisu do podkatalogów var/smarty/compile/ oraz var/log/ w katalogu projektu dla użytkownika serwera WWW.
Zachęcam do testów i zapraszam do devcentrali, na SourceForge.

Update 1158617449: W svn-ie leży sensowniejsza niż poprzednio aplikacja demonstracyjna.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

2 comments | permalink | trackback | rss

24 Aug 2006

Całkiem przypadkiem odkryłem kilka dni temu niezwykłe własności akustyczne mojej łazienki. Wyszedłem do niej z gitarą, bo akurat p. rozmawiała przez telefon a ja nie chciałem przerywać gry (tak tak, czasami się zawieszam ;-). Dźwięk, który wypełnił to niewielkie pomieszczenie był tak przestrzenny i pełny, że doświadczyłem przyjemnego uczucia wtórnej inspiracji. Mruk strun w niskich rejestrach zyskał na wybrzmiewaniu i wyrównał się, zupełnie jakbym podłączył się pod kompresor. Wysokie tony zyskały przyjemny dzwoneczek, ale nie z ostrym lecz łagodnym atakiem, a nawet z minimalnym pogłosem (skąd on się tam wziął???). Środek pasma jest delikatnie przyciszony i wyrównany, w sam raz dla akompaniowania akordami.
Jednym słowem, byłem urzeczony. Całe pomieszczenie zdawało się napełniać dźwiękiem, każdy detal dawał się zauważyć, każdy slide, ciągnięcie broniło swej obecności, wyraźnie się podkreślajac i nie pozwalając umknąć uwadze. Cud! Cuuud po prostu!

Naoglądałem się Vaia na Google Video czy innych Tubach, kupiłem sobie nawet kostkę (o bowe zgupjałem, przecież ja nie gram kostką! no i Vai to nie mój klimat). Zastanawiam się czy z okazji zbliżającej się wymiany strun do innego wiosła nie kupić sobie ósemek. Do tej pory grałem na 9-42 lub 9-48, bo dłużej wybrzmiewają, a nie ciągnę ich aż tak bardzo, żeby mi przeszkadzał ich opór. Do tego dochodzi mostek z wibratorem typu "nie używać, to służy tylko do wyglądania" a nie żaden tam Floyd Rose, więc zastanawiam się czy nie będą się zbyt szybko rozstrajać. Osobiście preferuję brzmienia jasne i nieznaczne przestery (ECC83 roksuje hard!), tapping jest mi obcy, slide i pull-off wręcz przeciwnie, chciałbym więc założyć właściwe struny. Jakie? Wszelkie sugestie, wrażenia, doświadczenia, za i przeciwy chętnie przyjmę.

A teraz marsz, wypróbuj własną łazienkę! ;-)

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

gee, one comment! | permalink | trackback | rss

z macicą w służbie ojczyzny

01 Aug 2006

Temat jest już rozdrapany, choć list protestacyjny podpisałem zanim pojawił się artykuł w GW. Jest taka idiotyczna reklama społeczna promująca dzietność i piętnująca konsumpcyjny styl życia w którym dzieci coraz częściej przegrywają w walce z dobrami materialnymi i karierą zawodową. Nieco mądrego i wiele więcej głupiego na temat tego spotu zostało już powiedziane. Zastanawia mnie jednak motywacja twórców i/lub zleceniodawców tej produkcji. Co mianowicie ma ona konkretnie ośmieszać? Co kryje się pod godnym drwiny "konsumpcyjnym stylem życia" i "karierą"?

Ja i p. od roku mamy własną jamę. Jej zakup i remont pochłonął wszystko co dotychczas zgromadziliśmy i będzie pochłaniał sporo przez następne 29 lat. Dopiero teraz kończymy wyposażanie się w podstawowe sprzęty domowe (no chyba nikt mi nie powie, że kuchnia gazowa nie jest podstawowym sprzętem domowym). Pół roku temu dochrapaliśmy się drugiego komputera (używany Dell L400, narzędzie pracy), a pierwszy już ledwie dyszy. Kilka miesięcy temu naprawiłem sobie zęby, co nie pozwoliło mi wyjść z dołka finansowego aż do niedawna, z tym że niedawno wykorzystałem urlop, więc wiadomo - znów czekam do 5-go. Okap kuchenny nad tą kuchenkę liżę przez szybę w sklepie.
Więc jeśli to wszystko, te absolutnie podstawy są tylko gadżeciarstwem, sztucznymi potrzebami, blichtrem marnym, którego nie potrzebuję, to reklama ta wyśmienicie ośmiesza. Ale na pewno nie mnie, nas, naszych znajomych, więc pozwól drogi Instytucie Matki i Dziecka, któremu bliski jest lost niedoszłych dzieci, że najpierw kup^H^H^Hurodzę sobie kuchnię, żeby później móc dziecku podać kaszkę na ciepło a następnie urodzę okap kuchenny, żeby mu beciki nie prześmierdywały smażeliną.
I co, czy jest to aż tak zajebiście śmieszne? Czy tak cholernie godne pośmiewiska jest to, że chciałbym przynajmniej zrobić Zenda a p. obronić doktorat?? To jest ta zła kariera???

Na Śląsku zatrzymano trzecią w ciągu kilku dni pijaną matkę. Tym razem w Łaziskach Górnych kobieta zostawiła bez opieki na ulicy niemowlę w wózku. Jak się okazało, 43-letnia matka, u której stwierdzono 3,25 promila alkoholu w wydychanym powietrzu, była w domu. Zapomniała o swoje miesięcznej córeczce. za GW z dn. 01.08.2006. Becikowe. Zaczyna się. Jak trafnie napisał kiedyś Opi: właściwie cały plan o kant dupy potłuc! Ludzie będą się rozmnażać w tempie, w którym robią to teraz. Normalni ludzie. Margines natomiast, któremu i tak wszystko jedno, zacznie cykl: wytrysk - tysiąc - sierociniec - impreza.

PS. Samochodu naturalnie nie posiadamy (bez żalu). Naturalnie nie planujemy.
PPS. Odkurzacz urodziła nam moja siostra.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

6 comments | permalink | trackback | rss

hot swap

10 Jul 2006

prime.minister@ivrp.pl został w trybie serwisowym wymieniony na prezesawszystkichprezesów@pis.pl. Umożliwiała to specjalna konstrukcja gabinetu umożliwiająca tzw. hot swap. Hot swap umożliwia wymianę wadliwie działającego (no, niewadliwie też, ale w tym przypadku mowa o działaniu wadliwym) elementu na sprawny bez wyłączania urządzenia. I tak jak macierz dysków RAID 5 ratuje aktywa przedsiębiorstwa, tak prezeswszystkichprezesów ratuje polską racyę stanu. Premier podziękował prezesowi za interwencję serwisową, gdyż jak sam przyznał zwalił mu się SMART i nie był w możności przeprowadzić autodiagnozy. Procedura hot swapu stanowiska premiera przebiegła wręcz modelowo, jak gdyby nic się nie stało, czym władza z ludu u steru ojczyzny udowadnia łże-elementowi wrogiemu zaposiąście kwalifikacji odpowiednich do dowodzenia okrętem na wzburzonych wodach łże-europy.

Od siebie tylko dodam, że podziwiam odwagę naszej part^H^H^H^Hwładzy. Ja sam bałbym się wykonać hot swap na moim ABicie BP6, a przecież to tylko głupie, blaszane pudło.
PS. Okładka Angory dość zabawna.

Update 1152526091: Przyszło mi do głowy, że to wcale nie był hot swap, tylko wirtualizacja. Marcinkiewicz chasał sobie radośnie w wirtualnym środowisku stworzonym przez Jarka, a ten widząc co proces wyrabia postanowił go wywłaszczyć.
kill -9

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

add coment | permalink | trackback | rss

23 Jun 2006

Dziś jest dziesięć tysięcy piąty dzień mojego życia. Wywarło to na mnie wielkie wrażenie. Gdyby nie to, że mam już dobry powód do zmian, byłby to znakomity pretekst - republika Vanatau kusi. Onwards, a jakże! ;-)

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

4 comments | permalink | trackback | rss

05 Jan 2006

pass

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

5 comments | permalink | trackback | rss

Referencji zachowanie osobliwe

26 Oct 2005

Być to może ktoś już miał podobną zagwózdkę. Gdy nie, może ustrzeże go to przed stratą mnóstwa czasu.

$ php
<?PHP

class Cos {
    function __toString() {
        return get_class(&$this)."\n";
    }
}

class Cos1 extends Cos {}
class Cos2 extends Cos {}


$a = new Cos1();
$b = new Cos2();

$out1 = &$a;
$out2 = &$out1;

echo $out2->__toString();

$out1 = &$b; // *

echo $out2->__toString();

?>
^D
Cos1
Cos1

It's not a bug, it's a feature. Z powyższego przykładu wynikają rzeczy następujące

  1. Przypisanie referencyjne do referencji powoduje natychmiastową propagację aż do ostatniej zmiennej w całym łańcuchu referencji.
  2. Wykonywanie przypisania referencyjnego na zmiennej, która kryje w sobie referencję powoduje zmianę celu tej referencji a nie obiektu referowanego (wiersz oznaczony gwiazdką)

Zachowanie jest identyczne w Zend Engine 1 oraz 2.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

gee, one comment! | permalink | trackback | rss

W prostych słowach

24 Oct 2005

Mamy nowego prezydenta. Został on wybrany głosami ciemnoty (prawie 3/4 spośród osób z wykształceniem podstawowym i 2/3 z zawodowym), starych dewot (grubo ponad połowa) oraz nierobów ze wsi (też 2/3). Gratulacje Tłusta Kaczko. Jaki Pan taki kram. I odwrotnie.

Rano usłyszałem na dworcu rozmowę kolejorzy, która wydała mi się esencją polskiej marności:
- To który w końcu wygrał? Kaczyński czy ten drugi?
- A co za różnica. Do roboty tak i tak trza iść. Jakby miesiąc wolnego dali to co innego.

Natomiast naklejki na oknach w tramwajach niezmiennie przypominają mi jaki jest kraj, w którym żyję: Nie wychylać się.

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

gee, one comment! | permalink | trackback | rss

03 Oct 2005

Hmm... Zawsze fascynowały mnie takie figury jak wstęga Moebiusa, butelka Kleina, teserakt, hipertorus czy 3-wymiarowa sfera.

ile fugi potrzebujesz?

Przeszedłem w podprzestrzeń?

Digg del.icio.us StumbleUpon Wykop Reddit Folksr

gee, one comment! | permalink | trackback | rss