Davide Muzzarelli » script completi /blog L'informatica a valore aggiunto Mon, 25 May 2015 22:41:52 +0000 en hourly 1 http://wordpress.org/?v=3.3.2 Dynamic pure SQL WHERE clauses and optional JOINs /blog/2015/01/dynamic-pure-sql-where-clauses-and-optional-joins/ /blog/2015/01/dynamic-pure-sql-where-clauses-and-optional-joins/#comments Tue, 06 Jan 2015 02:44:54 +0000 Davide Muzzarelli /blog/?p=332 Update 2015-05-25: see the video tutorial at the end of this article.

A dynamic SQL query is a query that operates accordingly through parameters.

Sometimes it is necessary to query the database filtering the rows in base at the user input. Like in a search, an user would choose several filters and not others.

The examples are in PostgreSQL and Python, but can be easily adapted to any database and language.

Imagine a table of customers:

CREATE TABLE "customers" (
    "id" serial PRIMARY KEY,
    "name" varchar(64) NOT NULL,
    "active" bool NOT NULL,
    "country" varchar(32)
);

The classic approach

The classic approach is to compose the SQL joining strings and conditional constructs, for example:

# Function that performs the query
# The parameters have null defaults
def customers_filtered(active=None, country=None):
    # This is the beginning of the query
    query = '
        SELECT *
        FROM customers
        WHERE'

    filters = []

    # Add the active filter, if not null
    if active is not None:
        filters.append('active=%(active)s')

    # Add the country filter, if not null
    if country is not None:
        filters.append('country=%(country)s')

    # Join all the filter queries chunks with an AND
    query += ' AND '.join(filters)

    # Prepare the parameters
    parameters = {
        'active': active,
        'country': country
    }

    # Execute the query and return the results
    return execute(query, parameters)

The resulting code is long, the query takes times to write and is unclear to read.

The database have to make an execution plan before executing the query. The execution plan is re-used when the SQL query is static, but this query is dynamic so the execution plan will be not re-used a second time and making the query a bit slower.

Optional WHERE clause

There is a solution with a negligible impact on perfomances, much easier to write and to read. The trick is to use NULL conditionals, so the query will be:

# Function that performs the query
# The parameters have the null defaults
def customers_filtered(active=None, country=None):
    # This is the full query!
    query = '
        SELECT *
        FROM customers
        WHERE (%(active)s IS NULL OR active=%(active)s)
              AND (%(country)s IS NULL OR country=%(country)s)'

    # Prepare the parameters
    parameters = {
        'active': active,
        'country': country
    }

    # Execute the query and return the results
    return execute(query, parameters)

The trick is to validate the right part of the OR clause only if the parameter is not NULL.

Optional JOINs

A similar approach can be applied for the JOIN clauses. First, create the “orders” table:

CREATE TABLE "orders" (
    "id" serial PRIMARY KEY,
    "customer_id" int NOT NULL,
    "date" timestamp NOT NULL,
    "value" int NOT NULL
);

It is possible to use a dynamic JOIN in order to get or not expensive data to calculate. The following SUM will be performed only if needed, setting the “flag” as true:

SELECT u.id, u.name, u.active, SUM(o.value) AS total
FROM customers AS u
LEFT JOIN orders AS o ON (true=%(flag)s AND o.customer_id=u.id)
GROUP BY u.id

Also in this case the performance impact is risible and the query remains very clear to read.

Conclusions

The classic method of composing the queries can be a source of bugs and headaches, this should be avoided. These solutions can reduce the code and increase the readability, especially when the ORM cannot be used or with the CQRS pattern (Command Query Responsibility Separation).

A video tutorial

Nat Dunn has created a nice video tutorial inspired by this article, it is part of his Python course:

