Ostatnio miałem nieprzyjemność integrowania płatnego komponentu ExtJs. Chodzi  konkretnie o komponent kalendarza, który wyglądem oraz funkcjonalnością naśladował Google Calendar. Niestety kod był napisany fatalnie, powiązania pomiędzy poszczególnymi elementami kalendarza były bardzo zawiłe co strasznie utrudniało integracje z istniejącym systemem.  Śledzenie wywołań poszczególnych metod i ich parametrów przysparzało sporo problemów. Dodatkowo dodawanie linijki console.log(arguments) wewnątrz cudzych funkcji nie jest eleganckie (choć i tak często to robię…)

Chcąc ułatwić swoją pracę i jednocześnie rezygnując z niepotrzebnego ingerowania w obcy kod stworzyłem bardzo prostą w działaniu funkcję do monitorowania wywołań metod wskazanych klass i obiektów (W przypadku biblioteki ExtJs sprawuje się ona znakomicie).

/**
 * Klasa monitoruje wywołania metod klas i obiektów
 *
 * @author Hubert Marzec
 * @param {Object} source - klasa/obiekt
 * @param {string} className - nazwa klasy
 * @param {array} methodList - wyszczególniona lista metod
 */
function classLogger(source, className, methodsList){
  if (source &&
    (typeof source == "object" || typeof source == "function")) {
    source = source.prototype || source;
    className = className || '';
    methodsList = methodsList || [];

    var name, method;
    var vLog = (typeof(console) !== 'undefined'
      && console != null) ? console.log : function(){};

    for (name in source){
     method = source[name];
     if (typeof method == "function" && (methodsList.length ?
       (inArray(methodsList, name)): true )){

       source[name] = function(name, method){
       return function(){
         var vResult = method.apply(this, arguments);
         vResult = vResult || '';
         vLog(className + '->' + name,
           arguments, ' = ',
           vResult
         );
         return vResult;
       };

      }(name, method);
     }
    }
  }

  function inArray(pArray, value) {
   pArray = pArray || [];
   for(var i=0, all=pArray.length; i < all; i++) {
     if (pArray[i] == value) {
       return true;
     }
   }
   return false
  }
}

Działanie jest dość proste: wybrane lub wszystkie (gdy nie wyszczególniono konkretnych) metody danego obiektu/klasy zostają opakowane przez dodatkową funkcje, która jest odpowiedzialna za wyświetlanie nazwy, parametrów wejściowych oraz wyniku funkcji oryginalnej.

firebug.jpg

Całość jest wyświetlana przy pomocy konsoli przeglądarki. W przypadku gdy konsola nie jest zaimplementowana, logi nie są wyświetlane. Przykładowe sposoby użycia classLoggera:

var mainWindow = new Ext.Window({
  title: 'okno'
});
classLogger(Ext.Panel, 'Ext.Panel');
classLogger(mainWindow, 'mainWindow');

Będzie mi bardzo miło, jeżeli ten fragment kodu się komuś przyda. Jednocześnie jestem otwarty na uwagi i waszą opinię.

category ExtJs, javascript, programowanie Komentarze (2)

NetDay Lublin #1

poniedziałek, 22 grudnia 2008

W końcu Lublin ma swoją imprezę branży internetowej “NetDay Lublin“. Nawet Zamość ze swoim ZamCamp nas wyprzedził, ale lepiej późno  niż wcale. Za całą imprezę odpowiada firma Enzo za co należą im się brawa, mam nadzieje, że do następnych edycji dołączy się więcej firm.

netday_mini.jpg

Pomimo choroby postanowiłem być twardy i nie opuścić pierwszego spotkania. Moja gorączka niestety stłumiła odbieranie bodźców zewnętrznych i pozwoliła dotrwać jedynie do końca części oficjalnej (z darmowego piwka i rozmów kuluarowych musiałem zrezygnować).

Impreza bardzo była udana i dobrze zorganizowana. W prawdzie wynikło 20 minutowe opóźnienie spowodowane nie punktualnością większości słuchaczy. Sam zjawiłem się tuż przed 15-stą i byłem jedną z pierwszych osób na sali. Pomimo awarii mikrofonu wszystko było słychać wyraźnie i nie wpłynęło to negatywnie na całą imprezę. Były trzy zapowiedziane prezentacje (materiały z nich są dostępne na oficjalnej stronie) oraz dwa spontaniczne wystąpienia bez oprawy graficznej, również bardzo ciekawe. Organizatorzy zapewnili gorące napoje oraz kupon na darmowe piwko :)

