Visualizzazione post con etichetta iife. Mostra tutti i post
Visualizzazione post con etichetta iife. Mostra tutti i post

martedì, dicembre 02, 2014

Esercitazioni su node.js: esercizio 6

Ora i duri cominciano a giocare.

Testo dell'esercizio
Scrivere un programma per effettuare una richiesta HTTP di tipo GET ad un URL passato al programma come primo parametro dalla linea di comando.
Scrivere sulla console il contenuto testuale della rispota HTTP.

Suggerimenti
Per questo esercizio utilizzerete il modulo http, in particolare il metodo get(), presente nella distribuzione di base: la documentazione la potete trovare a questo link

La funzione di callback è di questo tipo function callback(response) ... dove response è un oggetto che rappresenta uno Stream in node.js: prossimamente approfondiremo il concetto di Stream, per ora ci basti sapere che uno Stream è un flusso di dati che puo' emettere particolari eventi gestibili da nostre funzioni di callback.
Gli eventi piu' importanti da gestire sono "data" , "error" e "end".
Ad esempio response.on("data", function(data) {}) ci permette di processare parzialmente i dati della risposta, non appena disponibili, in maniera asincrona: l'oggetto response puo' emettere più eventi "data" a seconda della dimensione della risposta effettiva del server.

L'oggetto response ha anche un metodo setEncoding(...) che ci permette di trattare il parametro data, ricevuto dalla funzione di callback degli eventi emessi dall'oggetto responsedirettamente come una stringa nella codifica scelta (usate 'utf-8'). 

Buon Lavoro!

Una possibile soluzione
Più semplice del previsto, grazie ai moduli a disposizione della piattaforma node.js
 var http = require('http');  
   
 http.get(process.argv[2], function (response) {  
  response.setEncoding('utf8');  
  response.on('data', console.log);  
  response.on('error', console.log);
  response.on('end', function() {console.log("FINITO ... RICHIESTA CONCLUSA");});
 });  
Invochiamo il metodo get del modulo http passando come parametro la funzione di callback che gestisce la risposta.

La risposta è un oggetto che rappresenta uno stream di dati che emette eventi che hanno un nome: data,error, end.

L'evento data viene emesso ogni volta che c'è un blocco di dati disponibile per l'elaborazione, (ricordate che ora stiamo programmando in maniera asincrona).

L'evento error viene emesso ogni qual volta c'è un errore nella comunicazione con il server.
L'evento end viene emesso quando la richiesta è conclusa.

Notate che il secondo parametro del metodo on, nel caso di evento data ed error, deve essere una funzione che accetterà come parametri i dati effettivi ottenuti dal server: possiamo direttamente passare la funzione console.log che si occuperà di stampare a video i dati o i messaggi di errore.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Come al solito, per dubbi e/o domande: commentate!

Alla prox.
Ivan



Impariamo a definire i nostri ravioli .. ehm ... moduli in node.js

Node.js è stato disegnato all'insegno della modularità.
Abbiamo già notato come esista una pletora di moduli utilizzabili da un programma node.js:
basta utilizzare l'istruzione require('nomemodulo') per utilizzare le funzionalità esportate dal modulo.

La piattaforma provvede una serie di moduli inclusi nella distribuzione di base, abbiamo visto ad esempio l'utilizzo del modulo fs o del modulo path

In questo post impariamo a definire i nostri moduli da utilizzare nei nostri programmi, per cercare di programmare utilizzando ravioli e non spaghetti

Una volta definito un modulo, è anche possibile pubblicarlo sul repository ufficiale dei moduli gestiti da npm.

Date un'occhiata alla lista ufficiale di moduli per node disponibili attualmente.

Se vi piace un modulo, basterà eseguire in una shell il comando npm  <nomemodulo> nella cartella radice del progetto che state sviluppando, per poterlo utilizzare con una istruzione di require('nomemodulo') dal vostro codice.

Ad esempio, supponete di aver bisogno della funzionalità di trasformazione dei vostri oggetti json in formato xml: basterà utilizzare il modulo di terze parti che si chiama xml registrato sul repository ufficiale.

