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
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!
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.
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ść?
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.
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.
Update 1207781421: Jest i diagram sekwencji w dwóch wydaniach. Pierwszy obrazuje proste sprawdzenie prawa do zasobu (Product), drugi przedstawia użycie delegata praw.
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.
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.
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ń:
- problemy rozwiązywane przez Traits da się z łatwością rozwiązać poprzez poprawnie zaprojektowaną strukturę obiektów
- PHP za wszelką cenę chce być bardziej dynamiczny niż Ruby
- dynamic is not the only way to go
- Traits pozwalają kiepskim developerom poczuć się lepiej
- niektórzy developerzy nie odrobili lekcji i gówno wiedzą o interfejsach, kompozycji i delegacji odpowiedzialności do klas usługowych
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.
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 ;-)
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!
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!
add coment | permalink | trackback | rss
30 Jul 2007
71% GeekMingle2Update 1189429928:
Dziwne, w liceum z maty byłem pomiędzy dostateczny a dopuszczający ;-)
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ę?
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.
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.
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 ;-)
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.
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.
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ć.
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ć.
add coment | permalink | trackback | rss
co???
02 Nov 2006

s/Łagodny śnieg/Jebana breja, nawet nie myśl co będzie jutro/g
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.
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.
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ę! ;-)
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ć
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.
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
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! ;-)
4 comments | permalink | trackback | rss
05 Jan 2006
pass5 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
- Przypisanie referencyjne do referencji powoduje natychmiastową propagację aż do ostatniej zmiennej w całym łańcuchu referencji.
- 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.
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ę.
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.
Przeszedłem w podprzestrzeń?