Moje uwagi co do imprezy: zaczynać zgodnie z planem, zwiększyć ilość prezentacji do 4 i organizować zebrania częściej niż raz na dwa miesiące.

category życie programisty Komentarze (4)

Najciekawszym zadaniem w mojej ostatniej pracy było przeprowadzenie procesu rekrutacji na stanowisko programisty i webmastera. Było to dla mnie zupełnie nowym doświadczeniem, do tej pory stałem po drugiej stronie barykady i nie miałem pojęcia co mnie czeka. Teraz bogatszy o ciężko zdobyte doświadczenia chciałbym się z Wami podzielić moimi obserwacjami i wnioskami z perspektywy osoby rekrutującej (jednocześnie programisty). Zaznaczam jednak, że moje przemyslenia będą odnosić się do rekrutacji w małej firmie.

Obalenie mitu, że nie ma pracy

Jak to jest, że wszyscy narzekają, że nie pracy, że brakuje poważnych i ciekawych ofert, a z drugiej strony pracodawcy (no i po części ja ;) narzekają, że nie można znaleźć dobrego pracownika? Praca jest i czeka, ale aby ją wykonać trzeba coś umieć! A z tym jest duży problem. Rozumiem, że Ci lepsi i bardziej mobilni wyjechali do Krakowa czy Warszawy (mieszkam i pracuje w Lublinie), ale ktoś na pewno musiał zostać.

Błędy popełniane przez szukających pracy

Przede wszystkim strasznie niski poziom i jakość nadsyłanych CV! Jest to szczególnie przykre, ale po informatykach spodziewałem przyzwoitych aplikacji, a tu lipa z nędzą. CV ma zachęcić, zainteresować a nie odstraszać! Ludzie opisują doświadczenie w zawodach w ogóle nie związanych ze stanowiskiem na jakie się ubiegają. Niektórzy uważali, ze na stanowisko webmastera na pewno przyda się doświadczenie w Żabce, Stokrotce czy jako operatora wózka widłowego (sic!). Do tego dochodzi notoryczne pomijanie informacji o wykonywanych obowiązkach czy posiadanych umiejętnościach. Nigdy nie uważałem siebie jako eksperta w przygotowywaniu CV, ale wiele godzin spędzonych na czytaniu o tym jak powinien, a jak nie powinien wyglądać życiorys, pozwoliło mi poznać przynajmniej podstawy tej trudnej sztuki. Widać, że dla większości ciągle jest to za trudne. O tym jak według mnie powinno wyglądać CV programisty napiszę innym razem.

Kolejna sprawa to nie potwierdzenia i nie stawianie się na umówione spotkania. Wystarczy poinformować, że nie jest się już zainteresowanym. Innym ciekawym zjawiskiem jest ‘wbijanie się na harpuna’ do siedziby firmy bez zaproszenie, bez wysłania cv - takim mówi się od razu do widzenia.

Z wiedzą, nawet tą podstawową także jest duży problem. Wiem, że nie można wszystkiego nauczyć się na pamięć i zawsze można znaleźć odpowiedz w internecie, ale jeżeli startujesz na stanowisko programisty i proste zapytanie sql z sumowaniem i agregacją jest ponad twoje siły, zastanów się czy aby na pewno wybrałeś sobie dobry sposób na zarabiania na życie.

Sama rozmowa

Nie uznaje żadnych pseudo-psychologicznych testów osobowych, czy pytań w stylu: pokój jest wypełniony różnokolorowymi piłeczkami, gdybyś mógł, jaką chciałbyś zostać rośliną? WTF. Dlatego rozmowa, pomijając części formalne, była czysto techniczna. Bardzo ważne: niezależnie jakim doświadczeniem może się pochwalić aplikant, zawsze należy zweryfikować jego wiedzę. W przeciągu 20 minut rozmowy można się zorientować czy dana osoba zna się na rzeczy, czy tylko wpisuje głupoty w swoim życiorysie, aby go jakoś wypełnić. Pozwoli to uniknąć przykrych niespodzianek.

W przypadku wymagań finansowych, też było ciekawie. Z jednej strony osoby mniej doświadczone nie potrafiły się wycenić, z drugiej strony zdarzały się dzbany, które mimo bardzo malej wiedzy i doświadczenia żądały dość astronomicznych sum (uwierzcie, ciężko zachować powagę jak gość, który nie odróżnia javy od javascriptu, kod html opiera tylko na tabelkach, a na koniec żąda 2500zł netto - no nie wiem, może w Warszawie by tyle dostał…)

