Mostrando entradas con la etiqueta programació. Mostrar todas las entradas
Mostrando entradas con la etiqueta programació. Mostrar todas las entradas

20100124

How to get a contrasting font color for a given background ?

This is a sample code to give a font color with a good contrast for a given background color.

That php code snipped could be useful in case you give your users the option to set a custom background but not the font color (to avoid too many options) and you want that your text are always visible.

It simply returns a font color white or black depending on the background, but you can modify the function get_contrast if you want something more sophisticated.

The main function is:


function get_font_color($color)
{
$ar=html2rgb($color);
return get_contrast($ar[0], $ar[1], $ar[2]);
}


Supporting functions:

function get_contrast ($r, $g, $b)
{// returns white or black depending on the background
// in red green and blue
$a = 1 - ( 0.299*$r + 0.587*$g + 0.114*$b)/255;
if ($a<0.5)
{
return '#000';
}
else
{
return '#fff';
}
}

function html2rgb($color)
{// gets an array of R,G,B from an hexadecimal
// color in html format
if ($color[0] == '#') $color = substr($color, 1);

if (strlen($color) == 6)
{
list($r, $g, $b) = array($color[0].$color[1],
$color[2].$color[3],
$color[4].$color[5]);
}
elseif (strlen($color) == 3)
{
list($r, $g, $b) = array($color[0].$color[0],
$color[1].$color[1],
$color[2].$color[2]);
}
else
{
return false;
}
$r = hexdec($r); $g = hexdec($g); $b = hexdec($b);

return array($r, $g, $b);
}


Unit testing:

$i=0;
while ($i<1600000)
{
$rgb='#'.strtoupper(str_pad(dechex($i), 6, '0', STR_PAD_LEFT));
echo '<div style="background-color:'.$rgb.'; color:'.get_font_color($rgb).'">'.$rgb.'</div>';
$i++;
}


Thanks to this web entries:
Determine font color based on background color
Convert RGB from an HTML Hex Color

20091210

Tutorial de memcache

Memcache es una extensió de PHP que interactua amb memcached, que a la seva vegada es un daemon de cache en memoria, molt simple, que enmagatzema parells key=>value on value pot ser qualsevol cosa serialitzable amb PHP.

Per instal.lar amb Ubuntu farem el següent:
apt-get install memcached
apt-get install php5-memcache

Reiniciar apache:
/etc/init.d/apache2 restart

Es pot fer un php amb un phpinfo(); per veure si hi ha un apartat memcache

Per veure si esta funcionant el memcached (el daemon):
netstat -tap | grep memcached

Per modificar els parametres de memoria etc. editar /etc/memcached.conf

per exemple, per configurar 300Mbs de memoria disponible per variables a memcached modificarem el flag -m del fitxer de configuració:
-m 300

Reiniciar memcached
/etc/init.d/memcached restart

Per consultar les estadistiques en temps real del memcached:

telnet localhost 11211
>stats

Exemple de output:


Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
stats
STAT pid 4666
STAT uptime 153661
STAT time 1260523656
STAT version 1.2.2
STAT pointer_size 64
STAT rusage_user 4.850000
STAT rusage_system 15.100000
STAT curr_items 16001
STAT total_items 57971
STAT bytes 282532500
STAT curr_connections 1
STAT total_connections 120074
STAT connection_structures 32
STAT cmd_get 120016
STAT cmd_set 57971
STAT get_hits 66178
STAT get_misses 53838
STAT evictions 0
STAT bytes_read 1108602586
STAT bytes_written 1317300490
STAT limit_maxbytes 314572800
STAT threads 1
END


Ara només cal modificar el nostre codi per enmagatzemar rows de la base de dades, durant un cert temps, en aquests exemples, creem una clau $action.'/'.$param1.'/'.$param2 o sigui per exemple 'category/23/1' i el valor serà el resultat de la base de dades (una array de rows).

La instrucció set enmagatzema a memcached:

$mc->set($action.'/'.$param1.'/'.$param2, $rows, 1, 3600);

