Keystroke Dynamics Ruby gem
The KSD gem 0.0.1 is out! This is my simple keystroke dynamics library for Ruby GTK widgets. Developers can help out on GitHub.
Here are some screenshots of the included examples.

Enroll with login
or

Enroll with sentences

Try to log in
If you do it right, you will see something like:
Verified user aczid with mean accuracy of: 0.585 Logged in successfully!
Update Apparently somebody in China found this cool enough to blog about it! (translated)
Reversing 2-array axis in Ruby
Recently, I was working on a project that imports some CSV data into a dynamic database table. It needs to sort an array of floats. Along the way coding, I found myself doing something curious:
rows = @table_class.all
rows.each do | row |
key = row.primary_key.to_sym
@matches[key] = []
row.instance_variables.each do | column |
unless ['@id', '@repository','@primary_key','@original_values', '@new_record','@collection', '@updated_at'].include? column
x = row.instance_variable_get(column)
y = column.gsub(/@/, '')
@matches[key] << {:x => x, :y => y}
end
end
@matches[key] = @matches[key].sort_by { |match| match[:y] }
end
@matchesSorting in Ruby! This smells bad. I put the data in a database for this?
The solution
The solution was to reverse the axis of the imported data, thereby enabling MySQL to sort the data for us.
Instead of doing:
n=0
@parsed_file.each do | row |
hash = row2hash(row)
unless @table_class.first(:primary_key => hash[:primary_key])
instance = @table_class.new(hash)
if instance.save
n+=1
GC.start if n%50 == 0
end
end
endWe can parse the file with inversed axis by doing:
values = {}
@parsed_file[0].enum_with_index.map do |primary_key, idx|
if primary_key
pk = primary_key.to_sym
@parsed_file.collect do |row|
if row[0]
values[pk] = {} unless values[pk].is_a?(Hash)
values[pk][row[0].to_sym] = row[idx]
end
end
end
end
n = 0
values.keys.each do |key|
if values[key]
unless @table_class.first(:primary_key => key.to_s)
instance = @table_class.new(values[key])
instance.primary_key = key
if instance.save
n+=1
GC.start if n%50 == 0
end
end
end
endI admit this is totally crazy code, and I don’t expect you to follow along. The rest of the class needed a bit of modifying too, but the first code example above has been simplified to:
@matches[primary_key.to_sym] = @table_class.all(:order => [primary_key.to_sym.desc])Ofcourse this hasn’t hurt performance, either
Dynamic DataMapper objects from imported CSV data
I have been working on a project that required some CSV data to be imported into a database. After I noticed DataMapper classes can be migrated through a class method, the idea of dynamically creating anonymous instances of DataMapper classes for imports occurred to me. In the code below the column types are known, but the column names are not. Here, I know all the columns except the primary key are of type Float. You could extend this example to add magic for determining the type of data, if you need it. This is experimental code, your mileage may vary
class CsvImporter
attr_accessor :table_class
require 'fastercsv'
def initialize(filename)
# CSV filename
@filename = filename
# Table column names array
@table_columns = ['primary_key']
puts "Parsing CSV file #{filename}"
parse_file(@filename)
end
# Returns sanitized name from filename.
# Replaces dashes with underscores, removes slashes, removes .csv extension and prepends 'csvimport_'
def self.table_name(filename)
basename = File.basename(filename.to_s).to_s
table_name = "csvimport_#{basename.gsub(/\.csv/, '').gsub(/-/, '_').gsub(/\//, '')}"
table_name
end
# Import CSV data into the database table using the ORM class
def parse_file(filename)
@parsed_file = FasterCSV.read(filename)
analyze_header(@parsed_file.shift)
create_table(CsvImporter.table_name(filename), @table_columns)
n = 0
@parsed_file.each do | row |
hash = row2hash(row)
unless @table_class.first(:primary_key => hash[:primary_key])
instance = @table_class.new(hash)
if instance.save
n+=1
GC.start if n%50 == 0
end
end
end
end
# Converts a row of CSV data to a ruby Hash.
def row2hash(row)
hash = {}
row.size.times do |i|
unless row[i].nil?
hash[ @table_columns[i].to_sym ] = row[i]
end
end
hash
end
# Analyzes CSV header and adds fields to @table_columns array
def analyze_header(header)
header.each do | column |
# strips digit prefixes from CSV header and adds the result to
# table columns
if column
#column = "token_#{column.to_s}" unless column.to_s[0].is_a?(Integer)
@table_columns.push column.to_s.gsub(/^\d+: /,'')
end
end
end
# Automagically creates an ORM class for the import using @table_columns array
def create_table(name, columns)
# creates a new table class with a primary_key property
@table_class = Class.new do
include DataMapper::Resource
property :id, DataMapper::Types::Serial
property :updated_at, DateTime
property :primary_key, String
end
# set table name
@table_class.storage_names[:default] = name
# shift first element off because it is the primary key
pk = columns.shift
columns.each do | column |
# Here, I know all the columns except the primary key are of type Float. You can extend this to add magic for determining the type of data.
@table_class.property column.to_sym, Float, :precision => 11
end
# unshift PK back in place
columns.unshift(pk)
# dont destroy tables we already have
unless @table_class.storage_exists?
@table_class.auto_migrate!
end
end
endThe problem I had after this is that the anonymous object cannot be serialized in a traditionaly way. I decided to circumvent this by implementing a quick and dirty MySQL-specific DESC hack. I readily admit this is unstable, highly experimental code. If you plan to use it for any other purpose than mine, you will probably need to extend it a bit.
class CsvImporter
def self.load_class(name)
@table_class = Class.new do
# Again, these types are known to always be there
include DataMapper::Resource
property :id, DataMapper::Types::Serial
property :updated_at, DateTime
end
@table_class.storage_names[:default] = name
if @table_class.storage_exists?
desc = repository(:default).adapter.query("desc #{name}")
desc.each do |field|
case field.type
when /DateTime/i
klass = DateTime
when /Float/i
klass = Float
else
klass = String
end
klass = DataMapper::Types::Serial if field.id == "id"
if klass == Float
@table_class.property field.id.to_sym, klass, :precision => 11
else
@table_class.property field.id.to_sym, klass
end
puts "Created field with id #{field.id.to_sym}, class: #{klass}"
end
end
@table_class
end
endAnd there you have it. The ability to work with you CSV imported data through a DM class, as if it has always lived in the database. I hope somebody besides myself finds this cool/useful.
Simple keystroke dynamics analyzer/validator written in Ruby-GTK
I have written a simple keystroke dynamics analyzer/validator as a school project. An instance of Analysis can be attached to a widget, and its collected keystroke data can be averaged and compared using class methods in Analysis.
The Validation class holds class methods to manage a password hashes file and save/load encrypted keystroke analysis metrics to/from disk.
Full documentation is provided through RDoc annotations.
The code will be publicly browseable at my code site when I get permission to host it from my teacher. This is now a gem, and the code is available on github!
Testing C++ in Ruby, continued
For a while I’ve been working with SWIG to generate wrappers for my C++ code. I ran into some problems when using it in a real project, so I’ve made the following adjustments to my method. First of all I’m using mkmf to generate a Makefile for the shared objects. This is how the SWIG documentation shows it, and I have added some lines to link in more libraries and run the swig command. I’ve added the -minherit flag to support C++ inheritance.
require 'mkmf'
# Create wrapper module
`swig -c++ -ruby -minherit -Wall -o units_wrap.cpp units.i`
# Since the SWIG runtime support library for Ruby
# depends on the Ruby library, make sure it's in the list
# of libraries.
$libs = append_library($libs, Config::CONFIG['RUBY_INSTALL_NAME'])
# Also, we need the c++ libraries
$libs = append_library($libs, "stdc++")
# Create the makefile
create_makefile('units') This script, src/extconf.rb is executed by automake by adding the following in src/Makefile.am:
bin_PROGRAMS = example
example_SOURCES = example.cpp
noinst_unit_testsdir = .
noinst_unit_tests_DATA = units.so
EXTRA_DIST = extconf.rb autogen.sh
units.so:
ruby extconf.rb
makeI love this solution because now I only have 2 files to maintain compiler details instead of 3. src/units.i and src/Makefile.am. Also, by using the noinst_ prefix the module will not be included when I run ‘make dist’. You can see how it all fits together in my APR project.
Creating embedded systems