Discussion on
Hacker News: https://news.ycombinator.com/item?id=8842677
Reddit: http://www.reddit.com/r/Database/comments/2rh0ak/dynamic_pure_sql_where_clauses_and_optional_joins/
Twitter: https://twitter.com/davmuz_en/status/552295916385087489

]]>
/blog/2015/01/dynamic-pure-sql-where-clauses-and-optional-joins/feed/ 0
How to use Go and FastCGI /blog/2013/09/how-to-use-go-and-fastcgi/ /blog/2013/09/how-to-use-go-and-fastcgi/#comments Tue, 03 Sep 2013 20:09:23 +0000 Davide Muzzarelli /blog/?p=303 FastCGI is a good solution for using Go in a shared hosting like Dreamhost or also in a VPS. This post is about how to write a Go program in FCGI mode and deploy it on Apache.

In the CGI mode our program will be executed at every request. Go loads pretty fast so it may run fast like PHP, but it is possible to use a better approach.

FastCGI have the same protocol like CGI, but the web server loads the program one time then submit the requests. This is much more efficient, but the program have to be written better in order to keep a low use of RAM since the process will remain active for a while.

The web server can communicate with a FastCGI program in three manners: via the standard input/output, via a UNIX socket or via a TCP connection. In a shared hosting environment the standard input/output way is the most available and the web server will load the program for you automatically. Instead, using a UNIX or TCP socket, you will have to load and manage your program yourself monitoring it if it crashes (see Supervisord for example), because the web server can’t handle it for you.

The code

This example requires github.com/gorilla/mux and it’s tested on Golang 1.1.2, but it’s not mandatory:

go get github.com/gorilla/mux

The following is the source of the program (fcgitest.go):

import (
    "flag"
    "github.com/gorilla/mux"
    "io"
    "log"
    "net"
    "net/http"
    "net/http/fcgi"
    "runtime"
)

var local = flag.String("local", "", "serve as webserver, example: 0.0.0.0:8000")

func init() {
    runtime.GOMAXPROCS(runtime.NumCPU())
}

func homeView(w http.ResponseWriter, r *http.Request) {
    headers := w.Header()
    headers.Add("Content-Type", "text/html")
    io.WriteString(w, "<html><head></head><body><p>It works!</p></body></html>")
}

func main() {
    r := mux.NewRouter()

    r.HandleFunc("/", homeView)

    flag.Parse()
    var err error

    if *local != "" { // Run as a local web server
        err = http.ListenAndServe(*local, r)
    } else { // Run as FCGI via standard I/O
        err = fcgi.Serve(nil, r)
    }
    if err != nil {
        log.Fatal(err)
    }
}

The “flags” piks the command input. If “localhost” is set as a parameter, the program will run as a local web server and not in FCGI mode (see in your browser):

fcgitest -local=":8000"

The “init” function sets the number of processors to use at the same number of processors available by the machine.

The “homeView” function is a view that prints an HTML output to the user.

The main function sets up a URL router adding the “homeView” function at the root “/” path using the Gorilla handler.
The “if” part chooses if it runs as FCGI or as web server.

Pay attention at the “err = fcgi.Serve(nil, r)” line because it’s not documented well in the Go’s documentation: use “nil” for the standard input/output mode.

Run in TCP or Unix mode

Add two more flags for the TCP and UNIX socket modes:

var (
    local = flag.String("local", "", "serve as webserver, example: 0.0.0.0:8000")
    tcp   = flag.String("tcp", "", "serve as FCGI via TCP, example: 0.0.0.0:8000")
    unix  = flag.String("unix", "", "serve as FCGI via UNIX socket, example: /tmp/myprogram.sock")
)

…and change the “if” part like the following:

if *local != "" { // Run as a local web server
    err = http.ListenAndServe(*local, r)
} else if *tcp != "" { // Run as FCGI via TCP
    listener, err := net.Listen("tcp", *tcp)
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close()

    err = fcgi.Serve(listener, r)
} else if *unix != "" { // Run as FCGI via UNIX socket
    listener, err := net.Listen("unix", *unix)
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close()

    err = fcgi.Serve(listener, r)
} else { // Run as FCGI via standard I/O
    err = fcgi.Serve(nil, r)
}
if err != nil {
    log.Fatal(err)
}

The same code can be written with a “switch” statement.

In this case we are creating a TCP or a UNIX listener so it have to be closed at the end of the execution.

Deployment under Apache

This is an example of deployment under Debian/Ubuntu using the standard I/O mode, it should be compatible with Dreamhost and is a fast way to test it.

