Постове 1 - 5 от 17 с таг code

Apr 04

Понеделник 29.03.2010

Ruby Quick Tip: Regular Expressions in Case Statements – Как може да се използват регулярни изрази в case конструкциите в Ruby.

Вторник 30.03.2010

Odd Ruby Methods – В Ruby могат да се предефинират операторите, в поста има няколко интересни примера и размисли.

Fred Wilson’s 10 Golden Principles of Successful Web Apps – Доста внимателно го слушах и съм напълно съгласен с човека. Много добре обяснява точка по точка, какво е задължително да се гледа когато се прави уеб приложение.

Сряда 31.03.2010

Hopping in the cloud – Поредната страхотна статия от Giant Robots Smashing Into Other Giant Robots. В статията се описва как са прехвърлили своето Hoptoad приложение в “облакът” на Engine Yard. Там се описват част от промените по цялостната архитектура на приложението, които са направили.

Четвъртък 01.04.2010

Много внимавам на първи Април, всяка година е пълно с “лъжливи мини”. Някои са доста доста лесни за разгадаване като – MooJo и Objective-Sprout. Докато примерно Github: Announcing SVN Support, два дни си мислех че е шега. А пък  “svn checkout http://svn.github.com/schacon/simplegit.git” си работи :)

Sproutcore and NodeJS are stars and comets – Две от любимите ми теми – NodeJs и SprouteCore. Какво просто да кажа :)

JavaScript Style – Chris Wanstrath доста нагледно е показал какъв е JavaScript стила в света.

Петък 02.04.2010

Microsoft Gets More Involved with jQuery – От една страна, това е добре за тези които работят с Microsoft технологии, че поне на готово ще имат една добра JavaScript библиотека. Но от друга от Microsoft, като се замисля, нищо хубаво не съм видял. И много се надявам да не я развалят много или да се опитат да я наложат на всички насила.

Federico Cargnelutti – Most Visited Posts of 2009 – Много харесвам този блог, според мен е един от най-стойностните PHP блогове. Това е малко закъснял списък с най-посещаваните постове за миналата година.

Mar 28

Много бързо мина тази седмица :)

Понеделник 22.03.2010

Not Supported – Този пост на Jonathan Snook идва като контра на CSS vendor prefixes considered harmful и общо взето защитава решението на браузърите да използват свой стилове като -moz-border-radius вместо директно border-radius.
Implementing Dynamic Finders and Parsing Method Expressions – Ако и вие като мен сте си писали ActiveRecord имплементация, ще оцените подобаващо статията.

Вторник 23.03.2010

All aboard! An introduction to Rails 3 - Да си призная доста рядко до изчетатам цяла статия от IBM developerWorks, но този път си заслужава.
Building Framework Independent Code Libraries – от Grant Skinner, винаги идват полезни съвети. Като цяло статията му на пръв поглед е насочена към ActionScript, но може да се отнесе до почти всяка програмна среда.
Setting up a Rails Server and Deploying with Capistrano on Fedora from Scratch – По-рано през седмицата, коментирах вече един урок от Nettuts+. Този път вече съм доста по-доволен от тях.

Сряда 24.03.2010

Objective-C for Ruby developers, un not-so-petit interlude (1/2) – Като Mac потребител, винаги съм искал да се пробвам да напиша някое приложение за MacOS X, Като сложим и това, че поста е от блога на Phusion ( създателите на Phusion Passenger т.е. mod_rails и Ruby Enterprise Edition ), този пост минава веднага в графата за прочитане :)

Четвъртък 25.03.2010

Some Users Want Terrible User Interfaces – Има ли хора, който харесват ужасните потребителски интерфейси ? И още как !  Само вижте колко хора ползват Windows.

Петък 26.03.2010

HTML5 Forms Are Coming – Седмица приключи както и започна с Jonathan Snook :)

Mar 24

Днес прегледах поста – Image Resizing Made Easy with PHP от Nettuts+. Доста полезен пост, за начинаещи. Но както винаги имах няколко забележки относно от кода. Мислех да ги запиша като коментар, но той се оказа доста дълъг. Затова реших да го напиша като пост.

Като за начало ще е добре да видите началния код от тук ( променената от мен версия е тук). Така промените метод по метод.

