Skip to main content

Ralsina.Me — Roberto Alsina's website

Posts about nerdness

Creating a forum the easy way (32 LOC)

This is on­ly the first part of a project to cre­ate the sim­plest (for me) soft­ware fo­rum pos­si­ble.

Here are the fea­tures I wan­t:

  • Lo­­gin us­ing twit­ter / Face­­book / Google / OpenID

  • Un­lim­it­ed num­ber of threads

  • Sup­­port for like / dis­­­like both on threads and on posts

  • Avatars

  • HTML in com­­ments

  • Mail the us­er on replies

  • RSS feeds for threads

You can see it in ac­tion at http://­foro.net­man­ager­s.­com.ar (for a lim­it­ed time on­ly ;-)

And here is the code:

import bottle
import disqusapi as disqus
import json
shortname = 'magicmisteryforum'
api = disqus.DisqusAPI(open("key").read().strip())

@bottle.route('/', method='GET')
def index():
    msg = bottle.request.GET.get('msg', '')
    threads = api.forums.listThreads(forum=shortname, limit=100)
    print threads[0]
    return bottle.template('main.tpl', threads=threads, shortname=shortname, msg=msg)

@bottle.route('/new', method='POST')
def new():
    title = bottle.request.forms.get('title', None)
    if not title:
        bottle.redirect('/?msg=Missing%20Thread%20Name')
        return
    thread = api.threads.create(forum=shortname, title = title)
    thread_id = thread.__dict__['response']['id']
    # Redirecting to /thread/thread_id doesn't work
    # because threads take a few seconds to appear on the listing
    bottle.redirect('/')

@bottle.route('/thread/:id')
def thread(id):
    t = api.threads.details(thread=id)
    return bottle.template('thread.tpl', shortname=shortname, id=id, thread=t.__dict__['response'])

@bottle.route('/static/:path#.+#')
def server_static(path):
    return bottle.static_file(path, root='./static')

app = bottle.app()
app.catchall = False #Now most exceptions are re-raised within bottle.
bottle.run(host='184.82.108.14', port=80, app=app)

It re­quires Bot­tle and the Dis­qus python API

Of course, there is al­so a bit of tem­plat­ing in­volved, here is main.t­pl and the thread­.t­pl. Since I suck at HTM­L, it us­es Bluetrip CSS and it's more than sim­ple enough to cus­tom­ize.

OF COURSE I AM CHEAT­ING!

This thing is just a sim­ple ve­neer around Dis­qus! More like a blog with com­ments and with­out posts than a fo­rum! But ... what's miss­ing to make this a re­al fo­rum? It work­s, does­n't it? You could even use Dis­qus cat­e­gories to cre­ate sub­fo­rum­s...

All things con­sid­ered, I think it's a cute hack.

And if you wait a few days, this will lead to some­thing much more mag­i­cal!

Full source code at http://­mag­ic­fo­rum.­google­code.­com

New golfing challenge: PatoCabrera

In the spir­it of the De Vi­cen­zo web browser, I am start­ing a new pro­gram, called Pa­to Cabr­era. Here are the rules:

  • Twit­ter client (no iden­ti.­­ca in the first ver­­sion, but to be added lat­er)

  • Has these fea­­tures: http://­­paste­bin.lug­­men.org.ar/6464

  • Has to be im­­ple­­men­t­ed be­­fore April 4th

  • Smal­l­­er than 16384 bytes (of python code) but may be larg­er be­­cause of art­­work.

Let's see how it works :-)

De Vicenzo: A much cooler mini web browser.

It seems it was on­ly a few days ago that I start­ed this projec­t. Oh, wait, yes, it was just a few days ago!

If you don't want to read that again, the idea is to see just how much code is need­ed to turn Qt's We­bKit en­gine in­to a ful­ly-fledged brows­er.

To do that, I set my­self a com­plete­ly ar­bi­trary lim­it: 128 lines of code.

So, as of now, I de­clare it fea­ture-­com­plete.

