Montag, 11. Juli 2011

Reaktiv im Web mit node.js/express.js/socket.io/jQuery/jGrowl

Immer wieder beeindruckend ist die rasante und innovative Entwicklung im JavaScript Umfeld. Vor einiger Zeit war es noch jQuery, welches mit Hilfe einer Abstraktion einer Fluent-DSL zur HTML-DOM-Manipulation neuen Wind in die Web-Entwicklung brachte. Heute ist es bsw. Paper.js um Vektor Grafiken im Web zu erzeugen oder auch RxJs mit dessen Hilfe wir asynchrone, event-basierte Benutzer-System Interaktion fehlerfreier, einfacher und effizienter entwickeln können. Eine meiner jüngsten Twitter-Unterhaltung mit Norbert Eder und seinem Blog-Post Einstieg in Node.js animierte mich, mal wieder ein paar meiner Spikes im Umgang mit Node.js, JavaScript und jQuery in diesem Beitrag kurz zu beschreiben.

Am besten beginne ich dort, an der Norbert seinen Beitrag mit einen Link auf einen einfachen Node.js TCP Server beendet hat. Mein Wunsch war eine Implementierung eines einfachen Chats mit einer netten Benachrichtungsfunktion im MacOS Growl Stil.

Da ich möglichst schnell erste Ergebnisse sehen wollte, bin ich diesen Weg der Ubuntu Package Manager Installation für Node.js v0.4.9 gegangen.

sudo apt-get install python-software-properties
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs

Danach folgte die Installation von Express.js, EJS und Socket.IO mit Hilfe des Node Package Manager (NPM).

npm install -g express
npm install ejs
npm install socket.io

Bei Express.js handelt es sich um eine Ruby on Rails inspirierte Implementierung eines MVC Web Framework Stacks inkl. Request/Response Routing, Template Engine und Middleware zur einfachen und schnellen Erstellung von flexiblen und performaten Web Sites und RESTful Services.

EJs ist eine in Express.js eingebettete View Template Engine zur dynamischen Generierung von HTML Web Sites.

Ich verwende Socket.IO zur Implementierung von bidirektionaler Clientbenachrichtigungen (Push-Kommunikation) über den Browser.  Socket.IO abstrahiert verschiedene Formen der bidirektionalen Kommunikation zwischen Client und Server. Dazu bedient sich Socket.IO, wenn möglich HTML5 WebSockets, oder eines Flash Plugin's oder, sollte keine bidirektionale Kommunikation möglich sein, auch dem Ajax Polling.

Wie beginnen wir mit der Implementierung der Chat-App mit Node.js und Express.js?

Als erstes erstellen wir einen Web Application Skeleton unter der Verwendung der EJs Template Engine mit
mkdir FooBar & cd FooBar

und dem Kommando
express -t ejs

Danach lösen wir die Express.js dependencies über
npm install -d

Und fertig ist die erste "Default"-Web Anwendung. Gestartet wird die Anwendung per
node app.js
und ist unter http://localhost:3000 zu erreichen.

Express.js generiert dabei folgende Anwendungsstruktur

app.js zentrales Anwendungsprogramm
node_modules Node.js und Express.js Module Dependencies
package.json Anwendungsbeschreibungsdatei
public Inhaltsablage (assets) für JavaScript, Bilder, StyleSheets
views Ablage für HTML-Templates

Jetzt benötigen wir noch Socket.IO. Diese installieren wir über
npm install socket.io

Um Growl Benachrichtigungen anzeigen zu können, laden uns wir uns jGrowl über
curl -o public/javascripts/jquery.jgrowl_1.zip http://plugins.jquery.com/files/jquery.jgrowl_1.zip
unzip public/javascripts/jquery.jgrowl_1.zip -d public/javascripts/

Nun erstellen wir eine neue Template Datei mit
touch views/sample.ejs
vi views/sample.ejs