openImage

private function openImage($file)
{
    // *** Get extension
    $extension = strtolower(strrchr($file, '.'));

    switch($extension)
    {
        case '.jpg':
        case '.jpeg':
            $img = @imagecreatefromjpeg($file);
            break;
        case '.gif':
            $img = @imagecreatefromgif($file);
            break;
        case '.png':
            $img = @imagecreatefrompng($file);
            break;
        default:
            $img = false;
            break;
    }
    return $img;
}
// става:
private function openImage($file){
    if (!is_file($file)){
        throw new Exception("File {$file} doesn't exists");
    }

    switch(pathinfo($file, PATHINFO_EXTENSION)){
        case 'jpg':
        case 'jpeg': return imagecreatefromjpeg($file);
        case 'gif':  return imagecreatefromgif($file);
        case 'png':  return imagecreatefrompng($file);
    }

    throw new Exception("Invalid image extension for {$file}. Acceptable image types are jpg,jpeg,gif,png");
}

Като за начало тук PHP има вградена функция pathinfo, която извикана с PATHINFO_EXTENSION константа като втори аргумент връща какво е разширението на файла.

Също така няма смисъл от дефинирането на променливите $extension  и $img защото реално се използват един път. $extension за switch-a, а пък $img само да се  return-е.

Също така @ е нещо което НЕ ТРЯБВА да се използва!  Освен че е страшно бавно (ако някой се интересува ще обясня вътрешно какви глупости прави и защо е толкова бавна операция).  От друга страна не е много редно да се крият грешките в програмите.

За това тук съм добавил два Exeption-а. По-принцип не обичам да ги ползвам, но в този случаи е наложително.  Трябва да се провери, първо дали файла който искаме да променяме съществува и второ дали от позволените типове.

resizeImage

public function resizeImage($newWidth, $newHeight, $option="auto")
{
    // *** Get optimal width and height - based on $option
    $optionArray = $this->getDimensions($newWidth, $newHeight, $option);

    $optimalWidth  = $optionArray['optimalWidth'];
    $optimalHeight = $optionArray['optimalHeight'];

    // *** Resample - create image canvas of x, y size
    $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
    imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);

    // *** if option is 'crop', then crop too
    if ($option == 'crop') {
        $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);
    }
}
// става:
public function resizeImage($newWidth, $newHeight, $option='auto'){
    list($width, $height) = $this->getDimensions($newWidth, $newHeight, $option);

    $this->imageResized = imagecreatetruecolor($width, $height);
    imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $width, $height, $this->width, $this->height);

    if ($option == 'crop'){
        $this->crop($width, $height, $newWidth, $newHeight);
    }
}

List е доста подценявана PHP функционалност. Но за да може да се работи с нея промених и getDimensions да връща просто масив с два елемента на позиция 0 и 1. ( По-долу ще обясня за нея)

После промених имената на променливите $optimalWidth и $optimalHeight. От една страна са доста дълги и докато се пишат трябва да се натиска и shift заради W и H. От друга те се състоят от две думи, като едната е “optimal” която не ни трябва, даже и грешна. Защото се превеждат “оптимална ширина” и “оптимална височина” докато реално те са просто “ширина” и “височина” (т.е. $width и $height).

Описвам толкова дълго за тези променливи защото именуването на променливите е голяма част от красивия и разбираемия код.

getDimensions

private function getDimensions($newWidth, $newHeight, $option)
{

   switch ($option)
    {
        case 'exact':
            $optimalWidth = $newWidth;
            $optimalHeight= $newHeight;
            break;
        case 'portrait':
            $optimalWidth = $this->getSizeByFixedHeight($newHeight);
            $optimalHeight= $newHeight;
            break;
        case 'landscape':
            $optimalWidth = $newWidth;
            $optimalHeight= $this->getSizeByFixedWidth($newWidth);
            break;
        case 'auto':
            $optionArray = $this->getSizeByAuto($newWidth, $newHeight);
            $optimalWidth = $optionArray['optimalWidth'];
            $optimalHeight = $optionArray['optimalHeight'];
            break;
        case 'crop':
            $optionArray = $this->getOptimalCrop($newWidth, $newHeight);
            $optimalWidth = $optionArray['optimalWidth'];
            $optimalHeight = $optionArray['optimalHeight'];
            break;
    }
    return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
}
// става:
private function getDimensions($width, $height, $option){
   switch ($option){
        case 'portrait':   return array($this->getSizeByFixedHeight($height),  $height);
        case 'landscape':  return array($width, $this->getSizeByFixedWidth($width));
        case 'auto':       return $this->getSizeByAuto($width, $height);
        case 'crop':       return $this->getOptimalCrop($width, $height);
        case 'exact':
        default:           return array($width, $height);
    }
}