Vi posizionate nella cartella del vostro progetto e digitate npm install xml: verrà creata una directory node_modules, all'interno del vostro progettoche contiene i sorgenti del modulo, se un modulo dipende da altri moduli, questi verranno a loro volta scaricati in locale, tutto in maniera semplice e trasparente.

Ogni modulo ufficiale ha una pagina di documentazione come questa in cui ci sono le informazioni su come utilizzarlo.

In questo caso basta scrivere del codice di questo tipo
 var xml = require('xml');  
   
 var xmlString = xml({libri :   
      [  
           {  
                libro: [  
                     {titolo : 'Il signore degli anelli'},   
                     {autore : 'Tolkien'}  
                ],  
           },  
           {  
                libro: [  
                     {titolo: 'Guerra e pace'},  
                     {autore: 'Tolstoj'}  
                ]  
           }  
      ]  
 }, true);  
   
 console.log(xmlString);  
Per ottenere sulla console
 <libri>  
   <libro>  
     <titolo>Il signore degli anelli</titolo>  
     <autore>Tolkien</autore>  
   </libro>  
   <libro>  
     <titolo>Guerra e pace</titolo>  
     <autore>Tolstoj</autore>  
   </libro>  
 </libri>  
I singoli moduli devono essere definiti in un file, il cui nome sarà il nome del modulo, all'interno del file si deve utilizzare una istruzione del tipo module.exports = {}: assegniamo alla proprietà export dell'oggetto globale module un qualsiasi oggetto che definiremo noi, magari una iife per incapsulare dettagli implementativi mediante il meccanismo delle closure.

Facciamo un esempio.
Immaginiamo di implementare un modulo biblioteca che ci permetta di salvare in memoria dei libri e di ottenere la lista attuale dei libri.

Il programma principale utilizza il modulo biblioteca
 var biblioteca = require('./biblioteca');  
   
 biblioteca.inserisciLibro({  
      titolo : "Il signore degli anelli",  
      autore : "Tolkien"  
 });  
   
 biblioteca.inserisciLibro({  
      titolo : "Guerra e pace",  
      autore : "Tolstoj"  
 });  
   
 biblioteca.elaboraLibri(function(error, libri) {  
      if (error)  
           console.log(error.messaggio);  
      else {  
           libri.forEach(function(libro) {  
                console.log("Titolo: " +   
                          libro.titolo +   
                          "- Autore: " +   
                          libro.autore);  
           });  
      }  
 });  
Il modulo biblioteca dovrà essere implementato in un file dal nome biblioteca.js presente nella stessa cartella del file programma.js che rappresenta il programma principale.
 module.exports = (function() {  
      var libri = [],  
          inserisciLibro = function(libro) {  
             libri.push(libro);  
          },  
          elaboraLibri = function(callback) {  
             if (libri.length === 0)  
                callback({messaggio: 'biblioteca vuota'}, null);  
             else  
                callback(null, libri);       
          };  
          
          return {  
             inserisciLibro : inserisciLibro,  
             elaboraLibri : elaboraLibri  
          };  
 })();  
Notate la prima istruzione che assegna a module.exports il risultato di una funzione che viene eseguita immediatamente, tale risultato è un oggetto che espone solo i metodi inserisciLibro ed elaboraLibri.

Notate anche l'utilizzo di una funzione di callback definita in programma.js e passata come argomento a eleboraLibri, tale funzione di callback verrà eseguita solo se non ci sono condizioni di errore (in questo caso rappresentate da una biblioteca vuota).

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato questo post.
Ricordatevi, per il progetto esempio-xml, di posizionarvi nella cartella radice ed eseguire npm install xml per includere il modulo xml nel progetto.

Se avete dubbi e/o domande, commentate!

Alla prox.
Ivan


lunedì, dicembre 01, 2014

Esercitazioni su node.js: esercizio 3

Testo dell'esercizio
Scrivere un programma che utilizzi una singola operazione asincrona per leggere il contenuto di un file e stampare a video il numero di linee del file letto.
Il percorso completo del file verrà passato come primo argomento sulla linea di comando.

