Create an HTML table.

 The table body should have at least three rows of three columns.
 Each of these three columns should be labelled "X", "Y", and "Z". 
 An extra column should be added at either the extreme left or the extreme right of the table that has no heading, but is filled with sequential row numbers. 
 The rows of the "X", "Y", and "Z" columns should be filled with random or sequential integers having 4 digits or less. 
 The numbers should be aligned in the same fashion for all columns.


class HTML {
    method _attr(Hash h) {
        h.keys.sort.map {|k| %Q' #{k}="#{h{k}}"' }.join('')
    }
 
    method _tag(Hash h, name, value) {
        "<#{name}" + self._attr(h) + '>' + value + "</#{name}>"
    }
 
    method table(Hash h, *data) { self._tag(h, 'table', data.join('')) }
    method table(*data)         { self.table(Hash(), data...) }
}
 
class Table < HTML {
    method th(Hash h, value) { self._tag(h, 'th', value) }
    method th(value)         { self.th(Hash(), value) }
 
    method tr(Hash h, *rows) { self._tag(h, 'tr', rows.join('')) }
    method tr(*rows)         { self.tr(Hash(), rows...) }
 
    method td(Hash h, value) { self._tag(h, 'td', value) }
    method td(value)         { self.td(Hash(), value) }
}
 
var header = %w(&nbsp; X Y Z);
var rows = 5;
 
var html = HTML.new;
var table = Table.new;
 
say html.table(
    # attributes
    Hash(
        cellspacing => 4,
        style => "text-align:right; border: 1px solid;"
     ),
 
    # header
    table.tr(header.map{|elem| table.th(elem)}...),
 
    # rows
    (1..rows).map { |i|
        table.tr(
            table.td(:(align => 'right'), i),
            (header.len - 1).of {
                table.td(Hash(align => 'right'), 10000.rand.int)
            }...
        )
    }...
);