Първо имената на променливите $newWidth и $newHeight на  $width и $height. Причините ги обясних по-горе за $optimalWidth и $optimalHeight.

Промених и формата на масива който се връща от getDimensions от асоциативен масив с ключове optimalWidth и optimalHeight на прост масив с индекси 0 и 1. Кода става доста по четим и по-горе в resizeImage метода може да се ползва List.

Премахват се и излишните променливи $optimalWidth и $optimalHeight, защото реално ни трябва просто масива с двата елемента. И от съкращаването на формата му се вижда че тези две променливи са още по-излишни.  Когато се намерят техните стойности веднага може да се върнат в масив, вместо да се чака да се стигне най-долу.

И накрая премествам ‘exact’ да бъде и default случая в switch.

getSizeByFixedHeight /  getSizeByFixedWidth

private function getSizeByFixedHeight($newHeight)
{
    $ratio = $this->width / $this->height;
    $newWidth = $newHeight * $ratio;
    return $newWidth;
}

private function getSizeByFixedWidth($newWidth)
{
    $ratio = $this->height / $this->width;
    $newHeight = $newWidth * $ratio;
    return $newHeight;
}
// става:
private function getSizeByFixedHeight($height){
    return ($this->width / $this->height) * $height;
}

private function getSizeByFixedWidth($width){
    return ($this->height / $this->width) * $width;
}

Много мразя дефинирането на излишни променливи. Защото докато се чете кода трябва да се помни какво има във съответната променлива.

Когато се погледне примерно getSizeByFixedHeight в оригиналната версия, кода се чете:

- Дефинирам $ratio който е ширината на снимката разделена на височината и
- Дефинирам нова височина, която е новата ширина умножена по $ratio ( мисля и се сещам какво има в $ratio )
- Връщам новата височина

Докато моята версия се чете просто:

- Разделям оригналата ширината на снимката на височината, полученото го умножавам по $width и връщам стойността.

getSizeByAuto

private function getSizeByAuto($newWidth, $newHeight)
{
    if ($this->height < $this->width)
    // *** Image to be resized is wider (landscape)
    {
        $optimalWidth = $newWidth;
        $optimalHeight= $this->getSizeByFixedWidth($newWidth);
    }
    elseif ($this->height > $this->width)
    // *** Image to be resized is taller (portrait)
    {
        $optimalWidth = $this->getSizeByFixedHeight($newHeight);
        $optimalHeight= $newHeight;
    }
    else
    // *** Image to be resizerd is a square
    {
        if ($newHeight < $newWidth) {
            $optimalWidth = $newWidth;
            $optimalHeight= $this->getSizeByFixedWidth($newWidth);
        } else if ($newHeight > $newWidth) {
            $optimalWidth = $this->getSizeByFixedHeight($newHeight);
            $optimalHeight= $newHeight;
        } else {
            // *** Sqaure being resized to a square
            $optimalWidth = $newWidth;
            $optimalHeight= $newHeight;
        }
    }

    return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
}
// става:
private function getSizeByAuto($width, $height){
    if ($this->height < $this->width){
        return array($width, $this->getSizeByFixedWidth($width));
    }

    if ($this->height > $this->width){
        return array($this->getSizeByFixedHeight($height), $height);
    }

    if ($height < $width){
        return array($width, $this->getSizeByFixedWidth($width));
    }

    if ($height > $width){
        return array($this->getSizeByFixedHeight($height), $height);
    }

    return array($width, $height);
}

Колкото по надълбоко влизат вложените структури, толкова по-грозен е кода. Реално тук всяка ситуация може да се изнесе в отделен if и просто ако този if се изпълни се връща резултат веднага и така докато се стигне до края.