First install mod_fcgid, enable it and reload Apache. For example, this is under Debian/Ununtu:

sudo apt-get install libapache2-mod-fcgid
sudo a2enmod fcgid
sudo service apache2 reload

Compile, rename the file and deploy it on the web server:

go build fcgitest
mv fcgitest fcgitest.fcgi

Change the file access permissions removing the write flag from “group” and “others” or Apache will raise a 118 error:

chmod go-w fcgitest.fcgi

Add the following “.htaccess” file, it will redirect all the requests that are not a file or a directory to the Go program through the FastCGI protocol via the standard I/O mode:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d [OR]
RewriteCond %{REQUEST_URI} ^/$
RewriteRule ^(.*)$ fcgitest.fcgi/$1 [QSA,L]

Done, try it visiting the web site.

Conclusions

Personally I like to use this protocol via standard I/O because I run several web sites on my servers, like a hosting company; this allows me to keep the things simple and with no effort with a low RAM usage.

Download the full Go source code.

Post to Hacker News

]]>
/blog/2013/09/how-to-use-go-and-fastcgi/feed/ 4
New Smarty plugin for highlighting PHP code /blog/2011/02/new-smarty-plugin-for-highlighting-php-code/ /blog/2011/02/new-smarty-plugin-for-highlighting-php-code/#comments Mon, 21 Feb 2011 15:19:54 +0000 Davide Muzzarelli /blog/?p=241 I want to signal a new plugin for highlighting PHP code.

I wrote this modifier for Smarty, a template engine for PHP that I am using for a long time.

The plugin can be downloaded from here.

The license is the New BSD.

The usage is very simple:
{$php_code|highlight}

]]>
/blog/2011/02/new-smarty-plugin-for-highlighting-php-code/feed/ 0
Convertire un repository SVN ad uno GIT in remoto /blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/ /blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/#comments Sat, 28 Feb 2009 17:29:00 +0000 Davide Muzzarelli /blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/ Recentemente ho convertito i miei repository Subversion verso GIT.
La conversione in parte è dolorosa perché GIT è più difficile e complicato da usare rispetto a SVN ma offre un paio di vantaggi di cui ho bisogno: ottimizzazione della banda (possibilità di lavorare offline), migliore gestione dei branch. Prima o poi scriverò un post a riguardo dei vantaggi/svantaggi.

Convertire un repository non è una cosa così immediata, mi ci sono volute molte prove e la documentazione di GIT non è poi così completa. La maggior parte degli esempi che si trova in rete si riferisce all’uso di SVN con GIT, o all’importazione di un repository Subversin in locale e non ad una conversione completa in remoto.

Ecco le condizioni:

  1. Convertire un repository SVN ad uno in GIT, eliminando quindi il vecchio repository alla fine del passaggio.
  2. Il repository GIT deve trovarsi in un server remoto.
  3. I tag e i branch vanno convertiti correttamente nei corrispettivi in GIT.
  4. Il “trunk” deve diventare il “master”.
  5. Il repository Subversion da copiare si trova all’indirizzo svn://login1@server1/repository/progetto1/
  6. Il repository di destinazione GIT, già pronto ad accogliere i nuovi dati, si trova all’indirizzo git://login2@server2/repository/progetto2.git
  7. I comandi eseguiti non devono avere nessun effetto sul vecchio repository, in modo da poter ripetere la procedura da capo se qualcosa va storto.

Dopo aver provato la versione 1.5.2 di GIT, ho capito che non era adatta per questo lavoro dato che non era ancora pronta per via di qualche bug. Sono quindi passato alla versione 1.6.3, ma già la versione 1.5.3 potrebbe andare bene.

Il primo passaggio è quello di clonare in locale il primo repository sul nostro computer:

git svn clone -s svn://login1@server1/repository/progetto1/ progetto

Il parametro “-s” indica di utilizzare i nomi di default per “trunk”, “branches” e “tags”, in modo da individuare le directory giuste. E’ possibile sovrascriverli con i parametri “–trunk”, “–branches” e “–tags”.
L’ultimo parametro “progetto” è il nome della directory da creare in locale.
Una volta eseguito questo comando, git-svn inizierà a scaricare i dati nella directory “progetto”.