<!DOCTYPE html>
  <head>
    <title><%= title %></title>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">google.load("jquery", "1.4.4");</script>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
    <script type="text/javascript" src="/javascripts/jgrowl/jquery.jgrowl.js"></script>
    <link rel="stylesheet" href="/javascripts/jgrowl/jquery.jgrowl.css" type="text/css" />
    <script type="text/javascript">var port = <%= port %>;</script>
    <script type="text/javascript" src="/javascripts/client.js"></script>
  </head>
  <body>
    <form action="" id="form">
      <input type="text" name="message" id="message" />
      <input type="submit" value="Nachricht senden" />
    </form>
    <div>Verlauf:</div>
    <hr />
    <dl id="list"></dl>
  </body>
</html>

Nun registrieren wir die neue Url-Route zur views/sample.ejs in der app.js mit

app.get('/sample', function(req, res){
  res.render('sample', {
    title: 'Welcome to growl sample app',
    port: 3000,
    layout: false
  });
});

Mit dieser Anweisung wird Express.js angewiesem, auf der angeforderten Resource http://localhost:3000/sample das EJd Template sample.ejs zu rendern und auszuliefern.

Nun erstellen wir das notwendige Client side JavaScript zur Kommunikation mit dem Node.js Server.
touch public/javascripts/client.js
vi public/javascripts/client.js

$(function() {
    $.jGrowl.defaults.pool = 5;
    var socket = io.connect();
    socket.on('connect', function() {
        console.log('connect');
    });
    socket.on('message', function(msg) {
        console.log(msg);
        var now = new Date();
        $.jGrowl(msg, { sticky: false, header: now.toLocaleString(), life: 10000 });
        $('#list').prepend($('<dt>' + now.toLocaleString() + '</dt><dd>' + msg + '</dd>'));
    });
    socket.on('disconnect', function(){
        console.log('disconnect');
    });

    $('#form').submit(function() {
        var message = $('#message');
        socket.send(message.val());
        message.attr('value', '');
        return false;
    });
});

Im nächsten Schritt müssen wir die Socket.IO Event-Handler zur Nachrichtenkommunikation  in unserer Anwendung registrieren. Dies geschieht mit folgenden Snippet.

var io = require('socket.io').listen(app);
io.sockets.on('connection', function(client) {
    console.log('connected');
    client.send('hi...');
    client.broadcast.send('... new client connected.');
    client.on('message', function(msg) {
        console.log(msg);
        client.send(msg);
        client.broadcast.send(msg);
    });
    client.on('disconnect', function() {
        console.log('disconnect');
    });
});

Der spannende Teil ist vorallem

client.on('message', function(msg) {
        ...
        client.broadcast.send(msg);
 ...
});

Mit diesem Callback werden alle registrierten Clients beim Empfang einer Client-Nachricht auf Server-Seite mit einer Broadcast-Nachricht über die Änderung benachrichtigt. Die Benachrichtigung wird  über die Implementierung in der /public/javascripts/client.js Datei einmal in eine Liste geschrieben und zweitens über die jGrowl Anweisung

$.jGrowl(msg, { sticky: false, header: now.toLocaleString(), life: 10000 });

in die Growl Pipeline geschrieben. Schön finde ich, dass Socket.IO Nachrichten nur an die anderen Empfänger sendet und nicht an den ursprünglichen Absender. Mit

client.send(msg);

ist es möglich, die gesendete Nachricht auch an den Sender zurück zu senden.

Insgesamt sieht die app.js jetzt so

var express = require('express');
var app = module.exports = express.createServer();

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'ejs');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', function(req, res){
  res.render('index', {
    title: 'Express'
  });
});

app.get('/sample', function(req, res){
  res.render('sample', {
    title: 'Welcome to growl sample app',
    port: 3000,
    layout: false
  });
});