getOptimalCrop

private function getOptimalCrop($newWidth, $newHeight)
{

    $heightRatio = $this->height / $newHeight;
    $widthRatio  = $this->width /  $newWidth;

    if ($heightRatio < $widthRatio) {
        $optimalRatio = $heightRatio;
    } else {
        $optimalRatio = $widthRatio;
    }

    $optimalHeight = $this->height / $optimalRatio;
    $optimalWidth  = $this->width  / $optimalRatio;

    return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
}
// става:
private function getOptimalCrop($width, $height){
    $ratio = min($this->height / $height, $this->width /  $width);
    return array(
        $this->width  / $ratio,
        $this->height / $ratio
    );
}

Мin е  друга малка PHP функция с чиято помощ този код се по изчиства. Защото реално на нас ни трябва минималната стойност от двете зависимости.

Тук пиша на два реда връщането на масива, отново заради четимостта.

saveImage и crop

private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
{
    // *** Find center - this will be used for the crop
    $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 );
    $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 );

    $crop = $this->imageResized;
    //imagedestroy($this->imageResized);

    // *** Now crop from center to exact requested size
    $this->imageResized = imagecreatetruecolor($newWidth , $newHeight);
    imagecopyresampled($this->imageResized, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight);
}
public function saveImage($savePath, $imageQuality="100")
{
    // *** Get extension
             $extension = strrchr($savePath, '.');
                $extension = strtolower($extension);

    switch($extension)
    {
        case '.jpg':
        case '.jpeg':
            if (imagetypes() & IMG_JPG) {
                imagejpeg($this->imageResized, $savePath, $imageQuality);
            }
            break;

        case '.gif':
            if (imagetypes() & IMG_GIF) {
                imagegif($this->imageResized, $savePath);
            }
            break;

        case '.png':
            // *** Scale quality from 0-100 to 0-9
            $scaleQuality = round(($imageQuality/100) * 9);

            // *** Invert quality setting as 0 is best, not 9
            $invertScaleQuality = 9 - $scaleQuality;

            if (imagetypes() & IMG_PNG) {
                 imagepng($this->imageResized, $savePath, $invertScaleQuality);
            }
            break;

        // ... etc

        default:
            // *** No extension - No save.
            break;
    }

    imagedestroy($this->imageResized);
}
// става:
private function crop($optimalWidth, $optimalHeight, $width, $height){
    $x = ( $optimalWidth  / 2) - ( $width  /2 );
    $y = ( $optimalHeight / 2) - ( $height /2 );

    $crop = $this->imageResized;

    $this->imageResized = imagecreatetruecolor($width , $height);
    imagecopyresampled($this->imageResized, $crop, 0, 0, $x, $y, $width, $height , $width, $height);
}

public function saveImage($savePath, $imageQuality="100"){
      switch(pathinfo($savePath, PATHINFO_EXTENSION)){
        case 'jpg':
        case 'jpeg':
            if (imagetypes() & IMG_JPG){
                imagejpeg($this->imageResized, $savePath, $imageQuality);
            }
            break;

        case 'gif':
            if (imagetypes() & IMG_GIF){
                imagegif($this->imageResized, $savePath);
            }
            break;

        case 'png':
            if (imagetypes() & IMG_PNG){
                // Scale quality from 0-100 to 0-9
                // Invert quality setting as 0 is best, not 9
                $invertScaleQuality = 9 - round(($imageQuality/100) * 9);
                imagepng($this->imageResized, $savePath, $invertScaleQuality);
            }
            break;
    }

    imagedestroy($this->imageResized);
}

Промените по saveImage и crop са просто стилистични, така че няма да се спирам много подробно на тях.

Финални думи

Като започнах да чистя кода си обещах, че няма да променям имената на методите. Но ако питате мен от публичните методи – resizeImage и saveImage бих махнал “Image” и да станат само resize и save. Защото класа най-често би се използвал с променливи кръстени $resizer, $resize, $image и подобни и затова rеsize и save биха достатъчно ясно.

Като цяло урока от Nettuts+ е много добър. Въпроса че според мен в уроците за начинаещи, които главно ползват Nettuts+, трябва да има максимално добър код. Без излишен “шум”.

