Export subversion verschillen tussen twee revisions voor upload

Door drm op zondag 31 mei 2009 15:36 - Reacties (6)
CategorieŽn: Development, Tips, tips, tips ..., Views: 3.428

Subversion (svn) is ťťn van de meest wijd verspreide versioning systemen voor source code, en terecht want het is erg flexibel en praktisch opgezet. Zonder dat je verplicht wordt vaste structuren aan te houden in je repositories, en door eenvoudig combinaties van repositories op te zetten m.b.v. svn:externals kan je eigenlijk heel veel kanten op als het gaat om package en library beheer binnen een in development of onderhoud zijnd project. Hier kan ik nog wel eens wat tips voor op een rijtje zetten als daar behoefte aan is.

Ik vroeg me al een hele tijd af of er voor Subversion ook iets beschikbaar was om een soort patch te maken tussen twee verschillende revisions die als een "overwrite" mogen fungeren, wat ongelooflijk handig zou zijn voor het updaten van een FTP-locatie, waar je geen beschikking hebt over svn metadata of zelfs een svn client.

Na afgelopen week toch maar weer eens gezocht te hebben, kwam ik een python scriptje tegen wat in deze behoefte voorziet, onder de naam svnchanged_export.py. Met dit script kun je in een shell op eenvoudige wijze de gewijzigde bestanden uit een repository vissen en die uploaden. Hoewel de deletes natuurlijk niet direct gedaan kunnen worden op basis van deze informatie, zou dit een aardig begin kunnen zijn van een mash-up met een python ftp-client die de wijzigingen direct doorsluist naar een FTP-locatie.

Je hebt op de bak waar je script gaat draaien een python 2.5 en pysvn installatie nodig, allebei ongetwijfeld beschikbaar in je favoriete packaging systeem. (via apt en FreeBSD ports in ieder geval geen probleem). Op windows heb ik het zelf niet uitgeprobeerd. Let er op dat je weet welke revision je online hebt staan (hou dit bijvoorbeeld bij in een hidden file in de root van je website, of met behulp van consequente tagging waarbij de laatste live revision altijd een tag heeft).

Een aanrader in ieder geval, voor iedereen die niet afhankelijk wil zijn van (vaak trage of zelfs buggy) file-date of filesize comparisons in FTP clients :)

PS: Python is sowieso altijd erg veelbelovend als het op handige scripts gaat: de source code is over het algemeen goed te lezen i.t.t. bijvoorbeeld Perl scripts en er is een gigantische codebase beschikbaar voor allerlei toepassingen die ook als losse (zowel source als binary) packages gedistribueerd worden.

MySQL: ENUM's and booleans en

By drm on Saturday 23 May 2009 12:23 - Comments (6)
Category: Development, Views: 10.305

This is one i ran into a few years back, when an entire project was built on a database using ENUM's as bools. Unfortunately, it was for me to figure out what caused some weird bugs involved. This is a run-down of what happens when you actually use ENUM's as (evaluating) bools:

mysql> select '0'=0, '1'=1, 0='0', 1='1';
+-------+-------+-------+-------+
| '0'=0 | '1'=1 | 0='0' | 1='1' |
+-------+-------+-------+-------+
|     1 |     1 |     1 |     1 |
+-------+-------+-------+-------+
1 row in set (0.00 sec)


ENUM's store strings, right? Let's use them for booleans.

CREATE TEMPORARY TABLE t(b ENUM('0','1') NOT NULL);


Shouldn't be a problem.

mysql> insert into t(b) values(1), (0);
Query OK, 2 rows affected, 1 warning (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 1

mysql> select * from t;
+---+
| b |
+---+
| 0 |
|   |
+---+
2 rows in set (0.00 sec)

Hmm....
mysql> select b,b=1,b=0 from t;
+---+-----+-----+
| b | b=1 | b=0 |
+---+-----+-----+
| 0 |   1 |   0 |
|   |   0 |   1 |
+---+-----+-----+
2 rows in set (0.00 sec)
What ... :? How ... :?

Well....

mysql> truncate t;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values('1'),('0');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from t;
+---+
| b |
+---+
| 1 |
| 0 |
+---+
2 rows in set (0.00 sec)
Phew :o :)

mysql> select b,b=1,b=0 from t;
+---+-----+-----+
| b | b=1 | b=0 |
+---+-----+-----+
| 1 |   0 |   0 |
| 0 |   1 |   0 |
+---+-----+-----+
2 rows in set (0.00 sec)
Excuse... what.... what?! :? 8)7

Let's see....