Suggerimenti
E' la versione asincrona dell'esercizio precedente. Dare un'occhiata a fs.readFile(...).
Riflettere su differenza programmazione sincrona vs asincrona.

Buon lavoro.

Una possibile soluzione

 var fs = require('fs'),  
     file = process.argv[2],  
     numeroLinee;  
   
     fs.readFile(file, function (err, contenutoFile) {  
        if (!err) {  
            numeroLinee = contenutoFile.toString().split('\n').length;  
            console.log(numeroLinee);  
        } else {  
            console.log("Problemi in lettura " + file);  
        }  
 });  

La lettura del file è asincrona, la funzione readFile non è bloccante.
Anche in questo caso si puo' specificare la codifica del file da leggere ed omettere l'invocazione del toString()

 var fs = require('fs'),  
    file = process.argv[2],  
    numeroLinee;  
   
    fs.readFile(file,'utf-8',function (err, contenutoFile) {  
        if (!err) {  
            numeroLinee = contenutoFile.split('\n').length;  
            console.log(numeroLinee);  
         } else {  
            console.log("Problemi in lettura " + file);  
         }  
 });  

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox.
Ivan

Esercitazioni su node.js: esercizio 1

Con questo post, comincia una serie di esercitazioni pratiche, tratte dal sito nodeschool.io, per prendere confidenza con le librerie del linguaggio.

Testo dell'esercizio
Scrivere un programma che accetti uno o più numeri come parametri da linea di comando e che stampi a video la somma di tali numeri.
Esempio di invocazione del programma: node programma.js 2 3 5 , in tal caso l'output sarà 10.

Suggerimenti
Per risolvere l'esercizio, dare un'occhiata alla documentazione ufficila sull' oggetto globale process, in particolare alla proprietà process.argv.

Esempio di utilizzo di un ciclo for in javascript

 var indice,  
   max = 10;  
   for (indice = 1; indice <= max; indice++) {  
        console.log(indice);  
   }  
          

Una possibile Soluzione
 var somma = 0,  
     i = 0;  
   
 if (process.argv.length > 2) {  
      for (i = 2; i < process.argv.length; i++) {  
           if (Number(process.argv[i])) {  
              somma += parseInt(process.argv[i]);  
           }  
           else {  
              console.log("Parametro " + process.argv[i] + " non è un numero!" );  
           }  
      }   
      console.log(somma);  
 } else {  
      console.log('Esempio di utilizzo : node programma.js 1 3 5');  
 }  

Per convertire una stringa in un intero, ho utilizzato la funzione Number che trasforma una stringa nell'intero equivalente o ritorna NaN (Not a Number) nel caso in cui la stringa non rappresenti un numero.

In javascript esistono i concetti di valore falsy e truthy per le espressioni booleane: i numeri sono considerati valori veri mentre NaN, null, undefined valori falsi: Number('ertttt') ritorna dunque NaN che nell'if viene valutato a false.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox
Ivan

lunedì, novembre 24, 2014

Grafici di ogni tipo con flotr2

Se avete la necessità di visualizzare dei grafici sulla vostra pagina, con dati che tipicamente sono generati lato server, la libreria js flotr2 è una di quelle che fa al caso vostro.

Semplice da utilizzare, permette di creare grafici di ogni tipo con poche e chiare istruzioni.

Tempo fa ho sviluppato una pagina web che mostrava su grafici di questo tipo, l'andamento dell'utilizzo della memoria e cpu di grossi server per monitorarne le prestazioni.

I dati venivano generati lato server, leggendo alcuni file csv generati giornalmente e spediti al client in formato json, così come se li aspetta flotr2.

Oggi non mi occuperò dell'integrazione con componenti lato server (ci mancano ancora un po' di strumenti) ma definirò direttamente negli script js i dati per generare i grafici.

I passi per generare i grafici sono molto semplici.
  1. Aggiungere alla vostra pagina il riferimento alla libreria flotr2.
  2. Creare un div vuoto con un identificatore univoco nella vostra pagina html
  3.  <div id="grafico"></div>  
    
  4. Invocare
  5.  Flotr.draw(container, data, options);  
    