Надявам се този малък анализ да е бил полезен на някого. Приемам всякакви критики и съвети, под формата на коментари :)

Mar 14

Наскоро се наложи да поработя по един доста стар PHP проект, по който не бях работил от години. И там видях нещо, което ведна опреличих като code smell (Всъщност видях много такива неща, но само на това ще обърна внимание).

$cart = CartManager::getCurrentCart();
$cart->setClientInfo('atype',    $user['account_type']);
$cart->setClientInfo('fname',    $user['fname']);
$cart->setClientInfo('lname',    $user['lname']);
$cart->setClientInfo('street',   $user['street']);
$cart->setClientInfo('postnum',  $user['postcode']);
$cart->setClientInfo('epost',    $user['mail']);
$cart->setClientInfo('city',     $user['city']);
$cart->setClientInfo('company',  $user['company']);
$cart->setClientInfo('orgnum',   $user['orgnum']);
$cart->setClientInfo('phone',    $user['phone']);
$cart->setClientInfo('country',  $user['country']);

Което ако идвате от  Java света може и да ви изглежда нормално, но мен ме дразни.

На първо време направих метода setClientInfo да връща $this зада може да използвам прост method chaining. И кода стана така:

CartManager::getCurrentCart()
    ->setClientInfo('atype',    $user['account_type'])
    ->setClientInfo('fname',    $user['fname'])
    ->setClientInfo('lname',    $user['lname'])
    ->setClientInfo('street',   $user['street'])
    ->setClientInfo('postnum',  $user['postcode'])
    ->setClientInfo('epost',    $user['mail'])
    ->setClientInfo('city',     $user['city'])
    ->setClientInfo('company',  $user['company'])
    ->setClientInfo('orgnum',   $user['orgnum'])
    ->setClientInfo('phone',    $user['phone'])
    ->setClientInfo('country',  $user['country']);

Малко по-добре, но пак ми изглеждаше не естествено. Затова просто добавих възможността да може да се предава масив като аргумент. И така стана доста добре:

CartManager::getCurrentCart()->setClientInfo(array(
    'atype',    => $user['account_type'],
    'fname',    => $user['fname'],
    'lname',    => $user['lname'],
    'street',   => $user['street'],
    'postnum',  => $user['postcode'],
    'epost',    => $user['mail'],
    'city',     => $user['city'],
    'company',  => $user['company'],
    'orgnum',   => $user['orgnum'],
    'phone',    => $user['phone'],
    'country',  => $user['country']
));

За нещастие в този проект, нямаше как да направя кода стане още по-чист, без да се налага пренаписване на голяма част от проекта:

CartManager::getCurrentCart()->setClient($user);
Feb 28

Миналата седмица не можах да пиша, защото беше доста странна седмица. За сметка на това тази беше много спокойна и приятна седмица.

Понеделник 22.02.2010

“Do” it fast! - Do е библиотека за Node.js. Все повече ми харесва как се преплитат нещата около Node.js, дано скоро да ми се наложи да го ползвам.

Вторник 23.02.2010

JavaScript Override Patterns / JavaScript Overload PatternsAndrea Giammarchi, е описал два javascript pattern-a ( на български преведено pattern звучи странно). Тази седмица имаше доста подобни постове за javascript.

The Skinny on Scopes (Formerly named_scope) - ЕdgeRails, вече започва да действа.

How to Apply to a Job – ако някога си търсите работа като Ruby програмист, така написано вашето мотивационо писмо със сигурност ще изпъкне пред другите. Обаче дали в положителна страна много зависи :)

Сряда 24.02.2010

The Database Tea Party: The NoSQL Movement – Някой беше казал, че нашите деца ще гледат на SQL както ние гледаме на COBAL. За което не съм много сигурен. Тъй като, ако по-добрите технологии изместваха старите толкова бързо, отдавна IE6/Windows и други подобни нямаше да ги има :)

Internet Explorer Array.sort Unreliable – като стана дума за лоши технологии, IE има проблем с Array.sort.

Четвъртък 25.02.2010

The Geek Talk : Scott Chacon – в четвъртък попаднах на това интервю с Scott Chacon от github

Петък 26.02.2010

Evented programming patterns: Round-up: Всъщност това е серия от пет много добри постове.