Jak ciekawostkę wspomnę, że kiedy CV mi się podobało i byłem zainteresowany, prosiłem również o próbki kodu. W ten sposób wystraszyłem 50% zaproszonych osób na rozmowę. Albo kontakt się urywał, albo przekładali ciągle spotkania, tłumacząc się, że nie zdążyli przygotować tych próbek!? Żenada. Twój kod to twoja wizytówka. Bez niej nie pokazuj się ludziom ;)

category życie programisty Komentarze (27)

Dzban IT #2: Jak nie korzystać z svn’a.

wtorek, 28 października 2008

Pierwszy post z jakże ciekawej serii “co za idiota napisał ten kod” mogliście przeczytać tutaj. W kontynuacji tej kategorii będę opisywał błędy, idiotyczne rozwiązania z jakimi spotykam się na co dzień. A powstają one z różnych powodów:

  • bo coś źle się wgrało
  • bo się pomyliłem
  • bo wgrałem pliki z innego projektu
  • bo właśnie robiłem kupę i tak wyszło…

Dzisiaj zajmiemy się sprawą korzystania z repozytorium SVN. Obecnie system kontroli wersji jest narzędziem codziennego użytku w każdym polskim domu, co za tym idzie korzystanie z niego nie powinno przysparzać problemów. Jakże mylne jest to stwierdzenie.

Na pierwszy ogień idzie tworzeniu wielu głównych katalogów (modułów) w ramach jednego repozytorium svn. I tak powstają kwiatki typu: /trunk/dzbany, /trunk/dzbanek, /trunk/dzbany2, /trunk/DZBANY.

Druga sprawa to wrzucanie plików tymczasowych do repozytorium, a w szczgólności Thumb.db pod windosem. Wybrane pliki można dodać do ignorowanych i problem mamy z głowy.

Apropos wgrywania niepotrzebnych plików na serwer, oddzielną kategoria są pliki konfiguracyjne (np. config.ini). Te pliki również powinny być ignorowane. Można jedynie zamieścić szablon pliki konfiguracyjnego np. pod nazwą config.sample

I na miłość boską, rozwiązaniem konfliktu nie jest ‘prawy przycisk i mark as merget’. Jeżeli wystąpił konflikt sprawdź co go spowodowało i popraw go ręcznie.

category dzbany Komentarze (3)

Przytul programiste

niedziela, 28 września 2008

Kapitalne video pokazujące z jakimi problemami musi sobie radzić programista przez całe życie.

category programowanie, życie programisty Komentarze (2)

Zadanie było jasno określone: po dwukrotnym kliknięciu radiobuttona miał się on odznaczać. Czyli miał się zachowywać dokładnie jak checkbox. Po krótkich poszukiwaniach odnalazłem ten plugin - wszystko fajnie, radiobutton się odznacza, ale przy submicie formularza i tak pamiętana jest stara wartość zaznaczonego radiobuttona :/

Zgodnie z zasadą - jeżeli coś ma być zrobione dobrze, zrób to sam - napisałem prosty plugin, który przy dwukrotnym kliknięciu odznacza radiobutton, a przy wysyłaniu formularza usuwa wcześniej nadaną wartość.

/**
 * Behave like Checkbox
 *
 * @autor Hubert Marzec
 * @link dzbanyit.pl
 */
(function(){
jQuery.fn.likeCheckbox = function() {
    return $(this).each(function(){
        $(":radio", this).dblclick(function(){
            $(this).clone().hide().val('').insertAfter(this);
        });
    });
}
})(jQuery);

Jest to najprostsza wersja, bez limitu na liczbę tworzonych klonów- uznałem to za zbędne. A działanie jest bardzo proste, tworzymy klon naszego radiobuttona, ustawiamy pustą wartość, ukrywamy i wstawiamy za oryginalnym obiektem. Testowane pod IE 6,7 oraz FF 2,3.

category javascript, jquery Komentarze (0)

Zend Framework to oczywiście najlepszy framework php ;) ale ma bardzo duże problemy co do wydajności. Przy prostych stronach można to jeszcze przeżyć, ale dla aplikacji, które są przewidziane na duże obciążenie staje się to już drzazgą w tyłku.

Na pierwszy ogień pójdzie funkcja Zend_Loader::registerAutoload() - korzystanie z niej jest bardzo wygodne, ale wpływa bardzo negatywnie na szybkość i ilość pamięci wykorzystywanej przez skrypt.

