2014-10-02

Instalacja Symfony2 w chmurze RedHat'a

Jeśli jeszcze nie kojarzysz nazwy OpenShift to najwyższa pora nadrobić zaległości. RedHat udostępnia kompletnie za darmo możliwość instalacji 3 aplikacji (dla każdej dostajesz konto na serwerze wirtualnym dostępne przez ssh). Wspierane są wszystkie mainstreamowe technologie, od Javy, Rubiego, Pythona, Node.js aż po poczciwego Perla. 

Mnie oczywiście interesuje PHP, obecnie dostępne na openshift w ver. 5.4 ... Na początek wystarczy ;) Po rejestracji na stronie openshift.redhat.com system zachęca do instalacji pierwszej aplikacji. Tworząc nową aplikację w technologii PHP mamy do wyboru dwie możliwości. Skorzystanie z gotowej preinstalowanej aplikacji/frameworka lub wybór wersji PHP i "ręczną" konfigurację środowiska. Pierwsza opcja umożliwia expresowe postawienie szkieletu aplikacji i będzie atrakcyjna dla mniej doświadczonych developerów, lub w przypadku krótkoterminowych projektów, gdzie istotny jest czas. Możemy mieć gotowego WordPressa czy Drupala po jednym kliknięciu. Początkowo ucieszyłem się, że obok takich frameworków jak CakePHP dostępny jest też "od ręki" gear w Symfony2. Niestety okazało się, że jest on skonfigurowany z cardrige'm PHP 5.3 ... cóż trochę słabo...

Jak postawić najnowszą wersję Symfony2 z PHP 5.4 na platformie OpenShift ?

1. Zakładamy nową aplikację "Based on PHP 5.4 Cardridge", niech się śmiesznie nazywa np "polanto" ;)

2. Jeśli jeszcze nie masz skonfigurowanego klucza SSH, koniecznie użyj rhc do konfiguracji ssh na swoim komputerze.

3. OpenShift używa GIT do deploymentu aplikacji, bardzo spodobała mi się opcja autodeploymentu po push'u do repo, ale o tym za chwilę. Najpierw sklonujmy nasze repo do katalogu w, którym będziemy rozwijać aplikację lokalnie: 

$ git clone ssh://541a83925004460525001038@polanto-motyl.rhcloud.com/~/git/polanto.git/ 

4. Pora stworzyć nowy projekt Symfony2 w podkatalogu php aplikacji:

$ cd polanto
$ composer create-project symfony/framework-standard-edition php
$ git add -A
$ git commit -am "Blank Symfony Project"

5. Zanim wypchnę projekt do chmury warto skonfigurować deploy. OpenShift ma bardzo fajną cechę, polegającą na automatycznym deploymencie aplikacji po tym jak wykonasz git push. W przypadku Symfony2 warto jeszcze zadbać o zautomatyzowanie instalacji nowych, lub zupgradowanych pakietów Composera. W tym celu tworzę w podkatalogu ./openshift/action_hooks plik deploy:

---------------------------------------------------------------
#!/bin/bash
# .openshift/action_hooks/deploy

 export COMPOSER_HOME="$OPENSHIFT_DATA_DIR/.composer"

   if [ ! -f "$OPENSHIFT_DATA_DIR/composer.phar" ]; then
       curl -s https://getcomposer.org/installer | php -- --install-dir=$OPENSHIFT_DATA_DIR
   else
       php $OPENSHIFT_DATA_DIR/composer.phar self-update
   fi

 unset GIT_DIR
 cd $OPENSHIFT_REPO_DIR/php
 php $OPENSHIFT_DATA_DIR/composer.phar install

  chmod -R 0777 $OPENSHIFT_REPO_DIR/php/app/cache

  chmod -R 0777 $OPENSHIFT_REPO_DIR/php/app/logs
---------------------------------------------------------------
$ git add -A
$ git commit -am "deploy hook"

Ten skrypt zadba o instalację composera (jeśli go brakuje) a następnie instalację wymaganych pakietów.

6. Push jest tuż za zakrętem jeszcze tylko jeden szczegół. Ten krok nie jest konieczny, ale przyśpieszy deployment w początkowej fazie projektu, kiedy tych push'y może być najwięcej. OpenShift robi nam "niedzwiedzią przysługę" restartując PHP, MySQL i inne serwisy dodane do projektu, przy każdym deploy. Jeśli nie chcemy tego feature'a wystarczy dotknąć marker hot_deploy