El primer parametre es la clau, el segon el valor, el tercer si volem comprimir el contingut o no i el quart el temps en segons que tindrem el valor al memcache (1 hora)

La següent vegada que haguem de fer la consulta farem el següent:

$rows=$mc->get($action.'/'.$param1.'/'.$param2);

Si ens retorna null, es que no hi es, pero durant una hora, no ens caldrà repetir la consulta.

Exemples de codi:


function get_last($num=16, $offset=1, $flag = 1)
{
$sql = "SELECT v.*
, p.title portal_title
, p.url portal_url
, p.id portal_id
FROM ep_videos v, ep_portals p
WHERE v.portal_id = p.id
AND v.status = 'O'
ORDER BY v.added DESC
LIMIT ".(($offset-1)*$num).", ".$num;

$action='home';
$param1='1';
$param2=$offset;
$mc=new Memcache;
$cr=$mc->connect('localhost', 11211);
if ($cr)
{// tenim memcached
$rows=$mc->get($action.'/'.$param1.'/'.$param2);
if ($rows==null)
{// no el tenim a cache
$rows=parent::get_data($sql);
$mc->set($action.'/'.$param1.'/'.$param2, $rows, 1, 3600);
}
}
else
{// no tenim memcached
$rows=parent::get_data($sql);
}

return $rows;
}

function get_last_in_category($id, $num=16, $offset=1)
{
$sql = "SELECT v.*
, p.title portal_title
, p.url portal_url
, p.id portal_id
FROM ep_portals p
, ep_videos v
, ep_categories_videos cv
WHERE cv.video_id = v.id
AND cv.category_id = ".$id."
AND v.status='O'
AND v.portal_id = p.id
ORDER BY v.added DESC
LIMIT ".(($offset-1)*$num).", ".$num;

$action='category';
$param1=$id;
$param2=$offset;
$mc=new Memcache;
$cr=$mc->connect('localhost', 11211);
if ($cr)
{// tenim memcached
$rows=$mc->get($action.'/'.$param1.'/'.$param2);
if ($rows==null)
{// no el tenim a cache
$rows=parent::get_data($sql);
$mc->set($action.'/'.$param1.'/'.$param2, $rows, 1, 3600);
}
}
else
{// no tenim memcached
$rows=parent::get_data($sql);
}

return $rows;

//return parent::get_data($sql);
}

function get_last_in_tag($id, $num=16, $offset=1)
{
$sql = "SELECT v.*
, p.title portal_title
, p.url portal_url
, p.id portal_id
FROM ep_portals p
, ep_videos v
, ep_tags_videos cv
WHERE cv.video_id = v.id
AND cv.tag_id = ".$id."
AND v.status='O'
AND v.portal_id=p.id
ORDER BY v.added DESC
LIMIT ".(($offset-1)*$num).", ".$num;

$action='tag';
$param1=$id;
$param2=$offset;
$mc=new Memcache;
$cr=$mc->connect('localhost', 11211);
if ($cr)
{// tenim memcached
$rows=$mc->get($action.'/'.$param1.'/'.$param2);
if ($rows==null)
{// no el tenim a cache
$rows=parent::get_data($sql);
$mc->set($action.'/'.$param1.'/'.$param2, $rows, 1, 3600);
}
}
else
{// no tenim memcached
$rows=parent::get_data($sql);
}

return $rows;

//return parent::get_data($sql);
}

20091015

Create the first bracket of a tournament


Bueno tetes, no tinc molt de temps però si algun cop us heu trobat en el cas de com crear un bracket per un torneig, no es del tot trivial.

Us deixo un troç de codi que el seu output es una array on cada element s'ha d'emparellar amb el següent per construir un bracket inicial d'un torneig ben balancejadet (com els de dragonball, vamos)

Evidentment $inicial es el primer jugador i $final es l'ultim, segur que no funciona si no son números de jugadors correctes, com 8,16,32,64... etc.