Una volta scaricato tutto, indichiamo il repository di destinazione:

cd progettogit remote add origin git://login2@server2/repository/progetto2.git

git-svn non converte direttamente i tag e i branch, ma crea solo dei branch in locale. Vediamo quali tag e quali branch abbiamo da convertire:

git branch -a

Mettiamo che il risultato sia questo:

* master miobranch altrobranch tags/1.0 tags/2.0 trunk

Il parametro “-a” serve a mostrare tutti i branch e non solo quelli locali.
Il branch master non contiene il trunk, ma il branch con l’ultimo commit, per cui sarà da correggere.

Siamo pronti per convertire i tag:

git tag 1.0 tags/1.0git branch -r -d tags/1.0git tag 2.0 tags/2.0git branch -r -d tags/2.0

Il primo comando crea un tag GIT utilizzando il riferimento a quello vecchio (che è in remoto).
Il secondo comando elimina il branch del vecchio tag (opzione “-d”) che si trova in remoto (“-r”).
Il terzo ed il quarto comando eseguono la stessa procedura sul secondo tag.

Ora passiamo alla conversione dei branch:

git checkout -b mionuovobranch miobranchgit checkout -b altrobranch altrobranchgit checkout trunk trunk

Il primo comando converte il branch “miobranch” in “mionuovobranch” cambiandone anche il nome.
Il secondo comando converte il branch “altrobranch” mantenendo lo stesso nome. Questo genererà un errore a cui non bisogna fare caso, proprio perché il vecchio branch ha lo stesso nome di quello nuovo.
Il secondo comando serve a preparare il trunk per sostituirlo al master.

Sostituiamo il master con il trunk:

git branch -D mastergit checkout trunkgit checkout -f -b mastergit branch -d trunk

Il primo comando elimina il master con tutta la sua storia (parametro “-D”).
Il secondo comando ci fa spostare all’interno di trunk.
Il terzo comando crea il branch master basato su trunk (parametro “-b”) e ignora tutti gli eventuali errori (parametro “-f”).
Il quarto comando elimina il branch trunk mantenendo comunque lo storico (parametro “-d”).

Siamo pronti per immettere i dati sul nostro nuovo repository di destinazione:

git push origin mastergit push origin 1.0git push origin 2.0git push origin mionuovobranchgit push origin altrobranch

Il primo comando carica i dati sul repository remoto.
I comandi successivi aggiungono i tag e i branch.

A questo punto il lavoro è finito: possiamo eliminare la direcotory locale di GIT e creare una nuova copia locale pulita.

cd ..rm -fr progettogit clone git://login2@server2/repository/progetto2.git progetto
]]>
/blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/feed/ 0
Installare il modem Huawei E220 di Tim sotto Ubuntu /blog/2008/10/installare-il-modem-huawei-e220-di-tim-sotto-ubuntu/ /blog/2008/10/installare-il-modem-huawei-e220-di-tim-sotto-ubuntu/#comments Fri, 31 Oct 2008 16:53:00 +0000 Davide Muzzarelli /blog/2008/10/installare-il-modem-huawei-e220-di-tim-sotto-ubuntu/ In questi giorni ho avuto a che fare con il modem Huawei E220 che Tim fornisce col proprio abbonamento a banda larga via GSM/GPRS/EDGE/UMTS/HSDPA.

Ho installato il dispositivo sotto Kubuntu 8.04 Heron utilizzando Kppp, ma si può usare anche Gnome-ppp oppure wvdial. Lo Huawei ha funzionato sia come GSM che come UMTS, non ho avuto la possibilità per provare l’HSDPA dato che è una rete ancora rara da trovare.


Lo Huawei è composto principalmente da un modem e da una memoria di massa che contiene il software necessario per Windows. Naturalmente GNU/Linux non ha bisogno della memoria di massa per poter usare il modem appieno. ;)