$ touch ./openshift/markers/hot_deploy
$ git add -A
$ git commit -am "enable hot deploy"

7. PUSH push push push push push :)

$ git push

Zauważ, że w tle, po stronie serwera instalowane są pakiety composera, aż miło popatrzeć... :)

Kilka rad wójka dobra-rada:
Korzystaj z RHC - życie będzie prostsze!
po ssh najłatwiej połączyć się komendą
$ rhc ssh mojaaplikacja

W przypadku problemów z composerem, zalogować się na konsoli i uruchomić
$ $OPENSHIFT_DATA_DIR/composer.phar self-update 

W przypadku błędów, zapraszam do lektury w tym katalogu:
$ cd $OPENSHIFT_LOG_DIR

404 po pierwszym deploymencie? Zrestartuj php - wykonaj:
$ ctl_app restart

Service Temporarily Unavailable ? Restart PHP!

2013-08-22

Iterator Modelu w CakePHP

Zadziwiające jak często stajemy przed problemem przeiterowania przez wszystkie rekordy jakiegoś modelu (np. przy generowaniu raportów). Najefektywniejszym rozwiązaniem jest zastosowanie interfejsu iteratora. Uniwersalny przykładem dla CakePHP może być ModelIterator. Klasę umieszczam w katalogu Model/Iterator
 
/**
 * Iterates Model
 *
 * @author gmotyl
 */
class ModelIterator implements Iterator{
  
  protected $key;
  protected $index;
  protected $count;
  protected $conditions;
  protected $Model;

  /**
   * Class constructor
   * 
   * @param array $conditions CakePHP find conditions
   */
  public function __construct($modelName, $conditions = NULL) {
    App::import('Model', $modelName);
    
    $this->Model = new $modelName;
    $this->conditions = $conditions;
    $this->count = $this->Model->find('count', array('conditions' => $conditions));
  }

  /**
   * Returns current row
   * 
   * @return array
   */
  public function current() {
    return $this->Model->find('first', array('conditions' => $this->conditions, 'page' => $this->index));
  }  
  
  /**
   * Increments row
   */
  public function next() {
    $this->index++;
  }
  
  /**
   * Returns index
   * 
   * @return int
   */
  public function key() {
    return $this->index;
  } 
 
  /**
   * Rewind to first index
   */
  public function rewind() {
    $this->index = 1;
  }
  
  /**
   * Returns FALSE if iteration is over
   */
  public function valid() {
    return ($this->count >= $this->index);
  }
}
Dodatkowo wygodnie będzie zaimplementować interfejs IteratorAggregate w AppModel (po którym dziedziczą wszystkie Modele w CakePHP:


App::uses('ModelIterator', 'Model/Iterator');

class AppModel extends Model implements IteratorAggregate{

(...)

  public function getIterator() {
    return new ModelIterator($this->name);
  }

(...)
Dzięki temu możemy w dowolnym miejscu użyć kostrukcji foreach, która przeiteruje się przez wszystkie elementy Modelu bez konieczności jawnego instancjonowania iteratora np. w kontrolerze UsersController :


    foreach ($this->User as $user) {
      debug($user);
    }   

2013-04-25

require.js i CakePHP

Jak w prosty sposób zaimplementować require.js w CakePHP?

Po ściągnięciu require.js wraz ze wszystkimi jego zależnościami do app/webroot/js

wrzucamy do layoutu kod:

<?php
  echo $this->Html->script( 
            'require', array(
            'data-main' => $this->Html->assetUrl('main', array(
            'pathPrefix' => JS_URL,
            'ext' => '.js'
          ))
          ));
?>

następnie w pliku app/webroot/js/main.js możemy includować poszczególne biblioteki np:

require(
  [
    "jquery/jquery-ui-1.8.23.custom",
    'jquery-ui-timepicker-addon',
    'underscore/underscore-min',
    'backbone/backbone-min',
    'bootstrap.min'
  ], 
  function($) {
    requireReadyFunction();
  }
);

2013-03-06

Testy jednostkowe - Instalacja PHPUnit

Zanim ochoczo zabiorę się do instalacji PHPUnita najpierw warto przypomnieć: po co to komu? :) Testy jednostkowe (ang. unit test) służą do częstego weryfikowania poprawności działania pojedynczych elementów programu takich jak np. obiekty. Przykładowo gdy piszemy klasę modelu korzystając z jakiegoś frameworka PHP, warto napisać dla niej Unit testy. Dzięki temu po każdej zmianie kodu aplikacji, możemy zyskać pewność, że kod działa poprawnie bez żmudnego manualnego testowania. Tak więc poddajemy fragment programu testowi, który wykonuje go i porównuje wynik (np. zwrócone wartości, stan obiektu, wyrzucone wyjątki) z wynikami oczekiwanymi. 
Co nam to daje, poza wysiłkiem implementacji test casów? Otóż zyskujemy możliwość błyskawicznego wychwycenia i poprawienia błędu - zanim jeszcze zostanie wprowadzony na środowisko produkcyjne.

OK to tyle jeśli chodzi o teorię - pora na praktykę. A w praktyce... cóż PHPUnit żądzi i basta :) Jest to swego rodzaju złoty standard w temacie testowania kodu PHP. Ale jak go zainstalować?

