Archive for February, 2010

Po co w ogóle istnieje ten blog?

Usunąłem reklamy google’a z tego bloga. Wcześniej myślałem, że przy 600 odwiedzinach w miesiącu zdobędę jakieś kieszonkowe, powiedzmy, na kino. Myliłem się ;) Przede wszystkim zaskakujące były treści reklamowe zupełnie niepasujące do treści bloga i przez to nieinteresujące gości.

Kilka przykładów:

Dodatkowo zainspirowany wpisem na wspaniałym blogu Setha Godina pomyślałem, że to będzie prezent dla wszystkich moich czytelników. W prawdzie reklamy były wyjątkowo dyskretne, ale teraz nie będzie ich w ogóle. I tyle. Po prostu przypomniało mi się, że tego bloga zakładałem nie po to, żeby zarobić, ale po to by się dzielić.

Dziękuję!

No Comments

Iteracje w projekcie – krótka retrospekcja

Dziś przeglądając w pracy dokument Product backlog jednego z projektów zauważyłem, że całkiem sporo iteracji za nami (dokładnie 6) i pomyślałem, że warto na chwilę zawiesić oko na wykresach wypalania (burndown charts).

Oto wykresy z ostatnich pięciu iteracji

jeśli się z nimi jeszcze nie spotkałeś, krótko wyjaśnię jak je odczytywać. Na osi pionowej odłożona jest ilość rzeczy do zrobienia, na poziomej czas od początku do końca iteracji. Niebieska linia wyznacza idealny sposób wykonywania zadań. To znaczy, ze jeśli szacowanie było w 100% trafne i nie pojawiły się niespodziewane problemy – tak wyglądałby wykres. Czerwona linia wyznacza faktyczny przenbieg iteracji. Jest kreślona na bieżąco codziennie. Jeśli wychodzi powyżej linii niebieskiej – zespół zaczyna mieć więcej zadań do wykonania, niż teoretycznie jest w tanie wykonać, jeśli poniżej – ma za mało zadań, żeby wypełnić pracą iterację

Burndown iteracja 1

Burndown iteracja 1

Burndown iteracja 2

Burndown iteracja 2

Burndown iteracja 3

Burndown iteracja 3

Burndown iteracja 4

Burndown iteracja 4

Burndown iteracja 5

Burndown iteracja 5

Jak widać, działo się różnie ;) Jednak najważniejsze co z nich wynika, to

  1. Zespół zawsze wiedział, czy jest do przodu (pod kreską), czy ma opóźnienia (nad kreską), więc mógł zawsze zareagować dodając lub usuwając zadania z iteracji
  2. W trakcie poznawania projektu i tworzonego systemu jakość szacowania rośnie
  3. Zawsze mamy informację zwrotną odpowiadającą na pytanie “jak dobre jest nasze szacowanie”, widzimy też przeszkody na które trafiliśmy

Nie jestem fanatycznym orędownikiem Agile ani w szczególności Scrum. Jednak nawet najbardziej zatwardziały obrońca modelu wodospadowego musi zauważyć, że przynajmniej niektóre aspekty zwinnego wytwarzania oprogramowania działają. A dzieje się tak dlatego, że zamiast wymagając od ludzi dostosowania się do “maszynowych” procesów – organizuje procesy, do których łatwiej się ludziom dopasować.

O pierwszych krokach przy wdrażaniu Agile możesz poczytać w innym wpisie “Zabawy ze SCRUM

No Comments

Tutorial: dashboard web 2.0 dla leniwych…


czyli dobrych programistów ;)

W moim pierwszym tutorialu napiszę jak wydajnie korzystać z tego co daje Ci cakePHP i jQuery. Aktualnie cake opiera się na współpracy z innym frameworkiem javascript – prototype. Ale już w wersji 1.3 core cake’a ma współpracować z jquery. Już nie mogę się doczekać. Ten tutorial oparłem na wersji 1.3.0-beta dlatego, że przy okazji jego pisania chciałem rzucić okiem na kolejną wersję tego świetnego frameworka php. Jednak nie będę korzystał z żadnych helperów jquery, zatem większość z tych rad powinna pasować do wersji 1.2 (w razie problemów – chętnie służę pomocą).
Jest to mój pierwszy tutorial, więc proszę o wskazówki, komentarze i odrobinę wyrozumiałości.

Efekt jaki chcemy osiągnąć można obejrzeć jako demo oraz źródła na świetnym hostingu, który gorąco wszystkim webdeveloperom i ich klientom polecam: vipserv.org.

W tym tekście zakładam, ze znasz podstawy cakePHP i javascript. Czyli przynajmniej zrobiłeś sławny blog tutorial , wiesz jak mniej więcej używać narzędzia bake, oraz potrafisz przynajmniej zniknąć element na stronie i zmienić jego kolor przy pomocy czystego javascript’u.