Przeprowadźmy test

Aby test miał więcej wspólnego z rzeczywistością zostanie wykonany na już przygotowanym frameworka do pracy. Z grubsza będą to:

  • Inicjacja i sprawdzenie autoryzacji na podstawie Zend_Acl (lista praw trzymana w pliku php)
  • Wykryciu i ustawieniu odpowiedniego locale (Zend_Locale)
  • Ustawienia widoku
  • Nawiązanie połączenia z bazą danych MySql
  • Ustawieniu routes na podstawie plików ini
  • Inicjacji Zend_Mail_Transport_Smtp

Akcja indexAction() jest pusta, nie jest wykonywane żadne zapytanie do bazy danych. Do profilowania kodu użyłem zmodyfikowanego Profiler’a z Magento Ecommerce. Kod został podzielony na odpowiednie bloki: application, setup, dispatch, action. Na mojej maszynie mam:

  • PHP 5.2.4
  • XDebug 2
  • Apache 2
  • MySql 5

Samych parametrów mojego komputera nie będę podawał, bo nie ma to sensu, nie będziemy zwracać uwagi na same liczby, a raczej na różnice procentowe.

zf_autoload_without_require_mini.png

Nas najbardziej interesuje pierwszy rząd  ‘Application’ obejmująca całą aplikacje. Najbardziej przykuwającą oko rzeczą jest pożerana pamięć 5MB.

Teraz przygotujemy plik require.php, który dołączamy jak najwcześniej w bootstrapie. Plik zawiera dołączenia wszystkich klas aplikacji (Zend i nasze własne), które były potrzebne do uruchomienia strony testowej.

require_once 'Zend/Session.php';
require_once 'Zend/Session/Abstract.php';
require_once 'Zend/Session/Namespace.php';
require_once 'Zend/Session/SaveHandler/Interface.php';
require_once 'Zend/Session/Exception.php';
require_once 'Zend/Exception.php';
require_once 'Zend/Acl/Role/Interface.php';
require_once 'Zend/Config.php';
itd...

zf_autoload_with_require_mi.png

Pomimo prawie 3-krotnie zmniejszonego czasu (0.25 vs 0.09), pamięć wykorzystana przez skrypt zmalała z 5 MB do 326 KB, to ponad 10-krotnie mniej!!! Nawet sobie nie wyobrażacie jaka to ulga dla serwer.

Listę plików ładowanych podczas działania aplikacji można otrzymać dzięki informacji jakie klasy zostały załadowane (w ZF jedna klasa to jeden plik), a tę informacje dostajemy dzięki funkcji get_declared_classes().

Wniosek

Na etapie tworzenia aplikacji spokojnie możemy ze względu na wygodę używać Zend_Loader::registerAutoload(), zaś już w czasie wdrażana zdecydowanie sugeruję rozwiązanie podobne do mojego.

category php, programowanie, zend framework Komentarze (8)

Prosty projekcik (to nie znaczy, że nie pracochłonny): formularz z ponad 130 polami, podzielonymi na około 75 kroków. Ktoś miał się zając przygotowanie formularza: html, css i walidacją na poziomie javascript!? Ja miałem to obsłużyć w php (jak będzie o tym w kolejnym wpisie, bo problem jest nawet ciekawy).

bad_code_mini.png

No i otrzymuje z pewnym opóźnieniem nie gotowy ‘formularz’. A tam lista pytań z nazwami pól: q1, q2, q3…q45…q99… a często miedzy nimi q9a, q9b, q9c WTF? Formularz był nie kompletny i musiałbym jeszcze między q9a a q9b wsadzić jeszcze 20 pytań. Gdybym sam umieścił w bazie danych pola o takich wartościach - to potem stałbym się bohaterem podobnego wpisu, a programista zajmujący się tym później projektem wyzywałbym mnie od dzbanów.

Nie pozostało mi nic innego jak samemu wszystko robić od nowa.

Wniosek: czasem lepiej zrobić wszystko od początku niż korzystać z czyiś rozwiązań.

category dzbany, programowanie, życie programisty Komentarze (3)

Jak ustawić wirtualne hosty w apachu

poniedziałek, 21 lipca 2008

Chciałem ustawić kilka wirtualnych hostów pod apache, przy czym zachowująć możliwość przeglądania głównego drzewa katalogów poprzez 127.0.0.1.

apache_logo_medium.png

