Ruby Social Club, Milano – Atto terzo: missione compiuta

Anche se con un po’ di ritardo, ci tenevo a esprimere la mia soddisfazione per la bella serata che è stata organizzata mercoledì scorso in quel di Milano dai ragazzi di Mikamai per il terzo Ruby Social Club. Una trentina di persone (successone), atmosfera molto informale, talk veloci ma interessanti e una transumata di massa al locale dove sono continuate le discussioni sugli argomenti più disparati tra birra e qualche piatto di cibarie. Purtroppo non sono mai riuscito a partecipare alle precedenti edizioni e devo dire che questa volta è valsa la pena fare di tutto per esserci. Il buon riffraff (a cui tra l’altro devo anche una birra, lo scrivo qui così rimane fino alla prossima occasione) ha già scritto qualche dettaglio sparso sulla serata, per cui non mi dilungherò ulteriormente. Sicuramente ci saranno altri appuntamenti in futuro, per cui… al prossimo RSC :-)

Sinatra has taken the stage!

Non si tratta del compianto Frank, ma di uno degli ultimi framework per lo sviluppo web arrivati in casa Ruby. La caratteristica principale di Sinatra è la sua semplicità, ancora più spinta rispetto a quella a volte anche un po’ troppo magica di Camping: con sole 5 righe è possibile ottenere una risposta dal server. Non ci credete? Allora, dopo averlo installato usando rubygems (gem install sinatra -y), provate il seguente codice…

# main.rb require 'rubygems' require 'sinatra' get '/' do 'Cool, Sinatra is performing on the main stage!' end

Usando il comando ruby main.rb si ottiene un’istanza di Mongrel con la nostra applicazione pronta a ricevere le chiamate dal browser, mentre nella finestra del terminale si viene avvisati che Sinatra has taken the stage on port 4567! Non resta da far altro che verificare l’effettivo funzionamento con un semplice wget -qO- http://localhost:4567/ (oppure curl http://localhost:4567/ se preferite):

Cool, Sinatra is performing on the main stage!

Sinatra può essere definito un micro-framework traverstito da DSL dotato di una grammatica ridotta, semplice e immediata. Sinatra non obbliga lo sviluppatore a strutturare la propria applicazione seguendo l’architettura MVC (ma è comunque possibile replicarla), non è legato a un particolare motore di template (ma supporta molto bene ERB e l’ottimo HAML), non offre la miriade di helpers inclusi nel prezzo come altri framework e inoltre è assolutamente ORM-agnostico: tutto ciò può avere i suoi pro e contro, ma queste caratteristiche lo rendono indubbiamente un ottimo framework per lo sviluppo di prototipi o di applicazioni semplici ma relativamente veloci. Sinatra, per come è strutturato, promuove inoltre lo sviluppo di interfacce REST per le proprie applicazioni web, rendendolo quindi uno strumento agevole per prototipizzare API.

get '/' do # mostra risorsa end post '/' do # crea risorsa end put '/' do # aggiorna risorsa end delete '/' do # cancella risorsa end

Viene naturale a questo punto chiedersi come vengano gestite le rotte, ma anche in questo caso è tutto molto semplice e intuitivo:

get '/hello' do redirect '/hello/anonymous' end get '/hello/anonymous' do 'Hello Mr. Anonymous, I would be glad to know your name!' end get '/hello/:name' do user = params[:name] "Hello, #{user}!" end # wget -qO- http://localhost:4567/hello # Hello Mr. Anonymous, I would be glad to know your name! # wget -qO- http://localhost:4567/hello/anonymous # Hello Mr. Anonymous, I would be glad to know your name! # wget -qO- http://localhost:4567/hello/NRK # Hello, NRK!