$inicial=1;
$final=32;
$num_jugadores=$final-($inicial-1);
$emparejamientos=array(1,4,2,3);
$num_jugadores_tmp=4;
while ($num_jugadores_tmp!=$num_jugadores)
{// desdoblabiento
$num_jugadores_tmp=$num_jugadores_tmp*2;
$emparejamientos_tmp=array();
foreach($emparejamientos as $emparejamiento)
{
array_push($emparejamientos_tmp, $emparejamiento);
array_push($emparejamientos_tmp
, ($num_jugadores_tmp+1)-$emparejamiento);
}
$emparejamientos=$emparejamientos_tmp;
}
print_r($emparejamientos);


Resultat per 16 jugadors:

Array ( [0] => 1 [1] => 16 [2] => 8 [3] => 9 [4] => 4
[5] => 13 [6] => 5 [7] => 12 [8] => 2 [9] => 15
[10] => 7 [11] => 10 [12] => 3 [13] => 14 [14] => 6
[15] => 11 )


I després de tractar-ho i simular resultats:

Ronda 1
1: 1 vs. 16 winner=1
2: 8 vs. 9 winner=9
3: 4 vs. 13 winner=13
4: 5 vs. 12 winner=12
5: 2 vs. 15 winner=2
6: 7 vs. 10 winner=7
7: 3 vs. 14 winner=14
8: 6 vs. 11 winner=6

20090520

How to emulate parse_ini_string in PHP5.2


If you have ever wanted to parse a ini file from "not-a-file" you may have
found the parse_ini_string function, but it only comes in PHP version 5.3 or newer.

Here you have a piece of code that has two options, the easy one, if parse_ini_string is present, use it, if not it creates a temporary file, put some content on it, parse_ini_file on that file and then delete the file.



if (function_exists('parse_ini_string'))
{// php 5.3
$return_ini_array=parse_ini_string($ini_file_string);
}
else
{// php anterior a 5.3
$temp_file_name = tempnam('/tmp', 'temp_lang_');
$handle = fopen($temp_file_name, "w");
fwrite($handle, $ini_file_string);
fclose($handle);
$return_ini_array=parse_ini_file($temp_file_name);
unlink($temp_file_name);
}



It gets an string with the ini-file-format called $ini_file_string and returns an array with the parsed elemements of the input string ($return_ini_array).

We'll that's not perfect, but it works !



PD: Com desestressa programar, algu vol anar a visitar clients mentre jo em quedo programant a la oficina ?


:)

20090427

CAPTCHAS cachondos, avui, follo a gmail

Quina va ser la meva sorpresa l'altre dia, al crear una conta nova de gmail, al veure aquest CAPTCHA.





Hoy FOLLO con FATATAS !!!

Impressionant !

20090227

O'Reilly Webcast: Developing Applications for Palm webOS

Cada cop m'esta molant mes el Mojo SDK, podeu veure el primer video per developers



També podeu reservar el llibre de Palm WebOS de O'Reilly o llegir el primer capítol gratuitament

M'ha agradat especialment de la demo que el Mojo SDK sigui Model-View-Controller, que els serveis del hardware es cridin via URLs de l'estil palm:// i implementis una funcio de callback pel success o el error i la facilitat d'utilitzar html standard amb algo de mojo i css.

