Davide Muzzarelli » * livello avanzato /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
Two template filters for Django /blog/2012/01/two-template-filters-for-django/ /blog/2012/01/two-template-filters-for-django/#comments Fri, 20 Jan 2012 11:46:23 +0000 Davide Muzzarelli /blog/?p=254 I want to signal two new template filters for Django.

Precise truncate words by chars

It truncates the text when it exceeds a certain number of characters and deletes the last word. Adds ‘…’ at the end of the text, only if truncated.

The difference between other similar filters is that this deletes the last word only if partial, so it is more precise and pleasant.

The snippet is here.

Divide a list into exact columns

It divides a list into an exact number of columns.

The difference between other similar filters it that the number of columns is guaranteed, even if there are not enough elements to fill all the columns.

The snippet is here.

]]>
/blog/2012/01/two-template-filters-for-django/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
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
Python client for the Nirvanix API /blog/2008/07/python-client-for-the-nirvanix-api/ /blog/2008/07/python-client-for-the-nirvanix-api/#comments Tue, 29 Jul 2008 20:46:00 +0000 Davide Muzzarelli /blog/2008/07/python-client-for-the-nirvanix-api/ I released a Python client for Nirvanix.

The project page is:
https://www.hosted-projects.com/trac/Design/pub/wiki/Nirvanapi

It include the most important API calls, is light (one file and less of 400 lines of code) and have not dependences.

The client support right now the following API calls:

LoginLogoutCreateFoldersDeleteFoldersDeleteFilesCopyFilesCopyFoldersMoveFilesMoveFoldersRenameFileRenameFolderSideuploadListFolderCreateHostedItemExtractFrames

The login is automatic also when the connection drop.

List a directory into Nirvanix in a little example:

from nirvanapi import NirvanixClientnc = NirvanixClient('XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 'username', 'password')print nc.list_folder('/')
]]>
/blog/2008/07/python-client-for-the-nirvanix-api/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
Sviluppo su IE, errore numero 80004004 /blog/2006/12/sviluppo-su-ie-errore-numero-80004004/ /blog/2006/12/sviluppo-su-ie-errore-numero-80004004/#comments Tue, 12 Dec 2006 17:50:00 +0000 Davide Muzzarelli /blog/2006/12/sviluppo-su-ie-errore-numero-80004004/ Come sempre Internet Explorer tortura sviluppatori e utenti fino all’ultimo con messaggi incomprensibili ed errori senza senso.

Questa è la volta dell’errore numero 80004004, la documentazione della Microsoft è carente sulle cause anche perché questo errore sembra essere attribuibile a più tipi di problema.

Il bug affligge le seguenti versioni di Internet Explorer, che sono praticamente tutte quelle degli ultimi dieci anni e passa:

  • Microsoft Internet Explorer 3.0
  • Microsoft Internet Explorer 3.01
  • Microsoft Internet Explorer 3.02
  • Microsoft Internet Explorer 4.0 128-Bit Edition
  • Microsoft Internet Explorer 4.01 Service Pack 2
  • Microsoft Internet Explorer 4.01 Service Pack 1
  • Microsoft Internet Explorer 4.01 Service Pack 2
  • Microsoft Internet Explorer 5.0
  • Microsoft Internet Explorer 5.01
  • Microsoft Internet Explorer 5.5
  • Microsoft Visual Basic, Scripting Edition 4.0
  • Microsoft Visual Basic, Scripting Edition 5.0
  • Microsoft JScript 4.0
  • Microsoft JScript 5.0
  • Microsoft Visual Basic 6.0 Learning Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition Service Pack 3

A volte la Microsoft lo attribuisce a Visual Basic, altre volte al suo Java Script, altre volte ad ActiveX; io credo invece che il problema riguardi una libreria in comune tra questi linguaggi.

In particolare, nello sviluppo web, il problema nasce dal fatto di cercare un elemento contenuto in una tabella o in un DIV nello stesso punto in cui si trova questo elemento.

L’errore viene generato con uno script del genere:

...<table>    <tr>        <td>            <div id="foo" class="bar">foobar</div>            <script language="javascript">                var obj = document.getElementById('foo');                var classes = obj.getAttribute('class');            </script>        </td>    </tr>...

Come si può vedere lo script è contenuto ed eseguito all’interno della stessa cella e tabella dell’elemento da cercare, questo solleva l’errore.

Attenzione a chi usa le API di Google, in particolare Google Maps, molte persone hanno riscontrato lo stesso errore senza aver trovato una soluzione (quella che ho appena suggerito). E’ per tale problema che Google consiglia di caricare gli script con un <body onload=”…”>, anche se in alcuni casi questo non è proprio possibile.

Per risolvere è sufficiente lasciare chiudere la tabella eseguendo lo script al termine della pagina. Nel caso in cui non si possa trovare un’alternativa il problema ricade ancora una volta sulle spalle dello sviluppatore che dovrà eliminare una funzionalità del proprio sito a svantaggio dell’utente. Grazie Microsoft…