Occorre solamente far notare che Sinatra effettua il lookup delle rotte nell’ordine di definizione, per questo motivo la seconda rotta /hello/anonymous non viene catturata dalla terza che invece specifica un più generico /hello/:name. Concentrandosi appunto su quest’ultima, possiamo notare come più in generale le variabili all’interno di una rotta possano essere definite semplicemente sfruttando la stessa sintassi utilizzata per definire un simbolo in Ruby e come esse vengano automaticamente rese disponibili nell’hash params.

Se avessimo bisogno di un approccio più in stile MVC? Partendo dal presupposto che con Sinatra non serve creare una classe controller ma basta definire le singole azioni, a questo punto mancano solo le viste. Occorre quindi creare una directory views nel path della nostra applicazione, preparare il proprio template salvando il file relativo nella suddetta directory (per esempio greet.haml oppure greet.erb a seconda di quale motore di template vogliate usare) e scrivere una semplice riga di codice per la rotta:

get '/hello/:name' do haml :greet end

Per convenzione Sinatra effettua l’autocompletamento del nome del file della vista aggiungendo .haml o .erb, per cui basta specificarne il nome. Se avessi voluto utilizzare erb avrei scritto erb :greet ma dal momento che haml tutto sommato mi piace (gem install haml), ecco qui il codice d’esempio assolutamente minimale:

!!! %html %head %title Greetings! %body %span Hello, %strong= params[:name]

A questo punto la chiamata a http://localhost:4567/hello/NRK produrrà il seguente output:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Greetings!</title> </head> <body> <span> Hello, <strong>NRK</strong> </span> </body> </html>

Tutto molto semplice, no?

Sinatra è in una fase di sviluppo abbastanza attiva ma è un framework già particolarmente interessante per tutte quelle necessità in cui Ruby on Rails oppure framework similari risulterebbero soluzioni spropositate. Essendo basato su Rack è possibile servire le applicazioni usando server differenti da Mongrel, come per esempio Thin o Phusion Passenger via Apache. Personalmente lo sto provando nei ritagli di tempo per creare una sorta di gateway da un servizio JSON-RPC che avevo realizzato qualche tempo fa a un servizio REST-oriented con tanto di interfaccia web per la gestione delle azioni e la visualizzazione dei dati, per ora sono molto soddisfatto della sua semplicità.

Ping? Pong! Spizzichi di Chrome, virtual machine, io, telecomandi e ruby

Per esserci ci sono… sì insomma, esisto ancora. Il fatto è che non avevo alcuna voglia di scrivere sul blog e mi scuso per questo, purtroppo però non riuscirò mai a rientrare nella logica blogorroica e spesso prettamente italica secondo cui l’assenza di un flusso costante di messaggi, possibilmente con cadenza giornaliera e magari anche quando da scrivere non si avrebbe proprio nulla, equivale a essere marchiati a fuoco come bloggher un po’ scarsi. Oddio in effetti un po’ scarso lo sono in questo senso (oso addirittura fregarmene delle statistiche!), però il fatto di non aver scritto per più di un mese non mi stupisce troppo dal momento che periodi simili mi sono capitati anche in passato in quel di usenet, quando ci bazzicavo una decina di anni fa. Tuttavia, in passato come oggi, il risultato finale è sempre lo stesso: rieccomi. Ah beh, che culo!

