Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: cojack, dodany: 27.06.2010 11:37, tagi: sql

PostgreSQL Nadeszła w końcu ta chwila, w której miałem wolny czas by usiąść i dokończyć implementację ltree. Udało mi się, uzyskałem to co chciałem, i to na czym by każdemu programiście zależało. Działa tak jak ja chce, a nie tak jak baza chce. Do rzeczy, w ostatnim i zarazem pierwszym wpisie o ltree w postgresie nie mogłem sobie poradzić z sortowaniem, kombinowałem razem z depeszem jak by go tu posortować, w końcu jakieś rozwiązanie padło. Tylko te rozwiązanie depesz wziął z swojej struktury drzewiastej, otóż mówię temu stanowcze NIE! Dlaczego? Ltree zostało napisane po to by nie robić nic rekursywnie, to po prostu miażdży podejście do drzew w każdym calu, żadne nested sety i inne śmiecie z rightem i leftem. Tam to idzie zęby połamać a nie to ugryźć. Poza tym jest to mało optymalne, to już wolę id, parent_id (taki joke).

Sortowanie w ltree

Rozwiązanie było bliżej niż można było się tego spodziewać. Miałem z trylion pomysłów jak to rozkminić. Pomyślałem o liczbie rzeczywistej której każda kolejna część po przecinku będzie przedstawiała nr zagłębienia, tylko to nie jest tak łatwo oprogramować i mogłyby wyjść niezłe kaszany, także odpuściłem sobie to. I olśniło mnie ARRAY! No jacha! To przecież jest tak proste i oczywiste że nie wiem o czym my rozmawiamy. Sam sobie ten problem urodziłem, a powodem tego było iż nikt wcześniej nie pisał o tym, w necie to nawet nie ma słowa o tym module, parę rzeczy znajdziemy w manualu i stronie twórców. Ale nikt nie pokwapił się z implementacją tego z sortowaniem i się nie podzielił.

Implementacja ltree z sortowaniem

Otóż sprawa jest banalnie prosta (teraz już jest):

CREATE TABLE "category" (
   "idCategory"  SERIAL PRIMARY KEY NOT NULL,
   "categoryPath"   LTREE,
   "ordering" INT[]
);

I mógłbym powiedzieć amen. Alę dodam jeszcze:

INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top', '{1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Science', '{1,1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Science.Astronomy', '{1,1,1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Science.Astronomy.Astrophysics', '{1,1,1,1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Science.Astronomy.Cosmology'. '{1,1,1,2}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Science.Astronomy.Planets'. '{1,1,1,3}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies', '{1,2}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Amateurs_Astronomy', '{1,2,1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Swiming', '{1,2,2}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Football', '{1,2,3}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Chess', '{1,2,4}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Basketball', '{1,2,5}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Voleyball', '{1,2,6}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Checkers', '{1,2,7}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Cards', '{1,2,8}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Skis', '{1,2,9}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Post_Cards', '{1,2,10}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Hobbies.Book', '{1,2,11}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections', '{1,3}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures', '{1,3,1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy', '{1,3,1,1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Stars', '{1,3,1,1,1}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Galaxies', '{1,3,1,1,2}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Astronauts', '{1,3,1,1,3}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Planets', '{1,3,1,1,4}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Sun', '{1,3,1,1,5}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Earth', '{1,3,1,1,6}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Asteroids', '{1,3,1,1,7}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.UFO', '{1,3,1,1,8}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Milk_Way', '{1,3,1,1,9}');
INSERT INTO category ( "categoryPath", ordering ) VALUES ('Top.Collections.Pictures.Astronomy.Space_Ships', '{1,3,1,1,10}');

To powyżej to demo do tabeli, o zapomniałbym o indeksach:

CREATE INDEX "categoryPath_gist_idx" ON "category" USING gist( "categoryPath" );
CREATE INDEX "categoryPath_idx" ON "category" USING btree( "categoryPath" );
CREATE INDEX "ordering_gist_idx" ON "category" USING gist( "categoryPath" );
CREATE INDEX "ordering_idx" ON "category" USING btree( "categoryPath" );

I sobie protestujcie sami jak to świetnie działa. W następnym i przy okazji ostatnim wpisie o ltree, przedstawię wszystkie metody do zarządzania drzewem, oraz widoki.

Autor wpisu: sokzzuka, dodany: 24.06.2010 20:07, tagi: php

Do napisania tego wpisu zainspirował mnie post u Zyx-a pod tym samym tytułem. Zyx przedstawił w nim świeżą koncepcję php-owego frameworka opartego o paradygmat MVC wraz z przykładami kodu. Uzasadnieniem dlaczego „wynajdywał koło na nowo” było to, że uważał, że istniejące frameworki tak na prawdę nie realizują paradygmatu MVC a tylko jego mutację zwaną też MVP. Ten artykuł nie jest bezpośrednio polemiką z Zyx-em a raczej bardziej próbą zademonstrowania podejścia alternatywnego zarówno do tego jakie prezentuj on jak i popularne frameworki.

Chciałbym więc przedstawić pewien zarys frameworka opartego o wzorzec MVP (a raczej MTV ;>) i zasady programowania funk(cyjnego/funkcjonalnego). Napisałem już jeden podobny framework do opisywanego na którym postawiona jest jedna z stron w moim portfolio i dobrze się sprawuje. Natomiast to co opisuje nie posiada jeszcze implementacji i jest w zasadzie kolejną iteracją mojej poprzedniej próby.

Dla jasności przypomnę jeszcze jakie są różnice między MVC i MVP. Oba wzorce służą do separacji warstwy prezentacji aplikacji (View) od warstwy logiki biznesowej (Model).

W MVC kontroler na podstawie requestu łączy odpowiednie modele z odpowiednimi widokami. Widok pobiera odpowiednie dane z modelu wg swojego uznania i tworzy odpowiedź.

W MVP kontroler wyciąga dane potrzebne do renderowania strony z modeli i przekazuje je do widoku. Jak widać w MVP widok nie jest zależny od modelu, po prostu renderuje dane, natomiast w MVC ma świadomość czym jest model (jaką klasą) i potrzebuje konkretnego interfejsu aby móc z nim się porozumiewać.

Wzorzec MVP powstał w celu zapewnienia lepszej testowalności i większej niezależności widoku od modelu.

Sposób działania mojego frameworka.Plik index:

$oContainerConfig = new CContainerConfigXml('config/object-config.xml');
$oFactory = new CContainerManager($oContainerConfig);
 
$oFlowConfig = new CFlowConfigXml('config/flow-config.xml');
$oExecutor = new CExecutor();
$oPageResolver = new CPageResolver($oFlowConfig);
 
$oFrontController = new CFrontController($oPageResolver, $oExecutor, $oFactory, $oFlowConfig);
 
echo $oFrontController->execute($_REQUEST, $_ENV, $_SERVER);

W pliku index widzimy kilka obiektów:

  • $oFactory jest kontenerem IoC, który konfiguruje wszystkie obiekty jakie istnieją w aplikacji.
  • $oFlowConfig jest obiektem który zwraca nam flow całej aplikacji.
  • $oExecutor jest obiektem wykonującym flow
  • $oPageResolver jest obiektem który na podstawie adresu zwraca informację jaka strona jest żądana oraz parsuje parametry

Przepływ sterowania wygląda tak:

  • PageResolver na podstawie $_SERVER i danych z FlowConfig wybiera stronę do pokazania
  • FrontController wyciąga z $oFlowConfig informacje o czynnościach, które należy wykonać dla danej strony oraz przekazuje je do $oExecutor aby je zrealizował
  • $oExecutor wykonuję odpowiednie Command-y i zbiera dane do widoku oraz zwraca je do FrontControllera
  • FrontController wyciąga z $oFlowConfig informacje o rodzaju widoku który wyrenderuje odpowiedź i przekazuje mu zmienne, zajmuje się również wszelkimi redirectami, które miałby wcześniej miejsce.
  • FrontController dostaje od widoku wyrenderowaną stronę i zwraca do index.php gdzie jest echowana
  • FrontController łapie również wszystkie wyjątki które mogą wystąpić i przeprowadza cały proces renderowania strony z błędem zgodnie z ww. flow

Teraz pewnie zastanawiacie się czym są wymienione między wierszami Command-y? Są to jakby pojedyncze akcje kontrolera, ale silnie wyabstrachowane. Zwracają array() z zmiennymi do widoku albo informacji o redyrekcji na inny adres. Reagują na konkretne eventy takie jak pojawienie się jakiejś zmiennej z get-a lub posta.

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: batman, dodany: 24.06.2010 17:30, tagi: jquery, javascript

Programiści pracujący nad jQuery UI wypuszczają co pewien czas kolejne milestone’y tej biblioteki. Dzisiaj światło dzienne ujrzał milestone 2, zawierający długo wyczekiwaną funkcjonalność – menu. Widżet ten zamienia standardową listę kotwic (znaczników a) w menu, obsługiwane zarówno myszką, jak i klawiaturą.

Równie ciekawym widżetem jest zaprezentowany w pierwszym milestone tooltip, którego zadaniem jest zastąpienie domyślnego, nieatrakcyjnego wizualnie tooltipa. Widżet ten jest w pełni konfigurowalny i dzięki czemu można dowolnie określić położenie tooltipa jak również jego wygląd.

Opis menu oraz tooltipa znaleźć można w dokumentacji jQuery UI pod adresami http://docs.jquery.com/UI/Menu oraz http://docs.jquery.com/UI/Tooltip.

Autor wpisu: batman, dodany: 24.06.2010 07:00, tagi: zend_framework

Zend Framework dostarcza ogrom gotowych komponentów, które pokrywają zapotrzebowanie większości programistów. Pośród nich, można znaleźć m.in. Zend_Mail. Jest to komponent odpowiedzialny przede wszystkim za wysyłanie wiadomości e-mail. Znajdują się w nim również klasy odpowiedzialne za pobieranie poczty. Klasy te umożliwiają sprawdzenie, czy na wskazanym koncie znajdują się nieprzeczytane wiadomości e-mail bez konieczności korzystania z Zend_Gdata (który swoją drogą nie oferuje bezpośredniej możliwości sprawdzania poczty). W zasadzie wszystkie niezbędne informacje można znaleźć w dwóch miejscach – dokumentacji Gmaia oraz dokumentacji Zend_Mail.

Zanim będzie można sprawdzić, czy na wskazanym koncie znajdują się nowe wiadomości, należy włączyć w Gmailu obsługę IMAP. Można to zrobić w “Ustawieniach”, w zakładce “Przekazywanie i POP/IMAP”. W sekcji “Dostęp IMAP” wystarczy zaznaczyć opcję “Włącz IMAP”. Resztę pracy wykona Zend_Mail.

Kod sprawdzający czy na koncie Gmail znajdują się nowe wiadomości, jest bardzo prosty. Składa się na niego utworzenie nowego obiektu Zend_Mail z odpowiednimi parametrami oraz pętla foreach, w której zliczane są nieprzeczytane wiadomości. Całość wygląda następująco:

$mail = new Zend_Mail_Storage_Imap(
	array(
		'host' => 'imap.gmail.com',
		'port' => 993,
		'user' => 'nazwa.uzytkownika@gmail.com',
		'password' => 'haslo.uzytkownika',
		'ssl' => 'SSL'
	)
);

$i = 0;
foreach($mail as $message) {
	if($message->hasFlag(Zend_Mail_Storage::FLAG_SEEN)) continue;
	$i++;
}
echo $i;

Gwoli wyjaśnienia. Tablica przekazana do konstruktora zawiera parametry wymagane do  połączenia się z Gmailem. Pętla natomiast sprawdza czy pośród wszystkich wiadomości, znajdują się nieoznaczone flagą “przeczytane”. Jeśli tak, to dla każdej takiej wiadomości zwiększany jest licznik, który zawiera liczbę nieprzeczytanych wiadomości,

Autor wpisu: sokzzuka, dodany: 23.06.2010 19:17, tagi: php

Dzisiaj chciałem podzielić się z Wami na temat kilku przemyśleń dot. type hinting i alternatywnego sposobu implementacji tego ficzera. Po co w ogóle type hinting ? Jak dla mnie type hinting spełnia trzy role:

  • dokumentacja
  • walidacja wejścia
  • lukier składniowy

Dzięki roli dokumentacji wiemy jaki typ argumentu należy przekazać do funkcji by działała ona w sposób przewidziany przez jej autora. Dzięki walidacji wejścia autor funkcji upewnia się, że dostaje właściwe parametry, których użyje aby wyprodukować wynik. Natomiast dzięki lukrowi składniowemu jaki dostarcza typ hinting, zamiast pisać przydługawe if-y takie jak:

function foo($bar, $baz, ...){
    if(is_int($bar)){
//...
    }
    if(is_string($baz)){
//...
    }
}

piszemy po prostu function bar (int $bar, string $baz){//...} co znacznie skraca zapis. Generalnie w obecnych wersjach języka, jedyną rzeczą jakiej brakuje jest owy cukier składniowy. Dokumentowanie istnieje dzięki tagom phpDoc, sprawdzić argumenty możemy dzięki wyżej wspomnianemu if-owi. Czyli tak naprawdę type hinting wiele nie wnosi.

Mogłby jednak wnosić bardzo wiele, o ile umożliwiłby nie tylko sprawdzanie typów ale „grubszą” walidacje np. na wyrażenia regularne, czy obecność pewnych kluczy w tablicy. Szalone ? Nie możliwe ? Oczywiście, że nie! Poniżej przykładowa implementacja w Pythonie który umożliwia takie rzeczy a poniżej wytłumaczenie:

from ctypes import ArgumentError
from re import match
 
class param:
    def __init__(self, type, name):
        self.__type = type
        self.__name = name
    def __call__(self, function):
        def decorated(*args, **kwargs):
            if self.__type.__class__.__name__ == 'str':
                test = match(self.__type, kwargs[self.__name])
                if  test == None:
                    raise ArgumentError('Argument: >>' + self.__name + '<< must match pattern >>"'+self.__type + '"<<')
            else:
                arg_type = kwargs[self.__name].__class__.__name__
                proper_type = self.__type.__name__
                if arg_type != proper_type:
                    raise ArgumentError('Expected object of type >>'+proper_type + '<< for argument >>' + self.__name + '<<')
            function(*args,**kwargs)
        return decorated
 
@param(int, 'bar')
@param('ala.','baz')
def foo(bar, baz):
    print bar, baz
 
foo(bar = '1', baz='ala ma')

Co ten kod robi ? Kod ten używa ficzera Pythona zwanego dekoratorami funkcji. Pokrótce mówiąc, jest to cukier składniowy dzięki któremu można w łatwy sposób przekształcać funkcje.

@param(int, 'bar')
@param('ala.','baz')
def foo(bar, baz):
    print bar, baz

Zapis jest podobny do phpDoca więc powinniście go rozszyfrować – @param(int,'bar') oznacza, że parametr bar funkcji foo powinien być inte-em, natomiast @param('ala.','baz') oznacza, że parametr baz powinien spełniać wyrażenie regularne ‘ala.’

Działa to w ten sposób, że pisząc @param(parametr1, parametr2 ...) przed deklaracją funkcji jest ona przekazywana do wcześniej zdeklarowanej klasy ‘param’, która w konstruktorze bierze argumenty dekoratora(parametr1, paramtr2) a w funkcji __call__ która jest odpowiednikiem php-oweg __invoke robi coś z nimi oraz naszą funkcją i zwraca przekształconą funkcję. W naszym przypadku przeprowadza walidacje parametrów i jeżeli jej nie pasują to wyrzuca wyjątek a jeżeli jest wszystko ok to wywołuje naszą oryginalną funkcje.

I co o tym wszystkim sądzicie ?

Autor wpisu: batman, dodany: 22.06.2010 23:19, tagi: internet

Google wypuścił na świat nowy serwis. Tym razem nie jest to żadne “rewolucyjne” narzędzie do komunikacji, ani kolejny dodatek do poczty, kalendarza, czy dokumentów. HTML5 Rocks, bo o nim mowa, jest serwisem, w którym web developerzy znajdą wszystkie niezbędne informacje na temat tworzenia stron w nowym standardzie.

Swoistym pokazem możliwości jest interaktywna prezentacja, z której dowiemy się o zmianach jakie wprowadza HTML5, przy czym HTML5 oznacza tutaj HTML + CSS + Javascript Web API. Prezentacja została wykonana w całości w HTML5, dlatego też nie będzie działać w przeglądarkach nieobsługujących tego standardu, np IE8.

Drugim ciekawym elementem serwisu jest piaskownica, w której mamy możliwość wprowadzać zmiany do przykładowego kodu i obserwować na żywo efekt tych zmian.

Ostatnim elementem serwisu są tutoriale. W chwili obecnej jest ich tylko 9, jednak z czasem ich liczba będzie rosła. Tutoriale te będą się głównie skupiać na opisywaniu krok po kroku nowych możliwości HTML5.

źródło: blog.chromium.org

Autor wpisu: sokzzuka, dodany: 22.06.2010 10:40, tagi: php

I stało się. Dzisiaj wyszło (przez przypadek) na liście internalsów, że został usunięty safe mode z trunka, co oznacza, że nie będzie go już w przyszłych wersjach. Long live php! ;) . W końcu spełnia się sen niektórych o wywalaniu zaszłości.

Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.