59. package Remedie::PubSub;
use Coro;
use Coro::Signal;
my %channels; # id => Coro::Channel
sub broadcast {
my($class, @events) = @_;
for my $event (@events) {
# who is listening?
for my $queue (values %channels) {
$queue->put($event);
}
}
}
60. # from HTTP::Engine app
# /rpc/events/poll?s={clientID}
sub poll {
my($self, $req, $res) = @_;
my $session = $req->param(‘s’);
my $events = Remedie::PubSub->wait(
$session, 55, # timeout
);
return $events || [];
}
61. package Remedie::PubSub;
sub wait {
my($class, $session, $timeout) = @_;
my $sweeper = async {
# check timeout
};
$signal->wait;
my @events;
push @events, $queue->get
while $queue->size > 0;
return @events;
}
74. CREATE TABLE channel (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
type INTEGER NOT NULL,
parent INTEGER NOT NULL,
ident TEXT NOT NULL,
name TEXT NOT NULL,
props TEXT
);
CREATE UNIQUE INDEX channel_ident ON channel (ident);
75. CREATE TABLE item (
id INTEGER NOT NULL PRIMARY KEY
AUTOINCREMENT,
channel_id INTEGER NOT NULL,
type INTEGER NOT NULL,
ident TEXT NOT NULL,
name TEXT NOT NULL,
status INTEGER NOT NULL,
props TEXT
);
CREATE INDEX item_status ON item (status)
CREATE UNIQUE INDEX item_ident ON item (channel_id,
ident);
129. Summary
• micro web server as a desktop app
• HTTP::Engine, JSONRPC and router
• Coro worker and Comet
• SQLite to store key-value
• jQuery plugins to enable desktop UIs
• More tools to make it really “.app”
I work for a company called Six Apart in San Francisco. It’s a company behind many blogging software like Movable Type, Vox and TypePad.
I mostly work on TypePad and recently we shipped new services like TypePad Connect and TypePad Profile. I did most of the backend code for those products.
This is my CPAN/PAUSE ID and you might have already known my name by:
the 154 modules I wrote and uploaded on CPAN. Some of these might look familiar to you.
miyagawa is also my twitter ID, and I’ll post links to the slides of this talk to Twitter later. Since this is 20 minute talk and there are many things to talk about, it’ll be pretty fast presentation, you don’t need to take notes or remember URLs and what not. Just look at the slides later online.
Let me give you a quick intro of the application called “Remedie” I’ve been working on recently like half an year. I’ll use this application as an example app today.
So, long story short, I wanted to build some Desktop applications, with nice UI rather than Command Line interface.
There might be many options to build a desktop GUI application. Some MFC/.NET microsoft windows thing. That’s not something you want to learn in 2009.
wxWidgets. I think this is a cross platform library to build a GUI application that runs on Windows, Mac and Linux and there’s a perl binding. Padre, PerlIDE actually uses this to build the Perl editor. Might not be a bad choice if you want to build an editor.
Objective C and Cocoa API is the best way to build some beautiful looking applications that run on Mac OS X. I guess it’s worth investing your time learning these technologies since you can leverage that to build iPhone applications for instance. And there’re also scripting language glues for Python and Ruby.
Adobe AIR would probably be the best choice if you already know some ActionScript and want to build a nice looking GUI app that runs on major platforms.
So, all of these options have pros and cons. Some are platform specific and some things need a completely different paradigm and I would need a lot of time learning those stuff. I’m a Perl hacker and do web stuff every day. I know a lot of Perl, and little bit of JavaScript.
Why not just build a web application and ship it as a desktop application.
Remember when Steve Jobs announced their initial developer SDK for iPhone in 2007. It was Web 2.0 Ajax application that has an access to iPhone services via sandboxed JavaScript APIs.
Palm Pre has this operating system called WebOS and its SDK called Mojo SDK allows you to write an application with HTML, CSS and JavaScript.
And there’s also a project called PhoneGap that allows you to do the same thing against iPhone, Android and BlackBerry: PhoneGap gives you an abstract wrapper API to access phone device functionalities through JavaScript so you can write an application using HTML, CSS and JavaScript for these devices. Luke used this framework to write his YAPC schedule app for Android.
And now HTML5 is coming on its way, with lots of features and APIs to access stuff like Geo location, native video embeds and local storage access.
It’s coming, but it hasn’t fully come yet. It’s definitely the future, but not available right now as its final form, in 2009. So what can fill this “gap”, like Phone Gap does?
It’s micro web app, or desktop web applications.
So let’s get back to this Remedie example and give you a quick demo. This is a pluggable, perl-based media center application that allows you to subscribe to virtually ANY video sites with RSS feeds, or even without RSS feeds if you write plugins. And you can watch videos or photos inline or using external player like QuickTime with single, pretty user interface.
Amazing? Cool. Let’s talk about how I built this application using which tools and modules. That’s probably what you’re interested in.
HTTP::Engine is based on Catalyst::Engine. It’s an extraction from Catalyst’s HTTP request and response handling code so that you can use it outside Catalyst to write a web application that runs under CGI, FastCGI or standalone server or mod_perl without changing anything. You can also use this to build your own micro web framework.
This is the callback function you need to bind the request. It takes an HTTP::Engine::Request object, which has a nice set of APIs to access request headers, cookies and parameters and all of that, and your callback is supposed to return the Response object with headers and body. That’s it.
It already has many interfaces supported, mostly ported over from Catalyst. Standalone, POE for event loop asynchronous process, FastCGI and mod_perl. Very nice.
So here, we’re making a desktop application. We don’t need a separate web server to serve millions of page views in a minute, so no need for FastCGI or mod_perl thing, right? Let’s just use ServerSimple, or POE if you want non-blocking processing.
And here are the things your callback handler is supposed to do. First of all, you need to serve static files to build the user interface. That will be HTML, CSS and JavaScript files.
HTTP::Engine has a extension framework called Middleware and they probably have static file serving code, so I’d probably switch to that instead of doing this file manipulation directly, which is kind of insecure.
The other thing your controller is supposed to is to implement Ajax backend actions with REST-JSON.
Note that this is a simplified code. And yes this is a little insecure.
And as I said, the construction to map HTTP request to internal API call is kind of crufty and insecure.
There are lots of solution to this, notably Path::Router, Dispatcher and HTTP::Dispatcher. They’re from best practical and Infinity Interactive, so pick whichever company you like. JSORB is also from infinity interactive and does more robust and kind of complicated REST-JSON mappings to call methods in the backend from JS client.
And lastly those REST APIs are vulnerable to Cross Site Request Forgery. If a hacker knows which port you’re running your application on, they can just link or send form POST to your endpoint to do whatever they want, using your identity.
Some authentication might help, but cookies or basic auth don’t because it’s CSRF. In Remedie we add some special header to the normal POST request, in jQuery client, so CSRF attacks will be just ignored. Switching to JSONRPC instead of normal GET/POST could solve that problem too.
And Bonjour. This is something I implemented last week and NOT the feature of HTTP::Engine yet, but when Remedie server launches it also publishes its hostname and port and machine name etc. over multicast DNS (aka Bonjour).
Will be useful for auto-discovery by clients, or share the list of channels with someone in your local networks.
Lots of people would have opinions which SQL database to use, for your web applications.
Usually you would pick one of MySQL or PostgreSQL, to build some scalable web sites with complex business logic to run against data stored on SQL server.
SQLite is a flat file based, portable SQL engine. It doesn’t implement all of ANSI SQL features, but very fast and useful for embedded software. There will be concurrency problems like locking if you have multiple processes opening the same SQLite database, but
So it’s best for desktop applications, because you’ll only have one process to open the same database, even if it’s a micro desktop web application that we’re talking about now.
Actually, software like Firefox, OS X’s Mail.app and iCal all use SQLite as its database.
Another reason that it’s good for desktop apps is the end-users don’t want to and need to run SQL server processes like MySQL or postgreSQL.
Also, if your use SQLite as a backend for your desktop app, backing up is easy. And you can synchronize your database between two machines like Home and Work using tools like Dropbox. Very nice.
Let’s talk about DB schema.
Actually let’s NOT talk about DB schema. You probably don’t need a DBA to design Desktop application. It depends on the size of data set your app is trying to handle, but most of the time, just use as simple schema as possible, to make it flexible and easily extensible.
Remedie’s schema is pretty simple. It has just a few index columns, like id and names, and then column called ‘props’ where we store all the properties as a JSON encoded object, or key-value pairs.
Key value pair is hot.
There are many document database, or key-value database systems like Couch, Mongo or TokyoTyrant and they actually need servers again, like MySQL or postgres.
So the idea is the same: we use SQLite to store arbitrary key-value pairs with some indexes.
How do we abstract those Database access. We need ORM. Another fight here which ORM to use.
Well, you can use anything you like. I don’t have strong opinion about this right now.
You can use DBIC or RDBO.
Remedie uses RDBO right now but there’s no special reason for that. I just wanted some excuse to try something different because I always use Catalyst, Data::ObjectDriver, YUI and Template Toolkit at work and for this desktop app I wanted to try the whole new stack to build a micro web app.
KiokuDB is one of the hottest technologies in the Perl these days, and there were lots of talks mentioning about it in this YAPC.
KiokuDB can actually do with its DBI backend to save key-value JSPON data in the SQLite database. So i think Yuval and maybe Stevan are interested in porting my Remedie RDBO code over to Kioku over the next few days.
So for this desktop web app thing, I would’t introduce anything special from the normal jQuery usage for your web applications. Just use HTML and CSS to design the UI.
You better not write HTML by hand, because your app is truly dynamic. You better manipulate the DOM object and use jQuery’s Ajax method to do Ajax JSON calls to the backend.
There are couple of templating plugins for jQuery but I chose jQuery.flydom. which is kind of similar to Template::Declare in Perl.
jQuery.hotkeys is a plugin for jQuery to enable hotkeys or keyboard shortcuts, like vi-style navigation, or using arrow keys to control video playback.
The usage is pretty simple. It overrides the original ‘keydown’ event so that you can specify which keyboard that it listens to, and then assign a callback to the pressed key.
jQuery.contextMenu is a plugin to enable some right-click or control-click menu so your web application actually looks like a desktop application.
jQuery corners is a plugin to do round corners, pretty important for Web 2.0 sites, and for desktop applications, to implement something like this:
this badge-like overlay is implemented using some hover divs with rounded corners.
jQuery has this event system that you can bind callbacks to browser events, like mouse over, or DOM ready, or link clicked, but you can also use the event system to trigger your app specific custom events and bind callbacks to them. I heavily use this system to make my JS like a real GUI application, because with GUI application most of them are coded in Event Loops.
jQuery UI is an extension to jQuery that implements some UI stuff.
Currently, we only use jQuery UI to implement the drag & drop stuff that you saw in the demo. Maybe I could use other features of jQuery UI to do some more fancy stuff.
There are many other jQuery plugins that I won’t talk about today but you might be interested in, to implement some desktop-app like features.
That’s it for jQuery. Let’s get back to this Desktop app thing again.
This desktop web app sounds sexy, but it’s actually just Client-Server model, right?
So we have 2 “apps” basically. One is the client and the other is the server. Let’s make the client as a real application. In this case client is a browser.
There’re tools to make Site-Specific Browser, or SSB in short.
Fluid and Prism. Fluid is Safari and Prism is Firefox.
This is how Fluid apps would look like. They’re just another browser (Safari) process that are specific to the site, like Github or Gmail or Flickr. You can use Fluid to build a site specific browser, to your application running on localhost:9999 or whatever port.
The users of your desktop app will be able to customize the frontend UI using Userscripts, like Greasemonkey for Prism and GreaseKit for Fluid. Users can also skin their app using Userstyles. So you don’t need to implement those plugin mechanisms or anything like that, to allow users to customize their app. SSB can do this already.
Fluid also allows you to extend your app and look more like a desktop app, so your app notification can be hooked to Growl, instead of jQuery.jGrowl that I mentioned, and has an access to Dock menu, like this.
Remedie’s RPC is implemented as an JSON REST API, so client and server are basically decoupled. There’s no tight connection between front-end app and backend micro web server APIs.
So theoretically, you can have more client applications other than normal browsers like Safari or Firefox, that talk to the same backend server over API. And you have more “views” to the application.
For example, iPhone.
Finally, we can actually make the Server as an app as well.
by packaging the server process as a bundle.
I used local::lib the excellent CPAN module to have application specific library path and install all dependencies locally inside the app bundle.
You can also use best practical’s shipwright to do the same thing, with slightly different approach.
And for Mac OS X, I used a tool called Platypus, to bundle those scripts, libraries and dependent CPAN modules in a .app bundle. it runs everywhere under Leopard, no CPAN installation required.