Dashboard na zakładkach

Zacznijmy od przygotowania przykładowej aplikacji. Wypiekamy (cake bake) aplikację złożoną z dwóch modeli Thing i Item (każda tabela składa się z id i pola tekstowego name).

Następnie tworzymy dla niej zbiorczy widok (tzw. dashboard, metoda all())

// /app/controllers/items_controller.php
class ItemsController extends AppController {
 //...
 function all() {}
}
// /app/views/items/all.ctp
<?php echo $this->requestAction(
            array(
               "controller"=> "items",
               "action"=> "index"),
            array("return")
      ); ?>
<?php echo $this->requestAction(
            array(
               "controller"=> "things",
               "action"=> "index"),
            array("return")
      ); ?>

Na razie nie przejmujcie się kwestią requestAction i wydajności. Zazwyczaj efekty requestAction należy umieszczać w cache’owanych elementach, ale w naszym przypadku już niedługo zrezygnujemy z samego requestAction.

widok zbiorczy - dashboard

tak to wygląda teraz

Zróbmy z tego widoku zakładki modyfikując widok all.ctp w następujący sposób:

<script type="text/javascript">
   $(document).ready(function() {
      $("#tabs").tabs();
   });
</script>

<div id="tabs">
   <ul>
      <li><a href="#tabs-1">Items</a></li>
      <li><a href="#tabs-2">Things</a></li>
   </ul>
   <div id="tabs-1">
      <?php echo $this->requestAction(
                  array(
                     "controller"=> "items",
                     "action"=> "index"),
                  array("return")
            ); ?>
   </div>
   <div id="tabs-2">
      <?php echo $this->requestAction(
                  array(
                     "controller"=> "things",
                     "action"=> "index"),
                  array("return")
            ); ?>
   </div>
</div>

Żeby wypieczone widoki były “otoczone” ramką z zakładki – trzeba w nich dodać taki fragment na samym końcu plików index.ctp:

<div style="float: none; clear: both;">&nbsp;</div>
Widok zakładek

tak wyglądają nasze zakładki

Dodajmy więcej elementów, żeby zobaczyć jak zachowuje się pagination…

insert into items(`name`) values ('Item 4'), ('Item 5'), ('Item 6'), ('Item 7')

i zmieńmy parametry stronicowania, żeby efekty stronicowania zobaczyć szybko ;)

class ItemsController extends AppController {
//...
	var $paginate = array(
	  'limit'=> 5
	);
//...

Widzimy, że w naszym zbiorczym widoku link ciągle prowadzi do items/index. Wolelibyśmy, żeby stronicowanie odbyło się w zakładkach.

Można oczywiście próbować rozwiązania via php. Oprogramować obsługę parametrów w metodzie all(), zmienić cel linków stronicowania i sortowania w widokach. I ogólnie dużo napracować się nad tym, żeby nasz kod wyglądał źle ;) . Już za chwilę moc javascript i w szczególności jQuery przyjdzie nam z pomocą. Zanim to nastąpi – zamieńmy nasze zakładki na Ajaxowe zmieniając widok all.ctp:

// views/items/all.ctp
<script type="text/javascript">
   $(document).ready(function() {
      $("#tabs").tabs();
   });
</script>

<div id="tabs">
   <ul>
      <li>
         <?php echo $html->link(
                  "Items",
                  array(
                     "controller"=> "items",
                     "action"=> "index"
                  )
               );
         ?>
      </li>
      <li>
         <?php echo $html->link(
                  "Things",
                  array(
                     "controller"=> "things",
                     "action"=> "index"
                  )
               );
         ?>
      </li>
   </ul>
</div>

…prościzna, prawda? Kwestię requestAction też już mamy z głowy. Jednak pojawił się problem – wnętrze zakładek pojawiło się w powtórzonym layoucie.
Szybko naprawimy to w app_controller.php przy pomocy RequestHanlder:

// /app/app_controller.php
var $components = array("RequestHandler");
function beforeFilter(){
    if($this->RequestHandler->isAjax()){
      $this->layout = "ajax";
    }
}

Wrócmy do kwestii stronicowania (i sortowania przy okazji). Chcemy, aby kolejne strony ładowały się w danej zakładce. Wykorzystajmy do tego potęgę jQuery.
Dodajemy taki prosty skrypt

// views/items/all.ctp
   $('#tabs a[href*=/sort:],#tabs a[href*=/page:]').live('click', function(){
       $('#tabs>div:visible').load($(this).attr('href'));
          return false;
   });

Jeśli interesuje Cię co się w tym fragmencie dzieje, zapraszam na do innego wpisu. W dużym skrócie: łapiemy linki, które mają w adresie “sort:” i “page:” i zmuszamy je aby przeładowały zawartość zakładki, zamiast całej strony.

, , ,

3 Comments