Problema, esta molt basat amb prototype, MOC pels ultrafans del jQuery, encara que diuen que sera compatible tot el core ja esta fet amb prototype :(

A la ronda de preguntes han sortit alguns dels problemes que ja es veuen a venir:
- Full screen apps
- Posibilitat d'accedir directament al hardware amb un llenguatge de mes baix nivell (per jocs, players, etc)
- Que passa amb el codi de les APPS que creis ? Al ser tot javascript+html+css sera tot visible i per tant, facilment copiable, com la part visible de les aplicacions web. I es més, serà modificable per l'usuari ? podra entrar al seu device i modificar codi ?

El tema s'esta possant interessant, la veritat !

20081209

Articles interessants d'avui

Com a recordatori per mi mateix com a mínim, he vist un nou PNG Fix per IE6 (a veure si trobem el Sant Greal dels PNGFixs per fi) i un exemple de com fer servir la API de Akismet per detectar SPAM desde PHP.

http://www.webresourcesdepot.com/ie6-png-fix-more-features-dd_belatedpng/
http://blog.unijimpe.net/utilizar-akismet-con-php/

20081201

Instal·lacio d'un nou PC


Sempre que instal·lo un nou PC intento que sigui un proces lo menys painful posible.

Fins i tot al 2008 on moltes aplicacions ja son rollo web, deu n'hi do la de aplicacions que has d'instal·lar en el PC.

De moment deixo la llista (espero que definitiva) de software que he instal·lat en el nou portàtil i els passos que he seguit.



1. Configurar la WIFI

2. Instal·lar software:
- AVG Antivirus Free
- Firefox 3
- Flash Player
- NotePad++
- Filezilla
- Navicat
- Chrome
- Palm Desktop
- Avantgo Client
- Safari
- Thunderbird
- VLC Player
- VNC Free
- Putty
- ActiveState Perl
- The GIMP
- WireShark
- Pidgin
- ScreenHunter
- WinRAR
- Office 2003 (contra mes antic millor)

3. Instal·lar Firefox add-ons:
- Firebug
- Alexa Sparky
- Firecookie
- MeasureIt

4. Instal·lar Thunderbird add-ons:
- Diccionari català
- Diccionari castellà
- Diccionari anglés

5. Desinstal·lar el que venia amb el portatil i no m'interessa:
- Norton Security (trial)
- Google Desktop

6. Configurar unitats de xarxa, impressores i scripts de backup amb PERL

7. Configurar l'escriptori i el menú de Inicio.




20081016

Redirecció de dominis mantenint el pagerank

301 powa! 

Amb la relativa novetat dels dominis .cat de vegades ens podem trobar que un client esta molt ben posicionat a google amb el seu domini .es pero no tant bé amb el .cat

Segons google, la solució per migrar un domini es utilitzar redireccions 301 o moved permanently, com que ens interessa només utilitzar example.cat a partir d'ara i deixar de fer servir example.es (encara que continui actiu) aplicarem el seguent codi a les primeres linies del nostre controller.


if ($_SERVER['SERVER_NAME']=='www.example.es'
|| $_SERVER['SERVER_NAME']=='example.es'
|| $_SERVER['SERVER_NAME']=='nou.example.cat')
{
header ('HTTP/1.1 301 Moved Permanently');
header ('Location: http://www.example.cat'.$_SERVER['REQUEST_URI']);
die();
}


D'aquesta manera aconseguirem que les urls amb example.es es redireccionin permanentment a les que tenen example.cat, fixeu-vos que mantenim la resta de la url (utilitzant $_SERVER['REQUEST_URI']) i així portem a l'usuari a la pagina que volia arribar pero amb el domini canviat.

Exemples:
http://www.example.es/hola/hola --> http://www.example.cat/hola/hola
http://example.es/hola/hola --> http://www.example.cat/hola/hola
http://nou.example.cat/hola/hola --> http://www.example.cat/hola/hola

(en l'exemple nou.example.cat era un domini temporal que vem utilitzar mentre estavem desenvolupant i que ja no ens interessa mantenir)


20080923

CODERS KARMA



Avui hem solucionat un bug de la editora "hard candy cms 2.8", es tracta que al guardar les imatges, calculem la seva mida amb el GD (width i height en pixels). Per enmagatzemar els valors, vaig fer servir la columna num_val que era una columna "float" que no es feia servir en aquest cas.

La idea era guardar el width com part entera i el height com a part decimal, MOOOOOOOOOOOOOOOOC, error !

Si tenim un tamany de 400x400 pixels per exemple, el cabron del mysql enmagatzema 400.4 en contes de 400.400 !

Això demostra que com jo sempre dic, el KARMA DELS PROGRAMADORS pasa factura.

Repetiu amb mi: No utilitzaré mai un camp de base de dades per un altre us pel qual no estigui dissenyat (i menys si es un float)

Finalment m'he vist obligat a crear un nou camp img_info a la taula de valors de la editora que s'utilitzarà només en valors de tipus (I)mage.

BON KARMA a tothom i bona Mercè

20080828

Fucking suse and php5 problem

I'm trying to setup a new server with a new apache, php and mysql.

Copying the same php development files must be enough, but I've spend two hours with this stupid error.

When connecting to the website, I've got a dump of php code, initially I though that it was the full code of the file referenced but investigating for a long time make me realize that it was the code of a single file included in the initial file.

The problem was finally that I've wrongly started some files with:
<?
instead of the full
<?php

Problem solved, but a lot of more white hair growing from my skull :(

UPDATED: Finally I've activated the short_open_tag in the php.ini file because is used in other places (correctly) when I wanted to display variables in template files. Thanks Oriol.
At least I've changed the wrong php files with the short open tag at the beginning.

20080709

Consells per implementar un "MVC framework"

Si algún dia fas un "MVC Framework" recorda les paraules del sabi:

Un "MVC Framework" no es un fi en si mateix, ha de servir per el projecte pel qual l'estas creant.


Afegiria que hipoteticament et pot servir per altres projectes, peró no cal obsessionar-se amb aquest punt.

Un "MVC Framework" ha de contenir les funcionalitats necesaries pel projecte que estas fent i prou, no milloris el Framework per soportar estructures massa genériques perquè acabaras fent un "MVC Framework" només per tenir un "MVC Framework" no per aplicar-lo en el projecte que es realment el teu objectiu.

Si trobes una millora que aplica a tot el framework i que et permetra reduir codi en un munt de fitxers, endavant, pero el meu consell es "si no començes primer a programar es dificil que trobis posibles optimitzacions REALS del framework", refactoring, refactoring and refactoring, until nearly-perfection (recorda que a la perfecció no pots arribar).

Principis per qualsevol web application en general i per un MVC framework en particular:

  • Premature optimization is the root of all evil
  • Keep it simple stupid (KISS)
  • Per un programador, un bon dia es reduir el codi en 100 lineas (si continua funcionant, clar)
  • Programa, no pensis
  • Pensa, no programis

20080617

Com crear un CDN gratis utilitzant Google App Engine




Un amic meu que no soc jo :) ha fet un bonic tutorial de com crear un CDN utilitzant la infraestructura mundial de google.

Article sobre com crear un CDN gratis amb "google app engine"

En nomes 68 linies de Python !

Es gratis fins a 5 milions de pageviews al mes.

20080319

Las 8 preguntas de las consultoras Big-5 al iniciar un proyecto


En el post "Question your work", Jason de 37 signals nos proponía 8 preguntas que debes hacerte antes de iniciar un proyecto (en mi caso para clientes).



Considero estas 8 preguntas muy interesantes y, consciente o inconscientemente, siempre las planteo en los proyectos que abordamos.

Las 8 preguntas originales son estas:

1. ¿Por qué estamos haciendo esto?
2. ¿Qué problema estamos solucionando?
3. ¿Es esto realmente útil?
4. ¿Estamos añadiendo valor?
5. ¿Cambiará la manera de hacer las cosas?
6. ¿Hay alguna manera más fácil de hacerlo?
7. ¿Cuál es el coste de oportunidad?
8. ¿Realmente vale la pena?

Pues resulta que hace unos días tuve una reunión con unos ex-Consultores de una Big-5 que me recordo a mis mejores épocas rodeado de Arturitos en Sony. Entiendo que un director de una consultora high-level, al cabo de los años y con la experiencia acumulada en mil proyectos, resumiría sus ocho preguntas más o menos así:

1. ¿Tiene sangre (pa$ta) el cliente? ¿Está dispuesto a que alguien le chupe la sangre (tiene presupuesto)? Si no es así, más vale echar a correr, no sigas leyendo.
2. ¿Puedo ser yo el que se lleve el proyecto? ¿Tengo algún conocido/pariente/networking dentro del cliente que me ayude a conseguirlo?
3. ¿Es posible que el proyecto que estoy intentando vender sea totalmente inútil, pero tan caro, que el director general de la compañia apruebe el presupuesto, aunque sea para vacilar con sus amigos directores generales mientras juega a golf?
4. ¿Tengo acceso al susodicho director general, el que toma las decisiones, y me da igual lo que opinen el resto de empleados que realmente utilizarán la herramienta?
5. Aunque el proyecto sea un fracaso, ¿tengo algún cabeza de turco a quien culpar? Es ideal escoger algún empleado del cliente, que son los peor vistos por las altas esferas, si puede ser el único que lo ha dado todo por el proyecto, mejor; ¡así seguro que salen más proyectos en el cliente!
6. ¿Soy capaz de incorporar un equipo de Análisis de como mínimo 5 personas y que creen un PowerPoint de mínimo 30 Slides, lleno de obviedades, antes de la firma del proyecto y por supuesto facturar todos estos recursos (de perfil recién licenciado) a precio de MBA de Stanford?
7. ¿Puedo colocar algún consultor mega-experto de Madrid (si estás en Barcelona) o de Barcelona (si estás en Madrid)? Luce un montón, sacarás algo más de margen con las dietas y encima siempre tienes tema de conversación con él. El lunes a media mañana cuando llega: "¿Cómo ha ido el vuelo?" y el viernes después de comer: "¿A qué hora tienes el vuelo?"
8. ¿Tengo algún perfil del tipo high-level-project-manager, si puede ser gordito y bonachón mejor, que sólo vaya a la primera y a la última reunión del proyecto y cuyo propósito principal sea invitar a comer o cenar a toda la cúpula directiva y después tomar unos cubatas y lo que surja?

Como veréis, no soy muy amigo de los consultores de alto nivel, ya que se comen los jugosos presupuestos grandes dejando las migajas que realmente aportan valor para las pequeñas pymes tecnológicas :)

20080307

Presentació de l'iPhone SDK (esdikey)

Aquesta nit no podía dormir i com que sabía que hi havia la presentació d'Apple del nou SDK per l'iPhone, m'he decidit a veure el vídeo de presentació.

JA NO PODRE DORMIR !

Increible, un SDK amb accés a totes les funcionalitats del Sistema Operatiu i les increibles funcionalitats que només trobes en un iPhone:

  • Multi-touch interface
  • TCP/IP integrat
  • 3D Engine
  • Geolocalització
  • Accés a la API del accelerometer (moviment espaial, tipus Wiimote)
  • SQLLite integrat
  • Só envolvente multicanal
  • Accés a la Agenda
  • Accés a la camera
  • ...


A més el SDK es gratuït, pots baixar-te'l i començar a programar JA!

Les eines de desenvolupament son espectaculars si fem cas de les demos, trempera tecnológica a tope !


També han pressentat una especie de tenda on podras descarregar els programes (sempre validats per Apple). Falta veure si aquest model de distribució de software trionfa o no, pero les característiques son espectaculars.

  • Instal.lació i actualització dels programes "over the air" sempre que tinguis connexió
  • Els programes gratuits son realment gratuits, si passen el control de qualitat d'Apple, el desenvolupador no ha de pagar res i l'usuari tampoc.
  • Els programes de pagament es venen amb un rati de 70-30 (70 pel developer, 30 per Apple)



L'unic fee que han anunciat es un fee de 99$ per entrar dins del developer program, amb la qual cosa crec que donaran support als desenvolupadors, dema mateix, omatech.com s'apuntarà al developer program, no hi ha res a perdre i si molt a guanyar.

Bé, els competidors ja es poden possar les piles:
  • Nokia ja cal que espabilis
  • Microsoft deixa-ho ja, si en tants anys no t'has fet amb el mercat, desenganyat, ja no ho faras
  • Google, potser necessitaras alguna cosa més que un Android per trencar el mercat.
  • RIM i Palm, el viure de rendes s'ha acabat, el killer mobile device definitivament ha arribat.


Si encara no era un "believer" avui m'han convençut, l'iPhone arrasarà, es un producte trencador que pot capturar molt "developer mind share" en molt poc temps i aquest cop Microsoft no té ni idea del que li ve a sobre.


Més informació a http://developer.apple.com
Per cert aquí teniu el vídeo que m'ha fet veure la llum

20080202

Com fer un backup incremental en 65 lineas de PERL



Sempre he volgut tenir una manera fàcil de fer backups incrementals de directoris sencers.

Aquest cap de setmana he dedicat un dia (no sencer) a implementar un procés amb els següents requeriments:

- El procés escanejara el directori d'origen i replicarà exactament tot el contingut al directori de destí
- Si pel camí trobem fitxers o directoris ja existents, comprovarem si s'han modificat
- Si el fitxer no s'ha modificat, no el copiem
- Si el directori ja existeix, no el recreem
- El directori d'origen MAI serà modificat pel procés, nomes es permet passar fitxers o directoris al destí, així minimitzem el perill de fer alguna bestiesa

Per mi, es un backup incremental prou bo.


La idea es instal·lar als nous Desktops de la oficina que van sobrats de disc dur, el procés, configurat perquè faci backup de les dues unitats de xarxa que tenim a la oficina i on tots treballem de forma compartida.

Amés una es una unitat de SAMBA que esta mapejada a un Linux de desenvolupament i l'altre es una unitat remota normal i corrent d'un Fileserver amb Windows.

Dilluns crearé un directori de backup per cada unitat de xarxa a cada PC dels nous i provaré el procés, a veure si tinc èxit.

De moment en local em funciona, us explico una mica com ho he fet:

Com sempre diu el Manel, el PERL es molt potent, val a dir que jo sempre modifico la frase per "El PERL es potent, pero PERILLOS!!!"

De totes maneres, aquest matí m'he instal.lat la versió gratuïta el "Active State Perl" el líder en entorn Windows i he començat a rascar, val a dir que fa un any que tinc experiència demostrada en PHP però el PERL és lleugerament diferent i he hagut de canviar una mica el Xip.

Anem a veure com fer un backup incremental d'un directori origen a un directori destí, en nomes 65 línies de PERL (incloent comentaris i sense fer xapusses per compactar codi)

Declaració de variables i d'intencions:

Les primeres línies son molt avorrides, només fem imports i declarem variables...


use strict;
use warnings;
use File::Find;
use File::Copy;
use File::stat;

my $files_copied=0;
my $files_updated=0;
my $dirs_scanned=0;
my $files_scanned=0;
my $dirs_created=0;

my $source = 'c:/apons/';
my $destination = 'd:/apons2/';
print "Anem a copiar tot el contingut de manera incremental de $source a $destination!\n";


$source serà el directori d'origen (recordem que aquest mai es modificarà) i $destination es el directori de destí, només cal que existeixi, el procés ja crearà tota la estructura de directoris per sota d'ell.

Uff, ja portem 15 lineas, no se pas si aconseguirem fer tot això en les 50 que ens queden :)