I parametri da impostare col dialer sono i seguenti:
Numero di telefono: *99***1#
Dispositivo: /dev/ttyUSB0
Velocità: 460800
Stringa di inizializzazione (la numero 2 per Kppp oppure la numero 5 per Gnome-ppp): AT+cgdcont=1,”ip”,”ibox.tim.it”
Attendi il segnale prima di comporre il numero?: No.
Username: qualsiasi va bene.
Password: qualsiasi va bene.

I parametri per Wind sono simili:
Stringa di inizializzazione: AT+cgdcont=1,”ip”,”internet.wind”
Numero di telefono: *99#

Con una installazione semplice il dispositivo funziona a casaccio (si connette una volta su dieci), questo perché viene riconosciuta prima la memoria di massa che il modem. L’installazione semplice consiste nell’inserire il dispositivo nella presa USB, eseguire il comando:

sudo modprobe usbserial vendor=0x12d1 product=0x1003

…poi staccare il dispositivo, attendere qualche secondo e reinserire il dispositivo.

Una piccola nota, in alcuni casi il codice di prodotto dello Huawei E220 è lo 0×1001 al posto dello 0×1003.

Si può automatizzare il tutto creando il file /etc/udev/rules.d/50-huawei-e220.rules con questo contenuto:

SUBSYSTEM=="usb", SYSFS{idProduct}=="1003", SYSFS{idVendor}=="12d1", RUN+="/sbin/modprobe usbserial vendor=0x12d1 product=0x1003"SUBSYSTEM=="usb", SYSFS{idProduct}=="1003", SYSFS{idVendor}=="12d1", RUN+="/sbin/modprobe ppp_generic"

Spendendo un po’ di tempo con Google ho messo a punto una soluzione alternativa per cui è sufficiente procedere con i seguenti passi che riporto per esteso:

  1. Configurare Kppp, Gnome-ppp oppure wvdial con i parametri riportati prima.
  2. Scaricare il file http://oozie.fm.interia.pl/src/huawei.tar.bz2 e scompattarlo.
  3. Entrare dentro la directory appena creata ed eseguire:
    sudo make ubuntu_install
  4. Modificare il file /etc/modprobe.d/blacklist e aggiungere alla fine:
    blacklist airprime
  5. Riavviare il computer.

Il primo passo scarica una serie di file per permettono a UDEV di riconoscere correttamente il dispositivo. Dato che sono file già pronti in questo modo evito di doverli pubblicare qui sul blog.
Il secondo passo installa tali file.
Il terzo passo è necessario per via di un bug, ovvero che viene caricato il modulo “airprime” che è incompatibile con lo Huawei. In pratica, tale modulo caricherebbe a caso il dispositivo della memoria di massa piuttosto che il modem rendendo difficile potersi connettere. Mettendo il modulo in blacklist questo non viene mai caricato e il dispositivo è così riconosciuto correttamente.
Dalla versione 2.6.29 del Kernel verrà rimosso definitivamente il modulo “airprime” risolvendo la questione una volta per tutte.

Una volta inserito il dispositivo è necessario aspettare una decina di secondi circa prima che esso sia pronto per la connessione.

]]>
/blog/2008/10/installare-il-modem-huawei-e220-di-tim-sotto-ubuntu/feed/ 5
Controllare la scadenza dei propri domini in un colpo solo /blog/2008/09/controllare-la-scadenza-dei-propri-domini-in-un-colpo-solo/ /blog/2008/09/controllare-la-scadenza-dei-propri-domini-in-un-colpo-solo/#comments Sat, 20 Sep 2008 16:58:00 +0000 Davide Muzzarelli /blog/2008/09/controllare-la-scadenza-dei-propri-domini-in-un-colpo-solo/ Se abbiamo molti domini registrati con diverse aziende e vogliamo controllare lo stato delle scadenze per ognuno abbiamo solo due possibilità: controllare il whois a mano oppure entrare in tutti i pannelli di controllo dei servizi di registrazione e controllare da essi.

In alternativa propongo uno script di una sola riga per controllare una lista di domini:

for k in $(cat "domini.txt"); do echo "** $k **:" && whois "$k" | grep Expir; done > aggiornamenti.txt