mysql> select b,IF(b,1,0),IF(b=1,1,0) from t;
+---+-----------+-------------+
| b | IF(b,1,0) | IF(b=1,1,0) |
+---+-----------+-------------+
| 1 |         1 |           0 |
| 0 |         1 |           1 |
+---+-----------+-------------+
2 rows in set (0.00 sec)

:o :D :o

mysql> SELECT NOT(b) FROM t;
+--------+
| NOT(b) |
+--------+
|      0 |
|      0 |
+--------+
2 rows in set (0.00 sec)


...

So, to sum up, please use INT's for bools, not enums. And if you do care to use enum's for whatever reason, never use numeric values to imply int-to-bool casting in expressions.

Should I continue, or should i break? en

By drm on Monday 18 May 2009 01:18 - Comments (8)
Category: Development, Views: 5.894

This is one of the oldies out of my "PHP is weird"-box. I dug it up from an old post of mine in the GoT forums.
---

What would you expect this code to do?


code:
1
2
3
4
5
6
7
for( $i = 1; $i <= 5; $i ++) {
   switch($i) {
      case 2: case 4:
         continue;
   }
   echo $i, "\n";
}

It should read
1
3
5
right?

No it doesn't. Read this:
continue
continue is used within looping structures to skip the rest of the current loop iteration and continue execution at the beginning of the next iteration.

Note: Note that in PHP the switch statement is considered a looping structure for the purposes of continue.

(...)

switch
Note: Note that unlike some other languages, the continue statement applies to switch and acts similar to break. If you have a switch inside a loop and wish to continue to the next iteration of the outer loop, use continue 2.
:? :? :?

Zend Framework: Decorator Pattern

Door drm op woensdag 13 mei 2009 19:32 - Reacties (7)
Categorie: Development, Views: 4.297

De theorie achter het Decorator Pattern is eenvoudig. Je hebt een object, je wilt bij een bepaalde functie van dat object (meestal het renderen ervan) wat eigen functionaliteit hangen, zonder dat je de het object van gedrag wilt laten veranderen. Denk hierbij aan het volgende:


PHP:
1
2
$widget = new Widget();
$widget->render();


Even aangenomen dat de Widget class een
code:
1
<object ... />

tag rendert, en je zou willen dat de widget wordt voorzien van een titel en in een div'je wordt gewrapt, dan kun je er een decorator aanhangen die daar zorg voor draagt:

PHP:
1
2
3
4
5
6
7
8
9
$widget = new Widget();
// methode 1: Widget kan "gedecorate" worden, en zal de decorator
// aanroepen als hij gerenderd wordt.
$widget->addDecorator('MyWidgetDecorator');
// methode 2: Widget weet niks van MyWidgetDecorator, MyWidgetDecorator
// draagt zorg voor rendering en zal de render() method van de widget
// waarschijnlijk aanroepen
$widget = new MyWidgetDecorator($widget);
$widget->render();



Zend gebruikt dit design pattern in het Zend Framework in de Zend_Form package om formulieren van HTML te voorzien. ZF maakt gebruik van bovenstaande eerste methode. Dit betekent dat het element dat "gedecoreerd" wordt altijd op de hoogte moet zijn van het feit dŠt hij gedecoreerd wordt, terwijl dat binnen de context vaak helemaal niet interessant is.

Nu vind ik het hele principe dat je in de PHP code bepaalt wat de HTML-output wordt al eng, aangezien ikzelf (en pure front-enders met mij) dat gewoon in de templates willen bepalen. Ik ben ook een voorstander van Smarty, wat het nog eens extra bemoeilijkt, maar ook als PHP zelf als template renderer gebruikt, is het een vervelend principe.

De basis is namelijk dat je gegevens in je template hoort te hebben die je wilt gaan presenteren. Gegevens zijn bijvoorbeeld de foutmeldingen, of eventueel de controls (zoals een text-input of een button, hoewel je er over kunt discussieren of dat niet ook gewoon door je view bepaald moet worden). ZF's method van decoraten levert hier een probleem op. Als je namelijk een formulier wilt gaan renderen, dan kun je er niet op een intuitieve, eenduidige manier achter komen wat precies de bron van de gegevens is, en hoe je daar eventueel invloed op uit kunt oefenen.

Daarom vind ik dat Zend_Form een grote refactoring nodig heeft, om gebruik te gaan maken van methode 2. Het "decoraten" van het object gaat dan niet alleen meer over renderen, maar ook over toevoegen van properties, methodes en implementatie van interfaces die het object nog niet ondersteunt. Neem daarvoor een abstract base class, die simpelweg alle bestaande functionaliteit doorsluist naar het gewrapte object:


PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
abstract class Decorator_Abstract {
    protected $_innerObject;

    function __construct( $object ) {
        $this->_innerObject = $object;
    }