app.listen(3000);
var io = require('socket.io').listen(app);
io.sockets.on('connection', function(client) {
    console.log('connected');
    client.send('hi...');
    client.broadcast.send('... new client connected.');
    client.on('message', function(msg) {
        console.log(msg);
        client.send(msg);
        client.broadcast.send(msg);
    });
    client.on('disconnect', function() {
        console.log('disconnect');
    });
});

console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);

Abschließend starten wir die Anwendung mit
node app.js
und sollten dann mit folgendem Screen belohnt werden.

Verbunden mit mehreren Web-Browser Clients ist nun ein minimaler Chat mit Growl-Nachrichten möglich.







Und auch im iPhone, iPad oder Windows Phone 7 macht Node.js, Socket.IO und jGrowl eine gute Figur.























Ich hoffe dieser kleine Einblick in die Welt von Node.js und JavaScript macht Lust auf mehr. Meine Implementierung könnt ihr euch gern unter
http://beta2.mikebild.com:3000/sample
ansehen.

Dienstag, 21. Juni 2011

Linux S3 Bucket per Mount

S3QL installieren:
apt-get install python-software-properties
add-apt-repository ppa:nikratio/s3ql
add-apt-repository ppa:ubuntu-rogerbinns/apsw
apt-get update
apt-get install s3ql
mkdir ~/.s3ql
vi ~/.s3ql/authinfo
backend s3 machine any login AWS_KEY password AWS_SECRET
storage-url s3://mybucket password mypassword
chmod 600 ~/.s3ql/authinfo

S3QL erstellen:
mkfs.s3ql s3://mybucket
S3QL verwenden:
mount.s3ql --cachesize 204800 s3://mybucket /mnt/s3/mybucket
umount.s3ql /mnt/s3/mybucket

Dienstag, 10. Mai 2011

Wo ist "Long Left Button Down" im XAML?

Gerade bei Touch-Screen oder Windows Phone 7 Anwendungen wäre heute ein "Long Left Button Down Event" für beispielsweise Kontextmenüs ganz nützlich gewesen. Die folgenden Zeilen zeigen eine Lösungsweg mit dem Reactive Framework.
 
public event MouseButtonEventHandler MouseLeftButtonLongDown;
public Ctor()
{
    InitializeComponent();           
    var cancelEvent = Observable
     .FromEvent<MouseButtonEventArgs>(this, "MouseLeftButtonUp");
   
    var timer = Observable
     .Timer(TimeSpan.FromSeconds(1))
     .ObserveOnDispatcher();
   
    Observable
     .FromEvent<MouseButtonEventArgs>(this, "MouseLeftButtonDown")
     .Subscribe(x=> timer
      .TakeUntil(cancelEvent)
      .Subscribe(e => MouseLeftButtonLongDown(x.Sender, x.EventArgs)));

    MouseLeftButtonLongDown += (s,e) => MessageBox.Show("Test");
}
 

Für “this” kann natürlich jedes andere von UIELement abgeleitete XAML Element eingesetzt werden. Das Klick-Timeout liegt im Beispiel bei 1 Sekunde.

Donnerstag, 25. November 2010

KataFizzBuzz ala Reactive Framework

class Program
{
    static void Main(string[] args)
    {
        FizzBuzzer(1, 100).Subscribe(Console.WriteLine);
        Console.ReadLine();
    }
 
 
    internal static IObservable<string> FizzBuzzer(int start, int stop)
    {
        return Observable.Generate(start, x => x <= stop, x =>
                                    {
                                        if (IsFizz(x) && IsBuzz(x))
                                            return "FizzBuzz";
                                        if (IsFizz(x))
                                            return "Fizz";
                                        if (IsBuzz(x))
                                            return "Buzz";
                                        return x.ToString();
                                    }, x => x + 1);
    }
 
    internal static bool IsFizz(int x)
    {
        return (x % 3 == 0);
    }
 
    internal static bool IsBuzz(int x)
    {
        return (x % 5 == 0);
    }
}
 