Naturalmente funziona su tutti i sistemi Unix like come MacOS e Linux.

Lo script prende la lista dei domini dal file “domini.txt” e crea un file “aggiornamenti.txt”, con l’elenco dei domini e le date di scadenza.

Non è uno script molto raffinato ma è sufficiente per controllare lo stato delle scadenze in un colpo solo, risparmiando un sacco di tempo.

]]>
/blog/2008/09/controllare-la-scadenza-dei-propri-domini-in-un-colpo-solo/feed/ 1
Apache mod_rewrite and directories protected by password /blog/2008/09/apache-mod_rewrite-and-directories-protected-by-password/ /blog/2008/09/apache-mod_rewrite-and-directories-protected-by-password/#comments Thu, 04 Sep 2008 12:38:00 +0000 Davide Muzzarelli /blog/2008/09/apache-mod_rewrite-and-directories-protected-by-password/ As a web site developer, when I use a server side script (like a FCGI, PHP, ASP ecc.) I activate the mod_rewrite in order to convert the urls in a pretty format.

So, an url like this:

http://www.foobar.it/index.php?page=articles

…could be converted like this:

http://www.foobar.it/articles/

This is very good for SEO and just for the simplicity of the url.

In order to activate this, is simple to add this code in the “.htaccess” file in the root directory of the web site:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php?page=$1 [QSA,L]

The first line activate the mod_rewrite. The second one control if the url is not a real file on the web server, and the last one convert the url.

Actually it is not so complicated. But try to add a sub-directory protected by password and some problems will arise.

Make a directory “foo”, put a file under it like “index.html” in order to test the behavior and protect it with this “.htaccess” file:

AuthName "Restricted Area"AuthType Basicrequire valid-userAuthUserFile "/home/foobar/passwd"

This code will protect that directory with a password (contained in the “passwd” file in another directory of security reasons).

Try to access to it and you will discover that it is impossible to use. The problem is the mod_rewrite because it rewrite also the url of this directory and it can’t see the index.html file because it is hidden by the password. Without the password protection the file is well displayed…why?? :(

In order to solve this problem you have to add two lines to your root .htaccess file:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-d [OR]RewriteCond %{REQUEST_URI} ^/$RewriteRule ^(.*)$ index.php?page=$1 [QSA,L]

The first line added control if the url is a valid directory into the filesystem, it add a OR condition, and the second line force the void url (in this case is “http://www.foobar.it/) to convert itself with mod_rewrite. The home page will not be displayed without that second line.

Now it is possible to see the directory “foo” and access to the index.html but…only if you are just logged in. If you are not logged in you will be redirect to a 401.html page redirected again to the index.php file (only a little debug system can show this to you); so the login form is not displayed, and this is a big problem.

In order to force the login form, it is sufficient to add this last line in the “foo/.htaccess” file:

AuthName "Restricted Area"AuthType Basicrequire valid-userAuthUserFile "/home/foobar/passwd"ErrorDocument 401 default

Now the login form is forced and you can use it without problems.

]]>
/blog/2008/09/apache-mod_rewrite-and-directories-protected-by-password/feed/ 0
Preview di un testo con una sola riga di Python /blog/2007/06/preview-di-un-testo-con-una-sola-riga-di-python/ /blog/2007/06/preview-di-un-testo-con-una-sola-riga-di-python/#comments Sat, 09 Jun 2007 16:38:00 +0000 Davide Muzzarelli /blog/2007/06/preview-di-un-testo-con-una-sola-riga-di-python/ Nei siti web capita spesso di mostrare una preview di una news o di un articolo. Queste preview includono solo una parte del testo per poter elencare più articoli o news nella stessa pagina senza appesantire troppo la navigazione.

A tal proposito ecco una funzione di una sola riga che prende un testo, lo accorcia ed aggiunge i puntini di sospensione alla fine:

def extract_short_description(text, len=25): return ' '.join(text[:len].split()[:-1]) + '...'