    function __get ( $name ) {
        return $this->_innerObject->{$name};
    }
    
    
    function __call ( $method, $args ) {
        $refl = new ReflectionMethod($this->_innerObject, $method);
        return $refl->invokeArgs($this->_innerObject, $args);
    }
    
    
    function __toString () {
        return $this->_innerObject->__toString();
    }
    
    
    function __destruct () {
        unset($this->_innerObject);
    }
    
    
    function getInnerObject( $deep = true ) {
        $ptr = $this;
        if($deep) {
            while($ptr instanceof self) {
                $ptr = $ptr->getInnerObject(false);
            }
        } else {
            $ptr = $this->_innerObject;
        }
        return $ptr;
    }
}



Je kunt mbv deze class heel eenvoudig objecten wrappen en aanvullen met je eigen methods, zonder dat je het oorspronkelijke object kwijtraakt.


PHP:
1
2
3
4
5
6
7
8
9
10
11
12
class MyWidgetDecorator extends Decorator_Abstract {
    protected $_title = null;
    
    function __construct($widget, $title) {
        parent::__construct($widget);
        $this->_title = $title;
    }

    function __toString() {
        return sprintf('<div class="widget-container"><h1>%s</h1>%s</div>', $this->_title, (string) $this->_innerObject);
    }
}



Waarom is dit dan beter?
Je weet als decorator altijd wat er verder nog gerenderd gaat worden, en kan daar alles van beÔnvloeden wat je wilt. Stel je maakt een eenvoudige decorator waarmee je content escapet:

PHP:
1
2
3
4
5
6
7
8
9
class MyOutputEscapingDecorator extends Decorator_Abstract {
   function __toString() {
       return htmlspecialchars((string) $this->_innerObject);
   }

   function getRawValue() {
       return (string) $this->_innerObject;
   }
}


Je kunt daar elk willekeurig object instoppen die zichzelf kan renderen, ook al is dat ook weer een decorator:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyNewline2BrDecorator extends Decorator_Abstract {
   function __toString() {
       return nl2br((string) $this->_innerObject);
   }
}

class Something {
   function __toString () {
        return "This contains some <b>html</b>\n\nAnd some newlines";
   }
   function isSpecialCase () {
       return true;
   }
}

$something = new Something();
// do whatever

$something = new MyNewline2BrDecorator(new MyOutputEscapingDecorator($something)));

if($something->isSpecialCase()) {
    // do something extraordinary
} else {
    echo $something;
}



Maar je hebt geen enkel informatieverlies, in tegenstelling tot de rendering van Zend_Form, waarbij ik bij het renderen van de label helemaal niets van andere decorators, en of die al gerenderd zijn, en wat ze dan gerenderd hebben. Stel nou dat ik ergens in een template 1 lullige uitzondering wil maken? Dan ben ik de beer en moet ik terug in de back-end code om al die decorators uit te schakelen voor dat ene gevalletje, terwijl het feitelijk gewoon een presentatie-issue is. Met methode no2 kun je daarentegen gewoon zeggen:


PHP:
1
2
3
4
5
if($field->name == 'specialField') {
    echo $field->getInnerObject(); // only print field, no decorators
} else {
    echo $field;
}



Dan is het ineens wťl een prettige toevoeging van de form engine dat er standaard renderers zijn, zonder dat er allerlei informatie verdwijnt en het belachelijk omslachtig wordt om uitzonderingen te bouwen.

Open source en gratis software

Door drm op zaterdag 2 mei 2009 14:26 - Reacties (36)
Categorie: Tips, tips, tips ..., Views: 5.802

Ik vind het altijd lastig om voor programma's waar ik eigenlijk niet voor wil betalen, goede of vergelijkbare gratis cq open source alternatieven te vinden. We kunnen discussieren over de vraag of je dat wel altijd moet willen (en waarom je soms niet gewoon die paar tientjes voor een stukje software neer wil leggen), maar op de een of andere manier lijkt het alsof de Hollandse zuunigheid ook hier in doordringt - waarom betalen voor iets als je een gratis altnernatief kunt vinden? Daar laat ik het voor mezelf dan maar bij.

Zo zijn er natuurlijk OpenOffice.org voor je tekstverwerking en spreadsheets, Thunderbird voor de mail, WinSCP voor je SCP en (S)FTP praktijkjes, Eclipse voor development, notepad++ voor je simpele kladderwerk, allemaal wel bekend, vermoed ik.

Laatst heb ik eindelijk een alternief voor (hoewel uiteraard onovertroffen) WinRAR gevonden, met de naam 7-zip. Misschien bij jullie wel bekend, maar ik vond het de moeite waard om even onder de aandacht te brengen.

Heb je ook een tip? Laat hem hier achter :-)