[TestFixture]
class FizzBuzzTest
{
    [TestCase(1, 1, "1")]
    [TestCase(3, 3, "Fizz")]
    [TestCase(5, 5, "Buzz")]
    [TestCase(15, 15, "FizzBuzz")]
    public void FizzBuzzAlgoShouldNotFail(int from, int to, string result)
    {
        Program.FizzBuzzer(from, to).Subscribe(x => x.Should().Equal(result));
    }
}

Sonntag, 21. November 2010

Online Coding Dojo - Kata Tennis - Retrospektive

Für mich was das letzte Online Coding Dojo der .NET Online User Group durch seine Vielzahl an Wendungen im Verlauf des Abends außerordentlich spannend. Das nette Thema Game-Play rundete das Abendprogramm als besonders unterhaltsam dann nochmal ab. Leider konnte ich nicht bis zum Ende der Timebox teilnehmen und holte am Abend noch eine eigene Implementierung nach. Nachdem nun einige spannende Lösungsvarianten vorgestellt wurden, habe ich meine nun auch veröffentlicht.

Die Kata - KataTennis
Mir fällt eine Lösungsfindung ohne die Struktur einer Skizze immer etwas schwer, deshalb zeichnete ich neben der parallel stattfindenden Implementierung im Randori-Stil meine kleine Welt der Problem-Domäne.

Mir hilft diese Art der Anwendungsplanung - ohne Code - Klarheit über Code-Struktur, Funktionseinheiten und Datenstrukturen, die Funktionsweise, ihrer Interaktion und der Anwendungszustände zu erhalten. Das entscheidende für mich dabei ist, der grobe Überblick und Trennung der Aufgaben in Teilaufgaben. Ich versuche dann, wärend der Implementierung den Gedanken zu halten und in die gewünschte Funktionsweise umzusetzen. "Schwierige" Fälle beantworte ich vorerst nicht - sie bleiben mit einem gedanklichen Fragezeichen offen und werden an geeigneter Stelle mit einem test-, trace- und/oder debugger-getriebenen Vorgehen explorativ-iterativ bis zu Erfüllung der Akzeptanzkriterien "ausgecodet". Ich bin kein TDD-Hardliner, daher stellen Unittests auf verschiedenen Abstraktionsebenen meine Akzeptanzkriterien, und die aus Skizze erstellte Code-Struktur mein "System under Test" bereit. Aus diesem Mix, Planung und TDD Setup entsteht die API. In diesem Falle auch meine Variante der KataTennis.

Ganz besonders hat mir, da ich sonst kein Freund von Enums bin, die Idee "Game-States über Enums" abzubilden gefallen. Die Domäne wurde, auch im Code, um einen wichtigen Punkt, der verständlicheren Domänensprache, erweitert. Toll - ich hab das übernommen, aber ein kleiner Wermutstropfen bleibt - Operationen auf Enums, wie Increment (++), Decrement(--) sind zwar möglich, können allerdings zu ungewollten Exceptions führen. Hier gilt: Aufpassen bei Operationen über das maximale oder minimale Enum-Value-Scope hinausgehende.

Lehre
Die inzwischen entstandene Breite der Lösungsvarianten zeigt mir, wie unterschiedlich abstrakt das Problem der Spielstatushaltung, des Gameplays, des Code-Verständnisses und des Testens interpretiert werden kann. Mir war eine Trennung von Game-Play und Game-State, eine zentrale Steuerung der Transitionen über Constraints sowie eine relativ "natürliche" Verwendung der API wichtig. Schwer tat ich mich bei den Unittests der Game- und Play-States. Obwohl ich von der Notwendigkeit überzeugt bin, empfand ich das im Szenario als etwas lästig. Ich halte meine Augen und Ohren für eine umgängliche Lösung offen.

Fazit
Für mich ist es immer wieder erstaunlich, wie viele Lösungen eines Problems mit unterschiedlichen Ansätzen und Ideen verwirklicht werden können. Cool - da freue ich mich schon auf das nächste .NET Coding-Dojo.