PS: in alcuni casi l’errore non viene segnalato esplicitamente da Internet Explorer, bisogna provare con un’altra versione del browser o da un’altra macchina.

]]>
/blog/2006/12/sviluppo-su-ie-errore-numero-80004004/feed/ 8
Convertire l’HTML in HTML sicuro /blog/2006/11/convertire-lhtml-in-html-sicuro/ /blog/2006/11/convertire-lhtml-in-html-sicuro/#comments Thu, 30 Nov 2006 18:50:00 +0000 Davide Muzzarelli /blog/2006/11/convertire-lhtml-in-html-sicuro/ Scrivendo su di un blog tecnico capita spesso di aver codice da mostrare. In particolare, quando il suddetto codice proprio HTML bisogna provvedere a convertire alcuni caratteri perché altrimenti il browser, riconoscendolo, lo valuta.

In python sono sufficienti pochi comandi per effettuare la conversione:

# legge il file "from.txt" contenente il testo da convertiretext = file('from.txt', 'r').read()

# converte il fileimport cgitext = cgi.escape(text) # converte i caratteritext = text.expandtabs(4) # trasforma i tab in spazi

# salva il testo nel file "to.txt"file('to.txt', 'w').write(text)

Il testo ottenuto può essere così inserito nel proprio blog, o articolo, delimitandolo adeguatamente:

<pre class="code">...inserire il codice qui...</pre>

Per comodità ho già preparato un programma che effettua la conversione da un file all’altro. Le istruzioni possono essere ottenute digitando python html2html -h dalla console.

Il file è il seguente, da eseguire come qualsiasi altro script:

== FILE: html2html.py ==#! /usr/bin/python

__version__ = "1.0"__author__ = "Davide Muzzarelli (info@dav-muz.net)"__copyright__ = "(C) 2006 Davide Muzzarelli. GNU GPL 2."

import sys, cgi

if '-h' in sys.argv or '--help' in sys.argv: print """Usage: html2html SOURCE DEST

html2html.py --v1.0-- Convert a text in order to be used in a HTML page, useful for code postings.

Copyright (c) Davide Muzzarelli, 2006""" sys.exit(0)else: try:     from_file = sys.argv[1]     to_file = sys.argv[2] except:     print "Error: specify the source file and the destination file\nFor more informations please use the -h option."     sys.exit(0)

 # load text = file(from_file, 'r').read()

 # conversion text = cgi.escape(text) text = text.expandtabs(4)

 # save file(to_file, 'w').write(text)
]]>
/blog/2006/11/convertire-lhtml-in-html-sicuro/feed/ 1
Gestire i file di configurazione con SVN /blog/2006/11/gestire-i-file-di-configurazione-con-svn/ /blog/2006/11/gestire-i-file-di-configurazione-con-svn/#comments Sat, 11 Nov 2006 18:42:00 +0000 Davide Muzzarelli /blog/2006/11/gestire-i-file-di-configurazione-con-svn/ I progetti software hanno quasi sempre uno o più file di configurazione che sono personalizzati in base al contesto. Ogni sviluppatore può avere un file di configurazione personale e anche i server di test e di produzione ne hanno uno creato apposta.

Avendo un sistema di repository, come Subversion (aka: SNV), i file di configurazione sono gestiti come normali file e quindi inviati nella fase di commit. Ogni contesto (copia di lavoro o server) ha una sua personalizzazione del file che verrebbe sovrascritta dall’ultimo commit effettuato. Questo è un problema comune in quanto le configurazioni sono continuamente riscritte e sovrascritte, dovendo essere corrette ogni qual volta che si aggiorna il codice, il che comporta parecchio lavoro in più.

Si può risolvere il problema utilizzando l’opzione svn:ignore di SVN che permette di ignorare i file indicati, in questo caso i file di configurazione. Per mantenere comunque una base dei file ignorati è sufficiente creare dei i file di configurazione generici aggiungendo un’estensione alla fine.

Nel caso di esempio, in cui ho un file di nome config.ini, creo una copia con:

svn mv config.ini config.ini.tmpl

Con questo comando ho rinominato il file mantenendone intatta la storia delle precedenti revisioni. Ho scelto di aggiungere l’estensione tmpl per indicare che il file è un template (o modello) dell’originale, ma ognuno può utilizzare l’estensione o il nome che preferisce.

Per ignorare il file mi sposto all’interno della directory dove risiede e lancio il seguente comando:

svn propedit svn:ignore .

Questo comando mi permette di modificare la lista dei file da ignorare per la directory in cui mi trovo ( . ). Una volta eseguito il comando si apre l’editor di testo in cui si possono inserire i nomi dei file, uno per riga. E’ anche possibile usare i caratteri wildcard, come gli asterischi, per indicare contemporaneamente più file.

Una volta fatto posso inviare le modifiche:

svn ci -m"Da ora in poi potete usare i vostri file di configurazione senza rischi!"svn up

Da questo punto in poi il file config.ini è ignorato e posso procedere con il mio lavoro: creo una semplice copia del file di template config.ini.tmpl (senza usare SVN!) dandogli il nome config.ini e lo modifico secondo i miei bisogni. Al prossimo aggiornamento il file config.ini non sarà toccato e tutti i miei collaboratori potranno avere i loro file di configurazione senza creare problemi agli altri.

Nel caso in cui si debbano inserire delle modifiche utili a tutti gli sviluppatori è sufficiente modificare il solo template e ricreare il proprio file di configurazione personalizzato.

Il sistema funziona anche con link e directory aprendo la strada a diverse possibilità.

]]>
/blog/2006/11/gestire-i-file-di-configurazione-con-svn/feed/ 0