Our device showing off.
For a school project I have teamed up with a friend to create a small embedded system. He had this old serial cash register display lying around which can display semi-alphanumeric strings (actually just 0-9 and the letters H, E, L and P). We’ve programmed a PIC16F636 in C to encode data with RS232 timing and send it through our own circuit which connects to a serial port. Since this PIC chip doesn’t have a hardware UART, we’ve coded a software UART using inline assembly. We’ve created a report about this, which I will only release in Dutch. I believe it should be obvious what’s going on from a technical standpoint.
Here’s the PDF.
Using GeoIP from within Nginx

This is what it should look like if you are in Japan.
Since late last summer me and my friends have been experimenting with Nginx. It’s a neat little server, sublimely fast and it’s config format doesn’t make you wince. I use it especially for proxying dynamic content from other servers (like Mongrel) but it handles static content equally well.
I’ve played around with geolocation in most of my Rails apps and it made me think this sort of environmental information might just as well be managed by the server. The folks at MaxMind felt the same way, it seems. There are server-side modules available for Apache and Lighty. For Nginx it takes a little more effort, but it’s very doable. Let’s make it so!
Get the GeoLite CSV:
$ wget http://www.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
Unpack it:
$ unzip GeoIPCountryCSV.zip
Get the Nginx tarball:
$ wget http://sysoev.ru/nginx/nginx-0.5.35.tar.gz
Unpack it:
$ tar zxvf nginx-0.5.35.tar.gz
You can also get Nginx through you package manager but mine (Debian) didn’t install the included Perl script to convert the MaxMind CSVfile. You can run the script on the CSV file you extracted by running it like so:
$ perl ./nginx-0.5.35/contrib/geo2nginx.pl < GeoLiteCity_20080301/GeoLiteCity-Location.csv > countries.conf
This generates a countries.conf which Nginx can use. You have to include it in your nginx.conf. Here’s what I added to my nginx.conf in the http namespace. Before this step I moved the generated countries.conf file to my /etc/nginx directory.
geo $country {
default no;
include /etc/nginx/countries.conf;
127.0.0.0/24 us;
}This $country variable you set up only lives in Nginx so far. You have to add a proxy_set_header or fastcgi_param like so:
fastcgi_param COUNTRY $country
Or
proxy_set_header COUNTRY $country;
This will give you a $_SERVER[‘COUNTRY’] (in PHP) or something simmilar.
I’ve used this to automatically set the preferred language in my WebSVN site.
Testing C++ in Ruby with Automake and RSpec
Since I’m a C++ newbie, I felt I needed more confidence in my code. As I’ve been programming in Ruby for almost 2 years now, I decided to look for a way to test my C++ code in Ruby. I found an excellent post by Dean Wampler that deals with just this topic. Please take the time to read this fascinating article before continuing. I happened to have been playing with autotools for a different C++ project last week, so I decided to use them for this one too. I decided not to put the SWIG stuff into the Makefile, but rather use a shell script. Automake will make sure the modules get built the way they need to be built, and I can put everything described in Dean’s article in a little shell script which builds the wrapper object for Ruby. I’m using a file src/units.i which loads some module headers for SWIG to wrap. This code is now irrelevant. I’ve found a way to generate this by using mkmf
#!/bin/sh
CXX=/usr/bin/g++
SRC_DIR=src
CFLAGS="-fPIC -fno-strict-aliasing -g -O2"
BIN_DIR=bin
RUBY_LIBS=/usr/lib/ruby/1.8/i486-linux
SWIG=/usr/bin/swig
cd $SRC_DIR
SRCS=`ls *.cpp | sed "s/.*_wrap\.cpp//"`
OBJS=`ls *.o`
`${CXX} -I. -I${RUBY_LIBS} ${CFLAGS} -c ${SRCS}`
`${SWIG} -c++ -ruby -Wall -o units_wrap.cpp units.i`
`${CXX} -I. -I${RUBY_LIBS} ${CFLAGS} -c units_wrap.cpp`
`${CXX} -shared -L. -rdynamic -Wl,-export-dynamic -L/usr/local/lib -o units.so ${OBJS} -lruby1.8 -lpthread -ldl -lcrypt -lm -lc`You could extend it to take options for which module to build. As in my previous autotools example I have a ‘src’ dir containing my C++ code, and a new ‘spec’ dir containing my RSpec specifications in Ruby. The spec_helper.rb in the spec dir is taken from Dean’s example. Here’s an example of a spec for a FileReader class
require File.dirname(__FILE__) + '/spec_helper'
require 'units'
describe Units::FileReader do
it "should be a constant on module Units" do
Units.constants.should include('FileReader')
end
end
describe Units::FileReader, ".new" do
it "should create a new object of the type FileReader" do
fr = Units::FileReader.new("file.txt")
fr.filename.should_be "file.txt"
end
end
describe Units::FileReader, "#openFile" do
it "should open a file" do
fr = Units::FileReader.new
fr.openFile("file.txt").should_be true
fr.filename.should_be "file.txt"
end
end
describe Units::FileReader, "#closeFile" do
it "should close a file" do
fr = Units::FileReader.new
fr.closeFile.should_be true
fr.filename.should_be ""
end
end
describe Units::FileReader, "#readChar" do
it "should read a char" do
fr = Units::FileReader.new("file.txt")
char = fr.readChar
char.should_be fr.ch
char.is_a?(String).should_be true
char.length.should_be 1
end
end
describe Units::FileReader, "#readLine" do
it "should read a line from the file" do
fr = Units::FileReader.new("file.txt")
line = fr.readLine
line.is_a?(String).should_be true
line.match(/^.*\n$/).should_not be_nil
end
endOne little caveat I ran into: you have to expose the private variables of you C++ class with public accessors to be able to use them. Dean’s example shows these two little methods that do this, but it wasn’t obvious to me as my brain is cooked by programming in Ruby for too long. Hope this was helpful for anyone who wants to get started on testing their C++ code in Ruby easily.
Using autotools with C++ to make GTK apps
I’ve been playing around with GTK+, specifically gtkmm last week. I love the GTK+ GUI and I want to write some portable desktop applications. Luckily there are great docs for newbies to get started with. Although C++ has never been my language of choice, I really want to learn it. I think I will warm up to it given I can find a style that works for me. First up are Makefiles. Oh, Makefile. How slender and seductive you start out. Such warts you grow. So much does your ass inflate. After hacking away on my own Makefile for about 2 days I still had code that compiled. But along with that feat came an ugly Makefile which needed constant pruning. I decided it was enough, and thought I’d give automake a try, as is recommended in the gtkmm docs. Luckily the docs are good. But I think they need a bit of updating. On a new C++ project, here’s what I do. I’m using the first gtkmm example here.
aczid@homer:~/helloworld_gtk$ ls -lR .: total 4 drwxr-xr-x 3 aczid aczid 4096 2008-03-18 20:57 src ./src: total 12 -rw-r--r-- 1 aczid aczid 362 2008-02-09 11:35 helloworld.cpp -rw-r--r-- 1 aczid aczid 367 2008-02-09 11:32 helloworld.h -rw-r--r-- 1 aczid aczid 228 2008-02-09 11:33 main.cpp aczid@homer:~/helloworld_gtk$
As you see I have a ‘src’ dir conatining my C++ sources, and no Makefile. Now comes the magic.
aczid@homer:~/helloworld_gtk$ autoscan aczid@homer:~/helloworld_gtk$ ls -l total 8 -rw-r--r-- 1 aczid aczid 0 2008-03-18 20:59 autoscan.log -rw-r--r-- 1 aczid aczid 475 2008-03-18 20:59 configure.scan drwxr-xr-x 3 aczid aczid 4096 2008-03-18 20:57 src aczid@homer:~/helloworld_gtk$ mv configure.scan configure.ac
Autoscan has generated the input file for autoconf for you. Let’s take a look:
# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) AC_CONFIG_SRCDIR([src/main.cpp]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CXX AC_PROG_CC # Checks for libraries. # Checks for header files. # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_OUTPUT
Let’s add a few lines.
# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) FULL-PACKAGE-NAME(Hello World) VERSION(1.0) BUG-REPORT-ADDRESS(bugs@aczid.nl) AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) AC_CONFIG_SRCDIR([src/main.cpp]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE # Checks for programs. AC_PROG_CXX AC_PROG_CC # Checks for libraries. PKG_CHECK_MODULES(DEPS, gtkmm-2.4 >= 2.4 gtk+-2.0 >= 2.2 glib-2.0 >= 2.2) AC_SUBST(DEPS_CFLAGS) AC_SUBST(DEPS_LIBS) # Checks for header files. AC_STDC_HEADERS # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT
You see I added some GTK libs here. The next 2 lines after that set up the compiler options for them. Also notice I added references to a Makefile in the config files macro. These files need stubs to be processed by automake. There are a lot of macros in autoconf to do all kinds of crazy things. Edit ./Makefile.am and add:
SUBDIRS = src
And in the src/Makefile.am you put:
bin_PROGRAMS = helloworld helloworld_SOURCES = helloworld.cpp main.cpp helloworld_LDADD = $(DEPS_LIBS) AM_CPPFLAGS = $(DEPS_CFLAGS)
See, those LDADD and CPPFLAGS macros rely on our DEPS_LIBS and DEPS_CFLAGS variables declared in the configure.ac file. Now all you need to do is run some commands!
aczid@homer:~/helloworld_gtk$ aclocal /usr/share/aclocal/libmcrypt.m4:17: warning: underquoted definition of AM_PATH_LIBMCRYPT /usr/share/aclocal/libmcrypt.m4:17: run info '(automake)Extending aclocal' /usr/share/aclocal/libmcrypt.m4:17: or see http://sources.redhat.com/automake/automake.html#Extending-aclocal aczid@homer:~/helloworld_gtk$ autoconf aczid@homer:~/helloworld_gtk$ autoheader aczid@homer:~/helloworld_gtk$ touch NEWS README AUTHORS ChangeLog aczid@homer:~/helloworld_gtk$ automake --add-missing
Phew, that’s it! Now you can run ./confgure and it will generate a Makefile from the Makefile.in, which was in turn generated by Automake.
aczid@homer:~/helloworld_gtk$ ./configure checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking for g++... g++ checking for C++ compiler default output file name... a.out checking whether the C++ compiler works... yes checking whether we are cross compiling... no checking for suffix of executables... checking for suffix of object files... o checking whether we are using the GNU C++ compiler... yes checking whether g++ accepts -g... yes checking for style of include used by make... GNU checking dependency style of g++... gcc3 checking for gcc... gcc checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed checking dependency style of gcc... gcc3 checking how to run the C preprocessor... gcc -E checking for grep that handles long lines and -e... /bin/grep checking for egrep... /bin/grep -E checking for ANSI C header files... yes checking for a BSD-compatible install... /usr/bin/install -c checking for pkg-config... /usr/bin/pkg-config checking pkg-config is at least version 0.9.0... yes checking for DEPS... yes configure: creating ./config.status config.status: creating Makefile config.status: creating src/Makefile config.status: creating config.h config.status: config.h is unchanged config.status: executing depfiles commands aczid@homer:~/helloworld_gtk$ make cd . && /bin/sh /home/aczid/helloworld_gtk/missing --run autoheader rm -f stamp-h1 touch config.h.in cd . && /bin/sh ./config.status config.h config.status: creating config.h config.status: config.h is unchanged make all-recursive make[1]: Entering directory `/home/aczid/helloworld_gtk' Making all in src make[2]: Entering directory `/home/aczid/helloworld_gtk/src' g++ -DHAVE_CONFIG_H -I. -I.. -I/usr/include/gtkmm-2.4 -I/usr/lib/gtkmm-2.4/include -I/usr/include/glibmm-2.4 -I/usr/lib/glibmm-2.4/include -I/usr/include/gdkmm-2.4 -I/usr/lib/gdkmm-2.4/include -I/usr/include/pangomm-1.4 -I/usr/include/atkmm-1.6 -I/usr/include/gtk-2.0 -I/usr/include/sigc++-2.0 -I/usr/lib/sigc++-2.0/include -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/lib/gtk-2.0/include -I/usr/include/cairomm-1.0 -I/usr/include/pango-1.0 -I/usr/include/cairo -I/usr/include/freetype2 -I/usr/include/libpng12 -I/usr/include/atk-1.0 -g -O2 -MT helloworld.o -MD -MP -MF .deps/helloworld.Tpo -c -o helloworld.o helloworld.cpp mv -f .deps/helloworld.Tpo .deps/helloworld.Po g++ -DHAVE_CONFIG_H -I. -I.. -I/usr/include/gtkmm-2.4 -I/usr/lib/gtkmm-2.4/include -I/usr/include/glibmm-2.4 -I/usr/lib/glibmm-2.4/include -I/usr/include/gdkmm-2.4 -I/usr/lib/gdkmm-2.4/include -I/usr/include/pangomm-1.4 -I/usr/include/atkmm-1.6 -I/usr/include/gtk-2.0 -I/usr/include/sigc++-2.0 -I/usr/lib/sigc++-2.0/include -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/lib/gtk-2.0/include -I/usr/include/cairomm-1.0 -I/usr/include/pango-1.0 -I/usr/include/cairo -I/usr/include/freetype2 -I/usr/include/libpng12 -I/usr/include/atk-1.0 -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.cpp mv -f .deps/main.Tpo .deps/main.Po g++ -g -O2 -o helloworld helloworld.o main.o -lgtkmm-2.4 -lgdkmm-2.4 -latkmm-1.6 -lpangomm-1.4 -lcairomm-1.0 -lglibmm-2.4 -lsigc-2.0 -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 make[2]: Leaving directory `/home/aczid/helloworld_gtk/src' make[2]: Entering directory `/home/aczid/helloworld_gtk' make[2]: Leaving directory `/home/aczid/helloworld_gtk' make[1]: Leaving directory `/home/aczid/helloworld_gtk' aczid@homer:~/helloworld_gtk$
See? :) I’ve quite enjoyed fooling around with this. It beats the hell out of writing your own Makefiles. It’s nice to know your app will be compatible with GNU standards and there is much less to write and consequently maintain. In the future I think I’ll look at other systems that try to ease the same pains. Autotools were made for much bigger projects than mine. Scons is what I might look into next.
Easy UML from your rails app
If you're a programmer, chances are you're not a big fan of making software diagrams. Writing code is much more fun!
Of course, if you remain agile you can do a whole project by yourself. But say you are using rails in a professional environment? Sooner or later, you're going to want to show someone a global overview of how your app is structured.
In my opinion, diagrams are good to reach agreement on the design of software.
It should be easy to maintain,
it shouldn't all be written in advance, and
it definetly shouldn't be a product in and of itself.
Since we're lazy ruby programmers, why not turn the waterfall upside down and generate diagrams from our code?
Go and get Railroad. Its needs Graphviz to work. You can get railroad as a gem and graphviz is hopefully in your package manager.
After getting those, just try it out on your current project!
Generate a model diagram from your app including non-ActiveRecord models and inheritance.
$ railroad -M -a -i | dot -Tpng > doc/models.png
Generate a controller diagram.
$ railroad -C | neato -Tpng > doc/controllers.png
Check out the railroad site for more info, code examples and some sample output. The output actually looks pretty neat. Here's what it generates for Typo.