Zwyczajnie :) PEARem:

sudo pear config-set auto_discover 1
sudo pear install pear.phpunit.de/PHPUnit

W idealnym przypadku wystarczy następnie zainstalować PHPUnit
sudo apt-get install phpunit

i już można rozpocząć testy...
... niestety wielu użytkowników ubuntu napotka tu pierwsze schody, mi też się to przytrafiło. Po napisaniu przykładowego testu zabrałem się ochoczo do uruchomienia testu... lecz próba uruchomienia z terminala phpunit powodowała tylko komunikat:

PHP Fatal error: Call to undefined method PHP_CodeCoverage_Filter::getInstance() in /usr/bin/phpunit on line 39


Okazuje się, że niektóre wersje Ubuntu zawierają błąd na szczęście łatwo go samodzielnie naprawić uruchamiając po kolei komendy:

sudo pear config-set bin_dir /usr/bin
sudo pear config-set doc_dir /usr/share/php/doc
sudo pear config-set php_dir /usr/share/php
sudo pear config-set cfg_dir /usr/share/php/cfg 
sudo pear config-set data_dir /usr/share/php/data
sudo pear config-set test_dir /usr/share/php/test
sudo pear uninstall phpunit/PHPUnit
sudo pear channel-discover pear.phpunit.de
sudo pear install --alldeps phpunit/PHPUnit

2013-01-08

Odroid U2 - juz tydzień "zabawy"

Po tygodniu obcowania z odroidem U2 muszę stwierdzić - fajna zabawka ale raczej dla dużych dzieci... i to najlepiej z backgroundem informatycznym i ogromną dozą cierpliwości :) Jest to produkt developerski i jako taki nie pozbawiony błędów, nadal rozwijany. Nowe wersje softu wydawane są przez producenta co kilka dni. Na szczęście support na forum społeczności jest bardzo sprawny i nikt nie jest pozostawiany bez pomocy. Ja w pierwszym tygodniu użytkowania skupiłem się na konfiguracji androida - gdyż dla mnie Odroid ma pełnić funkcję HTPC oraz konsoli do gier. Z linuxem poczekam jeszcze parę dni, gdy większość bugów zostanie rozwiązanych. Pewną bolączką wydawanych nowych wersji softu Hardkernela jest konieczność flushowania całej karty pamięci i co za tym idzie utrata wszystkich ustawień i aplikacji. Gdy się to robi raz czy kilka razy dziennie - przestaje być już zabawne... dlatego cierpliwie poczekam.

Cierpliwością muszą się również wykazać domownicy użytkownika Odroida... Mam w domu jeden TV z wejsciem HDMI (stary monitor DVI się nie nadaje), dlatego podziwiam moją rodzinę, która cierpliwie znosiła moje klikanie bezprzewodową klawiaturą, ciągle restarty, gorączkowe ściąganie ROMu, przekładanie kart eMMC z odroida do czytnika, flash, itd. itd....
Przez ten tydzien przetestowałem pierwszą nieudaną wersję ICS, nstępnie JB w wersji od hardkernela i Cyanogenmod dla Androida. Ostatecznie zostałem przy najnowszym ICS gdzie jest zdecydowanie mniej bugów...

W obecnej chwili mam dwa zasadnicze problemy do rozwiązania. Odroid nie widzi mojego dysku USB 2.5 cala 1 TB Verbatim, problem z emulacją dotyku za pomocą myszki.

Trzeci problem z nie działającym padem bezprzewodowym XBOX360 rozwiązałem wpisując w terminal emulator:

su
mount -o remount,rw /system