dove
  1. Flotr è l'oggetto globale creato dalla libreria flotr2 che espone la funzione draw
  2. container: è il div in cui si vuol visualizzare il grafico, che deve avere width e height
  3. data: sono i dati espressi come array di array es: [[x0, y0], [x1,y1],[x2,y2]...];
  4. options: è un oggetto js, opzionale, per configurare al meglio le caratteristiche del grafico
I dati rappresentano dunque coppie del piano cartesiano, la singola coppia deve essere rappresentata da un array, la serie intera da un array di coppie, cioè un array di array.

Per fare un semplice esempio, tracciamo il grafico della funzione y=x*x (x al quadrato)




Pagina html minima

 <!doctype html>  
 <head>  
      <meta charset="utf-8" />  
      <title>Esempio di Utilizzo di flotr2</title>  
 </head>  
 <body>  
   <div id="container"></div>  
   <button onclick="mioPlotter.disegna('container')">Genera Grafico</button>  
   <script src="lib/flotr2.min.js"></script>  
   <script src="js/app.js"></script>  
 </body>  

codice js presente in app.js

 var mioPlotter = (function(){  
      var dati = [],  
        i,  
        disegna = function(id) {  
                     var tavola = document.getElementById(id);  
                         tavola.setAttribute("style","width:400px; height:200px;");  
                         Flotr.draw(tavola,[dati]);  
                  };  
        for (i = 0; i < 10; i++) {  
           dati.push([i,i*i]);  
        }  
        return {  
           disegna : disegna  
        }; 
 })();  

Abbiamo creato il modulo mioPlotter con una IIFE che espone il metodo disegna invocato al click del bottone.
Il metodo disegna accetta come parametro l'id della divisione in cui verrà disegnato il grafico.
I dati del grafico sono generati con il ciclo for, che aggiunge all'array delle coppie [i,i].
Vi lascio con un quesito: come fareste a stampare il grafico di y=sqr(x) ? (radice quadrata di x)

Non abbiamo usato il terzo parametro options, i cui valori di default sono elencati qui, con il relativo significato. Provate a giocare con i parametri di configurazione es:

  Flotr.draw(container, [ data ], {  
       yaxis : {  
        max : 10,  
        min : -10  
       }  
      });  

configurando in tal modo le proprietà dell'asse y: le altre proprietà, anche se non definite, mantengono i loro valori di default.

Ho aggiornato su github il progetto, fate un git pull o, se non lo avete ancora clonato, seguite le istruzioni di questo post

Alla prox
Ivan

martedì, novembre 18, 2014

Un pò di pratica

Non c'è niente di meglio che un pò di pratica per capire i concetti esposti fino ad ora.

Per l'occasione ho creato un progetto su GitHub, dove metterò di volta in volta tutto il materiale necessario per eseguire gli esempi che proporrò.

Git è un sistema di condivisione sorgenti distribuito.

Ognuno di voi potrà installare sulla sua macchina il repository dei sorgenti eseguendo un semplice comando da una shell, a patto di installare git per il proprio sistema operativo.

Se non usate ancora git, vi consiglio di provarlo, poi non potrete farne a meno.

Scaricatelo e installatelo seguendo questo link e scegliendo il pacchetto d'installazione in base al sistema operativo utilizzato.


  • Aggiungete al path di sistema la cartella bin presente nel percorso d'installazione
  • Create una cartella vuota che conterrà tutti gli esercizi che vi proporrò.
  • Aprite una shell nuova e posizionatevi nella cartella appena creata.
  • Eseguite il comando:
    git clone https://github.com/ivansaracino/passionejavascript.git


Il comando precedente copierà nella vostra cartella la cartella passionejavascript

Quando aggiornerò il repository remoto, vi basterà invocare il comando

  • git pull

dalla cartella radice passionejavascript.

Nella cartella passionejavascript troverete la cartella modulerevealedpattern contenente la soluzione al seguente esercizio:

Creare una pagina web con due bottoni, start e stop.
Appena caricata la pagina, il bottone start è abilitato, il bottone stop è disabilitato.
Quando premete il bottone start, dovrà partire un contatore, che si aggiornerà ogni secondo; a questo punto il bottone start diventa disabilitato e il bottone stop abilitato.
Quando premete il bottone stop, il contatore si arresterà e così via.

