If it won't be simple, it simply won't be. [Hire me, source code] by Miki Tebeka, CEO, 353Solutions

Thursday, February 26, 2009

@reboot

cron has a nice special rule called @reboot which will run every time the machine is rebooted (doh!).

I use it for two main things:
  1. Notify me when one of my servers was rebooted (sometimes IT don't tell me in advance)
  2. Run my services. This is easier than writing an /etc/init.d scripts and I store my servers crontab in the source control, so deploying a new machine is easier.
An example:
# Notify that machine was rebooted
@reboot /path/to/mail -s "Machine $HOSTNANE rebooted" me@somewhere.com < /dev/null
# Run my server
@reboot (cd /path/to/awesome/server/directory && ./run_server)


Where run_server is usually something like:
#!/bin/bash

nohup ./server.py&

Tuesday, February 17, 2009

calc


A very simple command line/GUI calculator. The main trick here is to export all of math to the global namespace and then just eval the expression.

Thursday, February 12, 2009

twiver


A Twitter search client the updates every 10min.

#!/usr/bin/env python

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from urlparse import urlparse
from urllib2 import urlopen
from operator import itemgetter
from rfc822 import parsedate_tz
from time import strftime
import re
from functools import partial

try:
import json
except ImportError:
import simplejson as json

HTML = '''
<html>
<head>
<title>Twiver - Refreshing Twitter Search</title>
<style>
h2 {
font-variant: small-caps;
}
table#results {
border: 1px solid black;
width: 100%;
}
table#results tr:hover {
background: silver;
}
span#updated {
font-family: Monospace;
}
</style>
</head>
<body>
<h2>Twiver - Refreshing Twitter Search</h2>
Query: <input id="query" size="60" /> <button id="run">Go</button>
<table id="results">
</table>
<span id="updated"></span>
<hr />
By <a href="mailto:miki.tebeka@gmail.com">Miki</a>
</body>
<script src="jquery.js"></script>
<script>
var running = 0;

function handle_result(data) {
if (!running) {
return;
}

var table = $('#results');
table.empty();
$.each(data, function (i, text) {
var tr = $('<tr><td>' + text + '</td></tr>');
table.append(tr);
});
$('#updated').html('Updated: ' + new Date());

setTimeout(update, 10 * 1000);
}

function update() {
var query = $.trim($('#query').val());
url = '/search?q=' + query;
$.getJSON(url, handle_result);
}

function run() {
var button = $('#run');
if (button.text() == "Go") {
var query = $.trim($('#query').val());
/* FIXME: The best way will be to disable the button until there
is text
*/
if (query.length == 0) {
alert("Please enter *something*");
return;
}
button.text("Stop");
running = 1;
update();
}
else {
running = 0;
button.text("Go");
}
}

function on_ready()
{
$('#run').click(run);
}
$(document).ready(on_ready);
</script>
</html>
'''

def format_time(time):
# Wed, 11 Feb 2009 00:10:36 +0000
time = parsedate_tz(time)
return strftime("%m/%d/%Y %H:%M", time[:9])

# 'http://mikitebeka.com' ->
# '<a href="http://mikitebeka.com">http://mikitebeka.com</a>'
inject_links = partial(
re.compile("(http://[^ ]+)").sub,
"<a target=\"_new\" href=\"\\1\">\\1</a>")

def format_result(result):
time = format_time(result["created_at"])
text = inject_links(result["text"])
return "[%s] %s" % (time, text)

class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/":
self.wfile.write(HTML)
elif self.path.startswith("/search"):
self.updates()
elif self.path.endswith(".js"):
self.wfile.write(open(".%s" % self.path).read())
else:
self.send_error(404, "Not Found")

def updates(self):
o = urlparse(self.path)
url = "http://search.twitter.com/search.json?" + o.query
data = urlopen(url).read()
obj = json.loads(data)
results = map(format_result, obj["results"])
self.wfile.write(json.dumps(results))

if __name__ == "__main__":
server = HTTPServer(("", 8888), RequestHandler)
server.serve_forever()

Monday, February 02, 2009

"Game of Life" in JavaScript


<html>
<head>
<title>Game Of Life</title>
<style>
table {
border: 5px solid black;
}
td {
width: 15px;
height: 15px;
}
td.dead {
background: green;
}
td.alive {
background: red;
}
</style>
</head>
<body>
<b>Game of Life<b>
<table id="board">
</table>
<button id="run">Run</button>
<button id="step">Step</button>
<button id="clear">Clear</button>

</body>
<script src="jquery.js"></script>
<script>
var NUM_ROWS = 30;
var NUM_COLS = 30;
var DEAD = 'dead';
var ALIVE = 'alive';
var RUNNING = 0;
/* We keep this for fast access since jQuery selectors
$('#board tr:nth-child(' + row + 1 + ') td:nth-child(' + col + 1 + ')'
are slow
*/
var BOARD = [];

function copy_board() {
var board = [];
for (var row = 0; row < NUM_ROWS; ++row) {
var cells = [];
for (col = 0; col < NUM_COLS; ++col) {
var state = get_cell(row, col).hasClass(ALIVE) ?
ALIVE : DEAD;
cells.push(state);
}
board.push(cells);
}

return board;
}

function cell_neighbours(row, col) {
var uprow = (row + 1) % NUM_ROWS;
var downrow = (row - 1) % NUM_ROWS;
var leftcol = (col - 1) % NUM_COLS;
var rightcol = (col + 1) % NUM_COLS;

/* FIXME: Find a better way (since -1 % 10 => -1) */
downrow = (downrow < 0) ? NUM_ROWS - 1 : downrow;
leftcol = (leftcol < 0) ? NUM_COLS - 1 : leftcol;

return [
[uprow, leftcol], [uprow, col], [uprow, rightcol],
[row, leftcol], [row, rightcol],
[downrow, leftcol], [downrow, col], [downrow, rightcol]
];
}

/* http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules */
function calc_new_state(current_state, num_alive) {
if (current_state == ALIVE) {
if (num_alive < 2) {
return DEAD;
}
else if (num_alive > 3) {
return DEAD;
}
else {
return ALIVE;
}
}
else {
if (num_alive == 3) {
return ALIVE;
}
else {
return DEAD;
}
}
}


function on_step() {
/* Copy old board since we're going to change the current */
var board = copy_board();

function is_alive(cell) {
return board[cell[0]][cell[1]] == ALIVE;
}

for (var row = 0; row < NUM_ROWS; ++row) {
for (var col = 0; col < NUM_COLS; ++col) {
var neighbours = cell_neighbours(row, col);
var num_alive = $.grep(neighbours, is_alive).length;
var current_state = board[row][col];
var new_state = calc_new_state(current_state, num_alive);
if (new_state != current_state) {
toggle(row, col);
}
}
}
}

function run() {
if (!RUNNING) {
return;
}
on_step();
setTimeout(run, 200);
}

function on_run() {
var button = $('#run');
if (button.text() == 'Run') {
button.text('Stop');
RUNNING = 1;
run();
}
else {
button.text('Run');
RUNNING = 0;
}
}

function get_cell(row, col) {
return BOARD[row][col];
}

function toggle(row, col) {
var cell = get_cell(row, col);

if (cell.hasClass(ALIVE)) {
var add = DEAD;
var remove = ALIVE;
}
else {
var add = ALIVE;
var remove = DEAD;
}

cell.removeClass(remove).addClass(add);
}

function make_handler(row, col) {
return function() {
toggle(row, col);
}
}

function initiaize_board() {
var table = $('#board');
for (var row = 0; row < NUM_ROWS; ++row) {
var tr = $('<tr />');
var cells = [];
for (var col = 0; col < NUM_COLS; ++col) {
var td = $('<td />');
td.addClass(DEAD);
td.click(make_handler(row, col));
tr.append(td);
cells.push(td);
}
table.append(tr);
BOARD.push(cells);
}
}

function on_clear() {
$('.' + ALIVE).removeClass(ALIVE).addClass(DEAD);
}

function on_ready()
{
initiaize_board();
$('#step').click(on_step);
$('#run').click(on_run);
$('#clear').click(on_clear);
}

$(document).ready(on_ready);
</script>
</html>

Blog Archive