Największy problem był w tym, że działał tylko jeden wirtualny host, jak dodawałem kolejny wszystko się sypało. Strasznie długo szukałem rozwiązania tego problemu. Żadne (nie)oficjalne materiały nie pomagały. Ale udało się. Mam nadzieje, że oszczędzi wam to dużo czasu.

Rozwiązanie

Konfiguracje opieram na apachu 2.x dostępnym w super paczce xampp i system win xp. Przy innym zestawieniu nazwy plików mogą się różnić, ale filozofia jest taka sama. Otwieramy plik \apache\conf\extra i umieszczamy kod podobny do tego:

NameVirtualHost *:80

<virtualhost *:80>
ServerName localhost
DocumentRoot E:/projects/
</virtualhost>

<virtualhost *:80>
ServerName photos.dzbanyit.pl
DocumentRoot E:/projects/krci
</virtualhost>

<virtualhost *:80>
ServerName projects.dzbanyit.pl
DocumentRoot E:/projects/margo/www
</virtualhost>

Następnie edytujemy pliku C:\Windows\system32\drivers\etc\hosts:

127.0.0.1 localhost

127.0.0.1 photos.dzbanyit.pl

127.0.0.1 projects.dzbanyit.pl

Restartujemy apacha i możemy się cieszyć poprawnie skonfigurowanymi wirtualnymi hostami ;)

category programowanie, życie programisty Komentarze (2)

Ostatnio natrafiłem na pewien problem podczas prac nad nowym projektem (opartym o Zend Framework). Rozwiązanie problemu sprowadza się do odpowiedzi na pytanie: Co jest ważniejsze: optymalizacja czy zasady programowania obiektowego?

Mamy model Photos oraz klasę PhotosRow reprezentujący pojedynczy rekord. W aplikacji istnieją różne rodzaje zdjęć (profile, albumy, wydarzenia). Każde zdjęcia, w zależności od typu posiada kilka różnych rozmiarów.

class Photos extends Advaf_Db_Table {
    public function deleteAlbumPhoto($photosId, $usersId) {
         if ($this->_hasAccessToPhoto(..., 'albums')) {
             $row = $this->findOne($photosId);
             return $row->delete();
         }
         return null;
    }

    public function deleteEventPhoto($photosId, $usersId) {
         if ($this->_hasAccessToPhoto(..., 'events')) {
             $row = $this->findOne($photosId);
             return $row->delete();
         }
         return null;
    }
}
class PhotosRow extends Advaf_Db_Table_Row {
    public function _postDelete() {
        $mgr = new PhotoMgr();
        foreach ($mgr->getSizes(this->type) as $size) {
            $path = $mgr->getPhotoFullPath(...);
            if(file_exists($path)) {
                unlink($path);
            }
        }
    }
}

Chciałem umieścić w metodzie _postDelete() klasy PhotosRow cały kod odpowiedzialny za usuwanie z przestrzeni dyskowej odpowiednich zdjęć. Mój kolega, Szymon od razu zwrócił uwagę, że to nie jest optymalne. Aby usunąć rekord, najpierw go pobieramy, potem usuwany (SELECT + DELETE, zamiast tylko DELETE). Wykonujemy jedno zapytanie dodatkowo, dla każdego zdjęcia. Dla 20 zdjęć do 20 extra zapytań! Szymon zaproponował aby przenieść cały kod odpowiedzialny za usuwanie zdjęć do Photos, a zdjęcia usuwać bezpośrednio $this->delete($where).

class Photos extends Advaf_Db_Table {
    public function deleteAlbumPhoto($photosId, $usersId) {
         if ($this->_hasAccessToPhoto(..., 'albums')) {
            $where = $this->quoteInto('id = ?', $photosId);
            $this->_unlink(...);
            return $this->delete(where);
         }
         return null;
    }

    private function _unlink(...) {

    }
}

Mimo wszystko obstaje przy swoim. Zgodnie z zasadą odpowiedzialności to klasa PhotosRow powinna po sobie posprzątać. Wydaje mi się, że właśnie w tym celu developerzy Zenda stworzyli takie metody jak _postDelete czy _postInsert w klasie Zend_Db_Table_Row.

Nie oznacza to oczywiście, że twórcy ZF nie moją głupich pomysłów… moim zdaniem do nich m.in należy obsługa formularzy i osobiście preferuje rozwiązanie Szymona: Fasic_Form.

Jestem bardzo Ciekawy twojej opinii, bo sprawa nadal nie jest rozstrzygnięta. Być może znasz lepsze rozwiązanie?

category życie programisty Komentarze (13)