L'esempio non è particolarmente utile ma mostra l'utilizzo del module revealed pattern,  un design pattern in javascript, che ha lo scopo di esporre sullo scope global un unico oggetto, sfruttando il concetto di closure e IIFE che abbiamo visto in questo post

Per eseguire la soluzione vi basta aprire il file index.html in un browser.

Notate come nello scope globale ci sia solo la variabile modulo  che espone le funzioni start() e stop() legate ai click dei rispettivi bottoni presenti nella pagina html.

Tutti i dettagli implementativi sono nascosti nella IIFE.

Se avete dubbi o problemi, non fate i timidi: chiedete commentando :))

Alla prox
Ivan

sabato, novembre 15, 2014

Spaghetti o Ravioli?

Siamo ora pronti a comprendere il codice presentato nel primo post, lo riscrivo qui per comodità

  var modulo = (function() {  
                   var f1 = function() {  
               
                            },  
                       f2 = function() {  
                 
                            },  
                       f3 = function() {  
                 
                            };  
               
                       return {  
                         f1 : f1,  
                         f2 : f2,
                         f3 : f3
                       };   
                })();  
   

Cosa contiene la variabile modulo? Contiene il risultato della esecuzione immediata della funzione dichiarata a destra, tale funzione ritorna il seguente oggetto


 {  
   f1 : function() {},  
   f2 : function() {}  
 }  
   
   

e quindi window.modulo espone le uniche proprietà f1 e f2 che in questo caso sono funzioni.

Le inner function, cioe le funzioni dichiarate all'interno di un'altra funzione, hanno l'importante proprietà di poter accedere alle variabili locali e i parametri dell'outer function anche quando l'outer function ha terminato la sua esecuzione.

Questa caratteristica, chiamata closure, insieme al concetto di  IIFE: Immediate Invocation Function Expression,  acronimo pomposo per descrivere una funzione che viene invocata appena subito dopo la sua dichiarazione, permette di creare degli oggetti che espongono solo le funzioni che vogliamo, nascondendo i dettagli implementativi; ciò ci permette di modularizzare il nostro codice, passando da un codice a spaghetti a un codice a ravioli :)

Facciamo ancora un esempio:

 var biblioteca = (function(nome) {  
        
      var libri = [],  
       
           nomeBiblioteca = nome,  
          
           inserisciLibro = function(libro) {  
               libri.push(libro);  
           },  
          
           getNomeBiblioteca = function() {  
               return nomeBiblioteca;  
           },  
          
           numeroLibri = function() {  
               return libri.length;  
           };  
   
          return {  
            insert : inserisciLibro,  
            getName : getNomeBiblioteca,  
            length : numeroLibri  
          };  
   
 })('civica di milano');  


 biblioteca.insert({titolo:'guerra e pace'});
 console.log(biblioteca.getName());
 console.log(biblioteca.length());

biblioteca è un oggetto, ritornato dalla funzione che viene immediatamente eseguita, espone dunque solo i tre metodi insert, getName, e length.

La variabile locale libri non è accessibile dall'esterno, essendo una variabile locale di una funzione,

I metodi dell'oggetto ritornato, possono ancora accedere alle variabili locali della funzione esterna, anche se la funzione esterna ha già finito la sua esecuzione: notate bene che in questo caso la eseguiamo immediatamente passandole il parametro 'civica di milano'.

Le linee di codice successive, memorizzano il libro dal titolo 'guerra e pace' nell'array libri, che esiste ancora nonostante la funzione precedente ha finito la sua esecuzione, stampano sulla console rispetivamente 'civica di milano' e 1.

Spero che ora non abbiate più dubbi sul potente concetto di closure, che ci permette di definire oggetti complessi nascondendo la loro complessita a chi deve utilizzarli.

Cominciate a utilizzare le IIFE e il vostro codice passerà da un groviglio di spaghetti a dei ravioli gustabili  uno ad uno :).

Alla prox.
Ivan