text[:len]” prende il testo e lo taglia fino alla lunghezza massima indicata (in questo caso sono 25 caratteri di default).
.split()” divide il testo in singole parole creando una lista.
[:-1]” elimina l’ultima parola perché probabilmente è stata tagliata.
‘ ‘.join(…)” unisce le parole in una stringa immettendo uno spazio tra di esse.
Infine “+ ‘…’” aggiunge i puntini finali :)

Spesso ciò che in altri linguaggi è richiede diverse righe di codice Python lo esprime in poche pur mantenendo la leggibilità allo stesso livello.

]]>
/blog/2007/06/preview-di-un-testo-con-una-sola-riga-di-python/feed/ 2
Duplicare un sistema di directory – alla faccia di TreeCopy /blog/2007/03/duplicare-un-sistema-di-directory-alla-faccia-di-treecopy/ /blog/2007/03/duplicare-un-sistema-di-directory-alla-faccia-di-treecopy/#comments Fri, 02 Mar 2007 13:25:00 +0000 Davide Muzzarelli /blog/2007/03/duplicare-un-sistema-di-directory-alla-faccia-di-treecopy/ Oggi è recensito su Punto Informatico un piccolo programma per Windows che copia un sistema di cartelle in un’altra posizione.

Punto Informatico ne motiva l’uso:
“Può capitare di ritrovarsi (specie a chi classifica in modo molto preciso i propri file) con una struttura molto complessa, ramificata ed annidata… che si vorrebbe magari replicare su un altro disco, magari perché ha richiesto tempo, fatica e tanti piccoli aggiustamenti. Questa utility ad interfaccia grafica a doppio pannello affiancato permette di eseguire questa operazione in modo estremamente semplice.”

Con GNU/Linux è possibile fare questo da anni in maniera ancora più veloce e semplice, non serve scaricare nulla e funziona con tutte le versioni.
E’ sufficiente posizionarsi al di fuori della directory da copiare e dare il seguente comando:

find directory_da_replicare -type d | xargs -I $ mkdir -p nuova_posizione/$
find directory_da_replicare -type d

ottiene l’elenco delle directory da copiare, mentre

xargs -I $ mkdir -p nuova_posizione/$

esegue la replicazione nella nuova posizione.
Per farlo funzionare basta sostituire directory_da_replicare con il nome della directory base, e “nuova_posizione” con il percorso dove andrà posizionata la replica.

Per comodità il comando può essere messo in un file ed eseguito come script.

C’era davvero bisogno di scrivere un programma come TreeCopy? ;)

]]>
/blog/2007/03/duplicare-un-sistema-di-directory-alla-faccia-di-treecopy/feed/ 2
Centrare orizzontalmente le pagine web /blog/2007/01/centrare-orizzontalmente-le-pagine-web/ /blog/2007/01/centrare-orizzontalmente-le-pagine-web/#comments Sat, 27 Jan 2007 17:15:00 +0000 Davide Muzzarelli /blog/2007/01/centrare-orizzontalmente-le-pagine-web/ Ecco pronto un metodo veloce per centrare le pagine nei siti web.

Questo è il file HTML:

<home>    <head>        <link rel="stylesheet" type="text/css" href="default.css" />    </head>    <body>        <div id="container">            <!-- metti tutto il contenuto qui -->        </div>    </body></html>

Questo è invece il contenuto del file CSS:

body {    text-align: center   /* centra in IE5.x */}

div#container {    width: 760px;    margin: 0 auto;   /* centra negli altri browser */    text-align: left;   /* ripristina l'allineamento */ }

Si utilizza il BODY per centrare il contenuto, questo metodo funziona su IE5.

La larghezza della pagina è definita a 760px che è quasi uno standard per la risoluzione 800×600 in quanto tiene conto della barra di scorrimento laterale e dei margini della finestra.

Il trucco per centrare la pagina è presto spiegato: si applica semplicemente un margine automatico ai lati del DIV in modo che il browser mantenga lo stesso margine da entrambi i lati. Questo metodo funziona anche ridimensionando la finestra senza l’ausilio di JavaScript.

L’attributo “text-aling: left” nel DIV serve a ripristinare l’allineamento corretto del testo.

]]>
/blog/2007/01/centrare-orizzontalmente-le-pagine-web/feed/ 1