find(\&Wanted, $source);

print "Dirs scanned: $dirs_scanned, Files Scanned: $files_scanned\n";
print "Dirs created: $dirs_created, Files copied: $files_copied, Files updated: $files_updated\n";


De fet aquí acaba el "main program", ja que la resta esta a la Subrutina "Wanted", en aquestes 3 línies, simplement cridem a la funció find, que recorre tots els fitxers recursivament partint directori indicat pel segon paràmetre, en aquest cas $source, per cada fitxer s'executarà la subrutina Wanted.

El find de PERL (que podem fer servir gracies al use File::Find; de les primeres línies) em recorda bastant a la comanda de UNIX find . -name "$source" -exec Wanted {} \; (sempre salvant les distancies)

Ah, les altres línies son com un resum de les operacions que hem fet, clar, no ?


sub Wanted
{
my $elemento = $_;
my $source_file=$File::Find::name;
my $destination_file=$destination.substr($source_file, length($source)-1);

if (-d $elemento)
{# Es un directori
$dirs_scanned++;
if (-e $destination_file)
{# Ya existia el directori, no fem res
#print "Directorio $destination_file ya creado NO action \n";
}
else
{# No existia el directori, el creem
$dirs_created++;
print "Vamos a crear -> $destination_file\n";
mkdir $destination_file;
}
}


Anem una mica més al gra, vegem el codi de la subrutina Wanted, aquí es on es realment fem la feina.

my $elemento = $_; asigna el fitxer actual que estem tractant a $elemento;

Assignem a $source_file la variable $File::Find::name que conte el nom complet del fitxer (incloent el directori).

A la línia my "$destination_file=$destination.substr($source_file, length($source)-1);" es on substituïm el directori d'origen pel directori destí.

El "if (-d $elemento)" comprova si el fitxer que estem escanejant es un directori, si aquest es el cas, fem el següent

$dirs_scanned++; incrementem les estadístiques.

if (-e $destination_file), si existeix el directori destí no fem res, en canvi al else, incrementem els $dirs_created, printem un missatge i fem un mkdir per crear el directori destí.

Ja portem 40 línies, ai mareta, no se si arribarem, ens queden 25 !!!



if (-f $elemento)
{# Es un fitxer
$files_scanned++;
if (-e $destination_file)
{# El fitxer ja existeix, mirem la data de modificacio
#print "El fichero $destination_file existe, comprobamos si esta actualizado.\n";
my $source_stat = stat($source_file);
my $source_mtime = $source_stat->mtime();
my $destination_stat = stat($destination_file);
my $destination_mtime = $destination_stat->mtime();
if ($source_mtime > $destination_mtime)
{# Efectiviwonder, el fitxer s'ha modificat, el copiem
print "OJITO, algo ha canviat al fitxer origen $source_file, maxaquem el backup\n";
copy($source_file, $destination_file);
$files_updated++;
}
}
else
{# El fitxer de desti no existeix, el copiem
print "El fichero $destination_file NO existe, lo copiamos.\n";
copy($source_file, $destination_file);
$files_copied++;
}
}
}