Ponieważ niestety komenda cp nie działa... użyłem cat :)
cat /system/usr/keylayut/Vendor_045e_Product_028e.kl > Vendor_045e_Product_0719.k
chmod 644 /system/usr/keylayout/Vendor_045e_Product_0719.kl
reboot
Rozwiązanie to opiera się na tym, że w androidzie jest profil dla pada przewodowego - brakuje jedynie dla bezprzewodowego. Proste skopiowanie profilu pod inną nazwą rozwiązało problem.

2013-01-02

Odroid U2

W ubiegły poniedziałek nareszcie dotarł z korei mój developerski egzemplarz odroid U2. Wspaniały początek nowego roku :)
Ponieważ zamierzam używać go również jako HTPC z zasilanym z USB dyskiem 1TB, zamówiłem już wcześniej w polsce silniejszy 3A zasilacz (koreańczycy oferują standardowo 2A - a to prowadzi do niedoboru mocy w przypadku dużego obiążenia). Z duszą na ramieniu dokonałem pierwszego odpalenia... a tu nic, nie reaguje. Na szczęście po dociśnięciu wszystkich styków zasilacza wkońcu odpalił... uff :)

System (Android, ale można zaintalować również arm'ową wersję ubuntu) odpalił się dosłownie w kilka sekund. 4rdzeniowy procek samsunga w połączeniu z ultra szybką kartą eMMC robią swoje :)
Do szczęścia brakowało mi jeszcze aplikacji googla (głównie play store), których koreańczycy nie mogli dołączyć do dystrybucji ze względu na licencję. Na szczęście instalacja jest trywialna:

1. w pierwszym kroku należy się upewnić że SDK androida jest zainstalowane w systemie.
2. teraz pora ściągnąć sterownik odroida dla windows z forum społeczności hardkernela i można już podłączyć PC do portu micro USB maszynki
4. teraz z konsoli, gdzie rozpakoaliśmy archiwum należy wykonać polecenia:
adb remount
adb push system /system
adb reboot

Po zrestartowaniu odroid ma już dostęp do sklepu googla i możemy zacząć zabawę.... ,która niestety szybko się kończy... image androida aktualnie zawiera jakiś błąd i po kilkudziesięciu minutach maszyna się zawiesi, po kolejnym restarcie już nie wstaje. Po zflashowaniu karty "świerzym" obrazem,  system sie uruchamia ale ponownie się szybko zawiesi. Producent zna problem i aktualnie pracuje nad rozwiązaniem. Tymczasem więc "pobawię" się linuxem - który ma niestety jedną wadę - brak sprzętowej akceleracji dla procesora graficznego Mali (na Androidzie śmiga jak burza)... więc gdy np. chcemy obejrzeć film, całość obliczeń bierze na klatę 4 rdzeniowy proc... no ale przynajmniej jest stabliny :)


2012-12-12

REST in peace - projektowanie API webowego cz.1 base URL

Dobrze napisane API aplikacji webowej powinno być przyjazne dla developerów, którzy z niego będą korzystali. Osiągnięcie tego celu ułatwia stosowanie wzorca architektury REST (Representational State Transfer).

Jednym z najważniejszych elementów API jest jego adres URL. RESTowe podejście mówi: "Rzeczowniki są dobre, czasowniki są złe". Bazowy URL powinien być prosty i intuicyjny, dzięki temu użycie projektowanego API będzie łatwe. Powinny być tylko 2 bazowe URLe na zasób.

Koń jaki jest każdy widzi - a więc pora na przykład ;) 


/horses /horses/1234

Pierwszy url reprezentuje kolekcję, drugi: element kolekcji. Zamiast używać czasowników w adresie URL zastosujemy czasowniki HTTPowe : POST, GET, PUT, DELETE. Z ich pomocą API wykona podstawowe operacje CRUD (Create-Read-Update-Delete) właśnie w tej kolejności. Tak więc zamiast adresu /getAllHorses użyjemy: GET /horses, zamiast /updateHorse/1234 -> PUT /horses/1234 itd.

Poniższa tabela przedstawia podstawowe akcje na zasobie konika:
Zasób
POST
(dodaj)
GET
(czytaj)
PUT
(aktualizuj)
DELETE
(usuń)
/horses
Nowy koń
Lista koni
Bulk update koni
Usuń wszystkie konie
/horses/1234
Błąd
Pokaż siwka
Jeśli istnieje to zrób update siwka,
jeśli nie – Błąd
Usuń siwka
W efekcie programista nie będzie potrzebował dokumentacji aby zrozumieć jak działa API.