Digressioni & deliri messi da parte, ci terrei a confessarvi di avere una discreta quantità di argomenti utili e interessanti (spero) su cui scrivere, mi auguro di avere il tempo materiale per riuscire nell’impresa, prima però preferirei seguire un percorso riabilitativo al posting abbastanza dolce per cui… ecco il temibile tag <ul/>, una bella unordered list per voi:

  • Chrome. Google è un fenomeno del marketing, me lo fa pensare il fatto che tutti stiano andando pazzi per un browser che fondamentalmente… crasha. Ehi, però crashano i singoli tab, mica tutto il browser: accidenti scusate, allora è tutto ok posso unirmi al delirio generalizzato! :-) Ironie e crash a parte, il browser è veloce e in effetti v8 è una scheggia, magari non sempre sovraumana in tutte le circostanze come vorrebbero far credere con i benchmark studiati ad hoc ma si tratta comunque dell’engine ECMAScript/Javascript più performante attualmente in circolazione, lo sta dimostrando con dei fatti. Il browser in sè potrebbe non essere malvagio, ma magari ci rivedremo più avanti quando Google l’avrà sistemato ancora un po’, avrà fatto uscire qualcosa di compilato anche per le altre piattaforme e magari, già che sono in ballo, avrà smesso di fare presunti pasticci con l’EULA. Per ora sulla mia macchina virtuale linux di sviluppo sono rimasti solo i sorgenti di v8 e il risultato della loro compilazione, mi interessano molto di più di tutto il resto.
  • A proposito di macchine virtuali, ho acquistato una VPS linux con SliceHost scegliendo il loro piano di base da 20$/mese. In seguito ho registrato un nuovo nome di dominio con Aruba (mi tornava comodo solo per la registrazione, infatti ho provveduto a cambiare immediatamente i nameserver autoritativi con quelli di SliceHost appena è stato possibile) e infine per gestire la posta ho creato un account di Google Apps sottoscrivendo il profilo standard nonché gratuito. Livello di soddisfazione: elevato, per ora. Ho diverse idee in testa, tra le varie figura anche la migrazione di questo blog (come del resto mi ripropongo da almeno due anni), ma ogni cosa avverrà a tempo debito.
  • Scusate se non ve l’ho mai detto, ma io è un linguaggio di programmazione grandioso. Dinamico, basato sulla prototipizzazione degli oggetti come Javascript, interamente orientato ai messaggi, totalmente privo di keywords, dannatamente curioso e divertente da scoprire. Ok è molto giovane, ci sono pochissime librerie e la documentazione non è propriamente aggiornata con lo stato dello sviluppo, inoltre è probabile che non sarà nemmeno la next big thing in programming (anche se non si sa mai), però vi assicuro che è un linguaggio molto divertente.
  • L’altro giorno ho acquistato il gadget allegato all’ultimo numero di Win Magazine (mai comprato in vita mia, sia chiaro): un telecomando IR per PC e Media Center, completo ovviamente di ricevitore USB. Semplice, sottile, 15€… ne volevo uno da tempo, ma non lo desideravo abbastanza intensamente da giustificare una spesa da 30€ o più. Peccato però che non sembri andare molto d’accordo con Zoom Player (da anni il mio player video per Windows preferito) per quanto riguarda la mappatura dei comandi. Fortunatamente però ZP è grande e può essere remotato in maniera piuttosto banale e documentata via TCP/IP oppure usando l’API SendMessage di Windows, per cui ho pensato: perché non sviluppare un’applicazione residente in background che ascolti i comandi provenienti dal mio bel telecomando e li traduca in comandi da inviare a ZP, mappando il tutto come diamine voglio io? Ci sto lavorando in C#, anche se il core è più lavoro di interazione con le API Win32 che altro. Qualche messaggio sull’argomento approderà anche su questi lidi.
  • Dalla saga del telecomando è scaturito quasi per caso un altro piccolo progetto, questa volta più generico e potenzialmente più utile anche ad altri. L’idea inziale per l’applicazione di cui vi ho accennato poco sopra prevedeva l’utilizzo integrale di IronRuby per la sua realizzazione, tuttavia mi sono accorto presto che le sue capacità di interop sono ancora particolarmente limitate per diversi aspetti (ecco perché ho scelto di usare C#). In compenso il codice che ho scritto per le prove mi ha portato all’idea di effettuare un porting per IronRuby del modulo Win32API distribuito nella standard lib dell’MRI, il tutto in onore di una sana compatibility-love. Dopo 3 giorni avevo già una prima versione funzionante come testimoniato da questo gist, ora inizia il lavoro di pulizia e ottimizzazioni che si concluderanno con il rilascio del tutto su GitHub e con una serie di post sul blog dedicati all’argomento dal momento che ci sono svariate considerazioni tecniche che potrebbero risultare particolarmente interessanti.

Per ora è tutto. Per ora…

IronRuby sempre più vicino a Ruby on Rails

Se non è in grado di far girare Ruby on Rails, allora non è Ruby… più o meno con queste parole John Lam, agli inizi dello sviluppo di IronRuby, aveva sottolineato la volontà di ottenere un’implementazione del linguaggio che fosse il più fedele possibile all’MRI (l’interprete originale di Matz). Oggi, con questo suo tweet, arriva la prima dimostrazione pratica di come questo obiettivo sia ormai sempre più vicino. Ovviamente manca ancora parecchio lavoro per completare IronRuby e attualmente il team è dedicato interamente a implementare e sistemare tutte le parti necessarie a poter effettuare l’hosting di una semplice applicazione RoR giusto in tempo per la RailsConf 2008 che avrà inizio tra pochi giorni, ma il ritmo sembra ormai molto buono. E’ interessante far notare che negli ultimi due mesi il lavoro del team di IronRuby si è fatto molto più trasparente in seguito a una forte critica sviluppatasi direttamente sulla mailing list, in cui si faceva notare come l’assenza di aggiornamenti sullo stato dei lavori e i commit in SVN abbastanza rari rendevano il lavoro di contribuzione e di testing molto difficile. La discussione ha generato un dialogo che ha portato a commit decisamente più frequenti, post dei diff sulla mailing list e review del codice e dei nuovi sviluppi… e la comunità che si sta formando intorno ad IronRuby ha apprezzato. Personalmente in queste ultime due settimane di silenzio sul blog ho avuto modo, tra le varie attività, di approfondire più seriamente gli internals di IronRuby provando a implementare qualche funzionalità mancante e devo ammettere che il suo codice e la struttura generale del progetto si sono dimostrati dannatamente ottimi e relativamente facili da capire nonostante non sia ancora esistente documentazione pubblica in merito (sarebbe stata inutile dal momento che il core ha cominciato a stabilizzarsi solo di recente). Se nel frattempo io mi sono divertito con metodi semplici come Kernel#rand e Kernel#srand per poi passare a Kernel#system con relativa classe Process::Status per cominciare a prendere familiarità con il progetto, qualcuno si è spinto ben oltre iniziando a ricreare un’implementazione di Shoes su IronRuby basandosi sulle API .NET di Windows Forms… potremmo chiamarlo IronShoes? Insomma ad oggi sono fortemente ottimista riguardo al futuro di IronRuby soprattutto visti gli ultimi sviluppi. Sono ottimista anche per la situazione di Ruby in generale perché stanno arrivando segnali molto interessanti dalla comunità, ma preferisco parlare di questo in un post successivo.

Interagire con Windows tramite Ruby

Oggi stavo effettuando alcune ricerche su internet quando sono incappato in un mio messaggio inviato qualche mese fa sulla mailing list di ruby-it in risposta a chi chiedeva aiuto su come poter recuperare l’elenco delle unità montate su un sistema Windows. Ruby ormai offre da tempo un supporto particolarmente maturo all’interazione con il sistema su piattaforme Windows tramite Win32OLE e alle svariate librerie del progetto Win32 Utils. In questo caso, a dimostrazione della flessibilità delle soluzioni offerte da Ruby, lo stesso task può essere risolto in tre modi molto differenti tra loro.

Il primo approccio sfruttando il modulo Windows::Volume delle Win32 Utils consiste nell’utilizzare in maniera diretta le API di Windows e la sua implementazione, anche se può apparire grezza e magari non troppo elegante agli occhi dello sviluppatore Ruby puro, è comunque piuttosto facile:

require 'windows/volume' include Windows::Volume def get_drive_letters buffer = " " * 1024 length = GetLogicalDriveStrings(buffer.length, buffer) buffer[0..length].split(0.chr) end p get_drive_letters # => ["A:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\"]

Utilizzando GetLogicalDriveStrings si ottiene una lista di tutte le unità logiche montate nel sistema, indipendentemente dalla loro natura fisica, il tutto sotto forma di buffer contenente l’elenco di lettere di unità separate dal carattere NULL. Questo ci costringe a istanziare un buffer e a estrarne il contenuto con cui è stato riempito dalla funzione API in una maniera abbastanza lontana dal tipico stile di programmazione in Ruby, ma è pur sempre fattibile e tutto sommato non è poi così brutto a vedersi, c’è di peggio in altri linguaggi.

In Windows esistono poi gli oggetti COM messi a disposizione dalla Scripting Runtime Library e uno di questi, tra l’altro forse tra i più utilizzati, è FileSystemObject. Sinceramente sentire questo nome mi fa venire gli incubi tornare in mente a quando svariati anni fa mi è toccato usare ASP 3.0 che, per la cronaca, non ho mai sopportato. Il fatto di non dover usare le API Win32 salendo quindi di livello ci permette di utilizzare semplicemente Win32OLE:

require 'win32ole' def get_drive_letters drives = [] fso = WIN32OLE.new('Scripting.FileSystemObject') fso.drives.each { |drive| drives << "#{drive.driveletter}:\\" } drives end p get_drive_letters # => ["A:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\"]

L’astrazione messa a disposizione da Win32OLE per le collection restituite da oggetti COM include automaticamente un metodo each che ci permette di iterare in maniera familiare e comoda il risultato dalla proprietà Drives dell’istanza di FSO.

Esiste infine la possibilità di utilizzare l’interfaccia messa a disposizione da WMI (Windows Management Instrumentation) per ottenere le stesse informazioni in maniera ancora più intuitiva con un approccio molto SQL-like nell’interrogazione delle informazioni sui sistemi Windows:

require 'win32ole' def get_drive_letters drives = [] wmi = WIN32OLE.connect('winmgmts://') devices = wmi.ExecQuery('SELECT * FROM Win32_LogicalDisk') devices.each { |drive| drives << "#{drive.DeviceId}\\" } drives end p get_drive_letters # => ["A:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\"]

Interrogando la classe WMI Win32_LogicalDisk si può ottenere rapidamente una nutrita serie di informazioni risparmiandosi un sacco di giri o chiamate, delegando tutto al sistema operativo. Inoltre è possibile scremare già in fase di richiesta e molto facilmente il set di risultati desiderato, del resto come abbiamo visto le query WMI ricordano molto da vicino SQL. Per fare un esempio, cambiando la nostra query in SELECT * FROM Win32_LogicalDisk WHERE FileSystem = ‘NTFS’ è possibile ottenere l’elenco di tutte le unità il cui filesystem è NTFS. Un altro vantaggio non indifferente è quello di poter richiedere le stesse informazioni a computer remoti semplicemente cambiando la stringa di connessione al provider WMI, per esempio utilizzando una stringa di connessione come winmgmts:{impersonationLevel=impersonate}!\\192.168.1.2\root\cimv2 (in un dominio AD può tornare molto comodo per tenere sotto controllo le proprie installazioni).

E’ possibile fare tutto e in molte maniere, ce ne è per tutti i gusti. Il vantaggio di poter interagire con Windows attraverso Ruby anziché con i soliti VBScript e JScript risiede prima di tutto nella sua estrema flessibilità rispetto ai suddetti linguaggi di scripting rivali forniti di serie nel sistema operativo, ma anche nella possibilità di sfruttare nei propri script di automazione tutte le classi standard e di terze parti che gravitano intorno a Ruby è un vantaggio da non sottovalutare nello sviluppo di task amministrativi più complessi. Ok posso percepire anche coloro che ci terrebbero a sottolineare come sia possibile fare le stesse cose con Perl e Python… sì, avete perfettamente ragione, ma qui si pubblicizza Ruby ;-P