El primer if "if (-f $elemento)" controla si el fitxer que estem tractant es realment un fitxer.

El segon, controla si el fitxer de destí ja existeix "if (-e $destination_file)", si es així declarem obtenim les propietats del fitxer origen i el fitxer destí amb les següents línies:


my $source_stat = stat($source_file);
my $source_mtime = $source_stat->mtime();
my $destination_stat = stat($destination_file);
my $destination_mtime = $destination_stat->mtime();


La funció stat, la podem fer servir gracies al "use File::stat;" en aquest cas obtenim el mtime (modification time) del fitxer d'origen i del destí.

Posteriorment comprovem si el "$source_mtime > $destination_mtime" si es així hem de maxacar el fitxer de destí sense pietat, ja que està obsolet:


print "OJITO, algo ha canviat al fitxer origen $source_file, maxaquem el backup\n";
copy($source_file, $destination_file);
$files_updated++;


Com veieu hem aprofitat per printar un bonic missatge i incrementar el contador de fitxers actualitzats.

Finalment anem al else, que es quan el fitxer de destí no existeix, llavors simplement copiem el fitxer al destí, printem un missatge i actualitzem les estadístiques.


print "El fichero $destination_file NO existe, lo copiamos.\n";
copy($source_file, $destination_file);
$files_copied++;



Aquí podeu veure un exemple de una execució incremental:


C:\apons>test.pl
Anem a copiar tot el contingut de manera incremental de c:/apons/ a d:/apons2/!
El fichero d:/apons2//lvg200706120451lb.pdf NO existe, lo copiamos.
OJITO, algo ha canviat al fitxer origen c:/apons/test.pl, maxaquem el backup
El fichero d:/apons2//i_want_you_to_learn_perl.jpg NO existe, lo copiamos.
Vamos a crear -> d:/apons2//proves
El fichero d:/apons2//proves/jquery.js NO existe, lo copiamos.
El fichero d:/apons2//proves/provainplace.html NO existe, lo copiamos.
El fichero d:/apons2//proves/provainplace2.html NO existe, lo copiamos.
El fichero d:/apons2//Superman-RedSon/Superman - Red Son 03/RED SON08.JPG NO existe, lo copiamos.
Dirs scanned: 250, Files Scanned: 2653
Dirs created: 1, Files copied: 6, Files updated: 1


No torno a copiar tot el codi complert, perquè son només 65 línies, una mica de entrenament de copy&paste no us anirà malament ;)

Enjoy!