The new fea­tures are:

  • Tabbed brows­ing (y­ou can ad­d/re­­move tab­s)

  • Book­­marks (y­ou can ad­d/re­­move them, and choose them from a drop-­­down menu)

This is what al­ready worked:

  • Zoom in (C­tr­l++)

  • Zoom out (C­tr­l+-)

  • Re­set Zoom (C­tr­l+=)

  • Find (C­tr­l+F)

  • Hide find (Esc)

  • But­­tons for back­­/­­for­ward and reload

  • URL en­try that match­es the page + au­­to­­com­­plete from his­­to­ry + smart en­try (adds http://, that kind of thing)

  • Plug­ins sup­­port (in­­clud­ing flash)

  • The win­­dow ti­­tle shows the page ti­­tle (with­­out brows­er ad­ver­tis­ing ;-)

  • Progress bar for page load­­ing

  • Sta­­tus­bar that shows hov­­ered links URL

  • Takes a URL on the com­­mand line, or opens http://python.org

  • Mul­ti­­plat­­form (works in any place QtWe­bKit work­s)

So... how much code was need­ed for this? 87 LINES OF CODE

Or if you want the PEP8-­com­pli­ant ver­sion, 115 LINES OF CODE.

Be­fore any­one says it: yes, I know the ren­der­ing en­gine and the tool­kit are huge. What I wrote is just the chrome around them, just like Aro­ra, Rekon­q, Ga­le­on, Epiphany and a bunch of oth­ers do.

It's sim­ple, min­i­mal­is­tic chrome, but it works pret­ty good, IMVHO.

Here it is in (bug­gy) ac­tion:

It's more or less fea­ture-­com­plete for what I ex­pect­ed to be achiev­able, but it still needs some fix­es.

You can see the code at it's own home page: http://de­vi­cen­zo.­google­code.­com

How much web browser can you put in 128 lines of code?

UP­DATE: If you read this and all you can say is "o­h, he's just em­bed­ding We­bKit", I have two things to tell you:

  1. Duh! Of course the 128 lines don't in­­­clude the ren­der­ing en­gine, or the TCP im­­ple­­men­­ta­­tion, or the GUI tool­k­it. This is about the rest of the browser, the part around the web ren­der­ing en­gine. You know, just like Aro­ra, Rekon­q, Epiphany, and ev­ery­one else that em­beds we­bkit or mozil­la does it? If you did­n't get that be­­fore this ex­­pla­­na­­tion... facepalm.

  2. Get your favourite we­bkit fork and try to do this much with the same amount of code. I dare you! I dou­ble dog dare you!

Now back to the orig­i­nal ar­ti­cle


To­day, be­cause of a IRC chat, I tried to find a 42-­line web brows­er I had writ­ten a while ago. Sad­ly, the paste­bin where I post­ed it was dead, so I learned a lesson: It's not a good idea to trust a paste­bin as code repos­i­to­ry

What I liked about that 42-­line brows­er was that it was not the typ­i­cal ex­am­ple, where some­one dumps a We­bkit view in a win­dow, loads a page and tries to con­vince you he's cool. That one is on­ly 7 lines of code:

import sys
from PyQt4 import QtGui,QtCore,QtWebKit
app=QtGui.QApplication(sys.argv)
wb=QtWebKit.QWebView()
wb.setUrl(QtCore.QUrl('http://www.python.org'))
wb.show()
sys.exit(app.exec_())

And if I want­ed to make the code uglier, it could be done in 6.

But any­way, that 42-­line brows­er ac­tu­al­ly looked use­ful!

This 42-line web browser, courtesy of #python and #qt -- http... on Twitpic

Those but­tons you see ac­tu­al­ly worked cor­rect­ly, en­abling and dis­abling at the right mo­men­t, the URL en­try changed when you clicked on links, and some oth­er bit­s.

So, I have de­cid­ed to start a smal­l, in­ter­mit­tent project of code golf: put as much brows­er as I can in 128 lines of code (not count­ing com­ments or blanks), start­ing with PyQt4.

This has a use­ful pur­pose: I al­ways sus­pect­ed that if you as­sumed PyQt was part of the base sys­tem, most apps would fit in flop­pies again. This one fits on a 1.44MB flop­py some 500 times (so you could use 360KB com­modore flop­pies if you prefer­!).

So far, I am at about 50 lines, and it has the fol­low­ing fea­tures:

  • Zoom in (C­tr­l++)

  • Zoom out (C­tr­l+-)

  • Re­set Zoom (C­tr­l+=)

  • Find (C­tr­l+F)

  • Hide find (Esc)

  • But­­tons for back­­/­­for­ward and reload

  • URL en­try that match­es the page + au­­to­­com­­plete from his­­to­ry + smart en­try (adds http://, that kind of thing)

  • Plug­ins sup­­port (in­­clud­ing flash)

  • The win­­dow ti­­tle shows the page ti­­tle (with­­out brows­er ad­ver­tis­ing ;-)

  • Progress bar for page load­­ing

  • Sta­­tus­bar that shows hov­­ered links URL

  • Takes a URL on the com­­mand line, or opens http://python.org

  • Mul­ti­­plat­­form (works in any place QtWe­bKit work­s)

Miss­ing are tabs and proxy sup­port. I ex­pect those will take an­oth­er 40 lines or so, but I think it's prob­a­bly the most fea­ture­ful of these toy browser­s.

The code... it's not all that hard. I am us­ing lamb­da a lot, and I am us­ing PyQt's key­word ar­gu­ments for sig­nal con­nec­tion which makes lines long, but not hard. It could be made much small­er!

Here it is in ac­tion:

And here's the code:

#!/usr/bin/env python
"A web browser that will never exceed 128 lines of code. (not counting blanks)"

import sys
from PyQt4 import QtGui,QtCore,QtWebKit

class MainWindow(QtGui.QMainWindow):
    def __init__(self, url):
        QtGui.QMainWindow.__init__(self)
        self.sb=self.statusBar()

        self.pbar = QtGui.QProgressBar()
        self.pbar.setMaximumWidth(120)
        self.wb=QtWebKit.QWebView(loadProgress = self.pbar.setValue, loadFinished = self.pbar.hide, loadStarted = self.pbar.show, titleChanged = self.setWindowTitle)
        self.setCentralWidget(self.wb)

        self.tb=self.addToolBar("Main Toolbar")
        for a in (QtWebKit.QWebPage.Back, QtWebKit.QWebPage.Forward, QtWebKit.QWebPage.Reload):
            self.tb.addAction(self.wb.pageAction(a))

        self.url = QtGui.QLineEdit(returnPressed = lambda:self.wb.setUrl(QtCore.QUrl.fromUserInput(self.url.text())))
        self.tb.addWidget(self.url)

        self.wb.urlChanged.connect(lambda u: self.url.setText(u.toString()))
        self.wb.urlChanged.connect(lambda: self.url.setCompleter(QtGui.QCompleter(QtCore.QStringList([QtCore.QString(i.url().toString()) for i in self.wb.history().items()]), caseSensitivity = QtCore.Qt.CaseInsensitive)))

        self.wb.statusBarMessage.connect(self.sb.showMessage)
        self.wb.page().linkHovered.connect(lambda l: self.sb.showMessage(l, 3000))

        self.search = QtGui.QLineEdit(returnPressed = lambda: self.wb.findText(self.search.text()))
        self.search.hide()
        self.showSearch = QtGui.QShortcut("Ctrl+F", self, activated = lambda: (self.search.show() , self.search.setFocus()))
        self.hideSearch = QtGui.QShortcut("Esc", self, activated = lambda: (self.search.hide(), self.wb.setFocus()))

        self.quit = QtGui.QShortcut("Ctrl+Q", self, activated = self.close)
        self.zoomIn = QtGui.QShortcut("Ctrl++", self, activated = lambda: self.wb.setZoomFactor(self.wb.zoomFactor()+.2))
        self.zoomOut = QtGui.QShortcut("Ctrl+-", self, activated = lambda: self.wb.setZoomFactor(self.wb.zoomFactor()-.2))
        self.zoomOne = QtGui.QShortcut("Ctrl+=", self, activated = lambda: self.wb.setZoomFactor(1))
        self.wb.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)

        self.sb.addPermanentWidget(self.search)
        self.sb.addPermanentWidget(self.pbar)
        self.wb.load(url)


if __name__ == "__main__":
    app=QtGui.QApplication(sys.argv)
    if len(sys.argv) > 1:
        url = QtCore.QUrl.fromUserInput(sys.argv[1])
    else:
        url = QtCore.QUrl('http://www.python.org')
    wb=MainWindow(url)
    wb.show()
    sys.exit(app.exec_())

The Outhouse and the Mall

Wear­ing the soft­ware en­gi­neer's hat: Code is the most triv­ial and the least im­por­tant part of a fea­ture.

—Michael Ia­trou

Michael tweet­ed that, I replied, he replied, but what the heck, I think some­times things can be bet­ter ex­plained in more than 140 char­ac­ter­s, thus this post [1].

So, why the mall and the out­house? Be­cause when we talk about soft­ware and code and fea­tures, we are not all talk­ing about the same thing. Imag­ine if I told you that bricks are triv­ial. Af­ter al­l, they have ex­ist­ed in their cur­rent form for thou­sands of years, they are pret­ty sim­ple to man­u­fac­ture, and have no in­ter­est­ing fea­tures, re­al­ly, ex­cept a cer­tain re­sistence.

Now, sup­pose you are build­ing an out­house. Since you are a fun­ny guy, you want to build an ac­tu­al brick out­house so you will use bricks to do it.

Now, since bricks are so bor­ing, you may feel com­pelled to be­lieve bricks are the least im­por­tant part of your ed­i­fice, and that the over­all de­sign is more im­por­tan­t. Should you carve a moon-shaped hole in the door? How deep should the la­trine be?

How­ev­er, that po­si­tion is fa­tal­ly flawed, since if you ig­nore those triv­ial, bor­ing brick­s, all you have is shit in a hole in the ground. That is be­cause you are con­sid­er­ing the bricks as just a mean to your end. You on­ly care about the bricks in­so­far as they help you re­al­ize your grand out­house vi­sion. I am here to tell you that you are wrong.

The first way in which you are wrong is in that ar­ti­fi­cial sep­a­ra­tion be­tween means and end­s. Ev­ery­one is fa­mil­iar with the eth­i­cal co­nun­drum about whether the ends jus­ti­fy the mean­s, but that's garbage. That'swhat you say when you try to con­vince your­self that do­ing things hap­haz­ard­ly is ok, be­cause what you do is just the means to what­ev­er oth­er thing is the end. Life is not so eas­i­ly di­vid­ed in­to things that mat­ter and things that don't.

Your work, your cre­ation is not just some ide­al iso­lat­ed end to­wards which you trav­el across a sea of dirty mean­s, try­ing to keep your sil­ver ar­mour clean. It's one whole thing. You are cre­at­ing the mean­s, you are cre­at­ing your goal, you are re­spon­si­ble for both, and if you use shod­dy brick­s, your out­house should shame you.

In the same way, if you do crap­py code, your fea­ture is de­meaned. It may even work, but you will know it's built out of crap. You will know you will have to fix and main­tain that crap for years to come, or, if you are luck­y, ru­in your kar­ma by dump­ing it on the head of some poor suck­er who fol­lows your step­s.

I am pret­ty much a ma­te­ri­al­ist. If you re­move the code, you don't have a fea­ture, or soft­ware, you have a con­cep­t, maybe an idea, per­haps a de­sign (or maybe not) but cer­tain­ly not soft­ware, just like you don't have a brick out­house with­out pil­ing some damn bricks one on top of the oth­er.

I al­ways say, when I see some­one call­ing him­self a soft­ware en­gi­neer, that I am mere­ly a soft­ware car­pen­ter. I know my tool­s, I care about them, I use them as well as I can ac­cord­ing to my lights [2] and I try to pro­duce as good a piece of fur­ni­ture as I can with what I am giv­en.

This tends to pro­duce hum­ble soft­ware, but it's soft­ware that has one re­deem­ing fea­ture: it knows what it should do, and does it as well as I can make it. For ex­am­ple, I wrote rst2pdf. It's a pro­gram that takes some sort of tex­t, and pro­duces PDF files. It does that as well as I could man­age. It does noth­ing else. It works well or not, but it is what it is, it has a pur­pose, a de­scrip­tion and a goal, and I have tried to achieve that goal with­out em­bar­ras­ing my­self.

My pro­grams are out­hous­es, made of care­ful­ly se­lect­ed and con­sid­ered brick­s. They are not fan­cy, but they are what they are and you know it just by look­ing at them. And if you ev­er need an out­house, well, an out­house is what you should get.

Al­so, peo­ple tend to do weird stuff with them I nev­er ex­pect­ed, but that's just the luck of the anal­o­gy.

But why did I men­tion malls in the ti­tle? Be­cause malls are not out­hous­es. Malls are not done with a goal by them­selves be­yond mak­ing mon­ey for its builder­s. The ac­tu­al func­tion of a piece of mall is not even known when it's be­ing built. Will this be a Mc­Don­ald­s, or will it be a com­ic book store? Who knows!

A mall is built quick­ly with what­ev­er makes sense mon­ey­wise, and it should look bland and recog­nis­able, to not scare the herd. It's a build­ing made for pedes­tri­an­s, but it's in­tend­ed to con­fuse them and make the path form A to B as long and me­an­der­ing as pos­si­ble. The premis­es on which its de­sign is based are all askew, cor­rupt­ed and self­-­con­tra­dict­ing.

They al­so give builders a chance to make lots of mon­ey. Or to lose lots of mon­ey.

Nowa­days, we live in an age of mall soft­ware. Peo­ple build star­tup­s, get fi­nanc­ing, build crap­py soft­ware and some­times they hit it big (Twit­ter, Face­book) or, more like­ly, fade in­to ob­scu­ri­ty leav­ing be­hind noth­ing at al­l, ex­cept mon­ey lost and sad pro­gram­mers who spent nights cod­ing stuff noone will ev­er see or use, and not much else.

Far from me say­ing star­tups are not a no­ble or wor­thy en­deav­our. They are! It's just that peo­ple who work on them should re­al­ize that they are not build­ing soft­ware. That's why code does­n't look im­por­tant to them, be­cause they are ac­tu­al­ly sell­ing eye­balls to ad­ver­tis­er­s, or col­lect­ed per­son­al da­ta from their users to who­ev­er buys that, or cap­tive pub­lic for game de­vel­op­er­s, or what­ev­er your busi­ness mod­el says (if you have one!).

They are build­ing mall­s, where the val­ue is not in the build­ing, which is pret­ty ghast­ly and use­less by it­self, but on the peo­ple in it, those who rent space in the mal­l, those who will use the mal­l, the soft­ware, the so­cial net­work, what­ev­er it is you are build­ing.

Twit­ter is not soft­ware, Face­book is not soft­ware. If they were, iden­ti.­ca and di­as­po­ra would be big­ger! What they are is peo­ple in one place, like a mall is not a re­al build­ing, but a col­lec­tion of peo­ple un­der a roof.

So, there is noth­ing wrong with build­ing mall­s. Just re­mem­ber that your ends and your means are one and a whole, that code is im­por­tan­t, that with­out code Face­book and Twit­ter don't work, and that with­out peo­ple they are a bad­land, and know what you are do­ing.

Be­cause the on­ly hard thing in life is know­ing what you want to do. The rest is the easy part. And be­cause malls with­out toi­lets suck.


Contents © 2000-2023 Roberto Alsina