Extending Kate With Pâté

November 12th, 2008

Pâté is a plugin for Kate (of KDE Desktop) that exposes editor’s functionality to Python. In short; with Pâté, you can write Kate plugins in Python.

I use Kate for (almost) all my text editing. I think it suits my needs perfectly. It is both as simple as I would be comfortable with and has as many features (such as multi document interface, regex search and replace, etc.) as I need to be productive. I am not an IDE person. Nothing against IDE’s, I have just never been comfortable with them. On the other side I have never taken the time to learn the classic (read antique) editors such as Emacs and Vim. I am sure learning them would be worth my time. But I doubt I will ever take the time for that. At present, I just fire up my Kate and it works pretty well.

Meanwhile, I keep hearing about these neat hacks with Emacs that when you do C-t, C-M-w and then C-k your active buffer is translated to Chinese and then automatically sent to your grandmother’s cell phone as SMS. Wow! And you can write your own macros (in Lisp, elisp) to extend the editor’s capabilities. There is virtually no limit to what you can do and it is not rare that these extensions exceed the borders of an editor. Of course you have to use (and learn) Emacs to take advantage.

This was true, before I discovered Pâté. It was always possible to write plugins for Kate, but Kate was not scriptable. Now using Pâté, you can extend Kate with ease (at least much much easier than writing C). The only thing that bugs me is I couldn’t figure out a way to reload my plugins without restarting Kate.

Creating Simple Pâté Plugins

The first plugin we write will turn the selection into a Django password hash. I use this when I want to create initial_data fixtures for User‘s quickly.

Since we do not want to instantiate a complete Django environment we won’t be able to import anything from django.contrib.auth.models. Instead let us copy a dumbed down version of get_hexdigest into our own module.

import random


def get_hexdigest(algorithm, salt, raw_password):
    try:
        import hashlib
    except ImportError:
        import sha
        return sha.new(salt + raw_password).hexdigest()
    else:
        return hashlib.sha1(salt + raw_password).hexdigest()

It should be clear enough, it returns a hash of the given password using the given salt[1]. Now we simply add kate to our imports (remember kate and not pate):

import kate

And add our own callback code:

@kate.onAction('Django Password', 'Shift+Alt+P')
def setPassword():
    v = kate.view()
    raw_password = v.selection.text
    v.selection.removeSelectedText()
    algo = 'sha1'
    salt = get_hexdigest(algo, str(random.random()), \
        str(random.random()))[:5]
    hsh = get_hexdigest(algo, salt, raw_password)
    v.insertText('%s$%s$%s' % (algo, salt, hsh))

The first line makes our function kate-aware. 'Django Password' will be the label for our menu item (it will be listed under Tools) and Shift-Alt-P will be the keyboard shortcut. The rest of the code should be self-explanatory.[2]

Now we copy our module into ~/.kde/share/apps/kate/pyplugins/ and restart Kate. It should show up in the menu and work now.

Second example is a JSON prettifier. I use JSON format for my fixtures, but valid JSON is not very readable. So I have this small plugin to convert a document between JSON and Python literals:

import sys, pprint
import kate
from django.utils import simplejson


HEADER = '# Pretty Printed\\n'

@kate.onAction('Django Pretty Json', 'Shift+Alt+J')
def togglePrettyJsonFormat():
    d = kate.document()
    source = d.text
    if source.startswith(HEADER):
        target = simplejson.dumps(eval(source))
    else:
        pp = pprint.PrettyPrinter(indent=2)
        target = HEADER + pp.pformat(simplejson.loads(source))
    d.text = target

I need HEADER to distinguish between two states. It can actually be anything, but it would be a good idea to make it a quote to have valid Python just in case.

Pâté Is Fun

I have enjoyed experimenting with Pâté. I hope it gets more attention and therefore ends up a much better plugin. If you ask me it should already ship with Kate. Kate is a nice editor, and empowering the users would only make it nicer and more popular.

If you have any ideas for pâté plugins, especially stuff that is useful in Django context, please add it to the comments. I would love to play a little more with pâté.


1: If it is not clear, take a look at django.contrib.auth.models.

2: To learn API, you can fire up Interactive Console under View and type help(kate).

Bookmark and Share

Dictionary Key Validation, Lists, Sets and Iterators

November 5th, 2008

Recently I needed to check if a Python dictionary has a specific set of keys exactly or not. I made some tests to see which method works best. For the sake of simplicity, I’ve compared keys of two dictionaries instead of a dictionary’s keys against a sequence of keys.

All of the following test results are averages of 10 repetitions of the given code, in the form of:

sum(timeit.Timer(<test_code>).repeat(10))/10.0

First, I did some simple tests with small sizes:

timeit.Timer('l1.sort();l2.sort();l1==l2', 'l1,l2=[1,2,3], [3,2,1]')
>>> 1.95

timeit.Timer('set(l1)==set(l2)', 'l1,l2=[1,2,3], [3,2,1]')
>>> 2.85

The second one with set‘s seems more intuitive to me. But it’s slower than the one with lists. Obviously you need to sort the lists before comparison. Because a dictionary’s keys are not ordered, and therefore its keys() would return a list with unpredictable order of items. On the other hand a set, even though it is unordered, would return the same hash for the same keys.

Now, what happens if we work on a larger number of items:

timeit.Timer('l1.sort();l2.sort();l1==l2', 'l1,l2=range(100), \
    [100-i for i in range(100)]')
>>> 15.49

timeit.Timer('set(l1)==set(l2)', 'l1,l2=range(100), \
    [100-i for i in range(100)]')
>>> 25.13

More or less the same results. Actually both of these tests are biased towards lists. We initialize our test data as lists. So while the first tests only consist of in-place sorting and comparison, the second ones involve creation of new set objects and comparison.

Let us see what happens when we start with actual dictionaries and do the same comparisons:

timeit.Timer('l1,l2=d1.keys(),d2.keys();l1.sort();l2.sort();l1==l2', \
    'd1,d2=dict([(str(i), i) for i in range(3)]),dict([(str(3-i), 3-i) \
    for i in range(3)])')
>>> 2.49

timeit.Timer('set(d1.keys())==set(d2.keys())', 'd1,d2=dict([(str(i), i) \
    for i in range(3)]),dict([(str(3-i), 3-i) for i in range(3)])')
>>> 2.83

And for comparison the first test without sorting (of course it evaluates to False):

timeit.Timer('d1.keys()==d2.keys()', 'd1,d2=dict([(str(i), i) \
    for i in range(3)]),dict([(str(3-i), 3-i) for i in range(3)])')
>>> 1.11

We can clearly see initializing sets takes slightly longer than in-place sorting. But what happens if we work with more keys:

timeit.Timer('l1,l2=d1.keys(),d2.keys();l1.sort();l2.sort();l1==l2', \
    'd1,d2=dict([(str(i), i) for i in range(10)]), \
    dict([(str(10-i), 10-i) for i in range(10)])')
>>> 5.21

timeit.Timer('set(d1.keys())==set(d2.keys())', \
    'd1,d2=dict([(str(i), i) for i in range(10)]), \
    dict([(str(10-i), 10-i) for i in range(10)])')
>>> 5.59

When we increased item number to ten performance gap decreased. This is probably because while set comparisons are based on hashes as I mentioned before, list comparisons are item-by-item comparisons due to its mutability. Let’s go even higher and see if the difference becomes more clear:

timeit.Timer('l1,l2=d1.keys(),d2.keys();l1.sort();l2.sort();l1==l2', \
    'd1,d2=dict([(str(i), i) for i in range(25)]), \
    dict([(str(25-i), 25-i) for i in range(25)])')
>>> 16.21

timeit.Timer('set(d1.keys())==set(d2.keys())', \
    'd1,d2=dict([(str(i), i) for i in range(25)]), \
    dict([(str(25-i), 25-i) for i in range(25)])')
>>> 9.79

This time sets are almost 100% faster. It seems sets perform better than sorted lists overall. They are not much slower at lower item counts and significantly faster at higher item counts. I dawned on me when using sets I can take advantage of iterators instead of creating list objects. Here are the results for all three sizes:

timeit.Timer('set(d1.iterkeys())==set(d2.iterkeys())', \
    'd1,d2=dict([(str(i), i) for i in range(3)]), \
    dict([(str(3-i), 3-i) for i in range(3)])')
>>> 2.07
timeit.Timer('set(d1.iterkeys())==set(d2.iterkeys())', \
    'd1,d2=dict([(str(i), i) for i in range(10)]), \
    dict([(str(10-i), 10-i) for i in range(10)])')
>>> 3.79
timeit.Timer('set(d1.iterkeys())==set(d2.iterkeys())', \
    'd1,d2=dict([(str(i), i) for i in range(25)]), \
    dict([(str(25-i), 25-i) for i in range(25)])')
>>> 8.92

It performs best for all sizes. In addition it is quite intuitive and readable. I think I have found the foundation of my comparison function, it should look similar to this:

if set(d.iterkeys())==set(sequence_of_keys):

And here is a chart combining all results:

Performance Comparison

PS: Yes, the prompts (>>>‘s) are in the wrong place. It is intentional. I’m not trying to implement python prompt in Markdown, it just looks better to me this way.

Bookmark and Share

Dieting Again

October 28th, 2008

I’ll be dieting for two months starting November 1st. Everything is planned out and ready. I am mentally prepared as well. You know, being underslept, undernutritioned and tired, all at the same time is demanding. I can’t say it is much fun, but I don’t really dread it. I have gained 4 kgs since the last diet. I already look like a fat pig to myself. So it is just time for another.

I frequently hear “why do go through all this, it is really unnecessary”. Why do people say that? I find it really annoying. What could be more natural for someone to take care of one’s pysical existance? You do brush your teeth right? How is staying fit different or less important than that? Let us hang 15 kgs of fat around your waist and you live with it just for a week. And see if your opinion changes.

Bookmark and Share

Getting A Little Further Than Hello World With Qooxdoo

October 17th, 2008

I have mentioned about Rich Internet Applications in a previous post. Qooxdoo is an AJAX framework, especially strong on creating desktop-like GUI’s. It allows you to build your interface in an object oriented manner. Like tkinter or GTK, and much more than the others it is like swing.

Qooxdoo is well documented and has a clean API. It comes with a Python program to help with builds. Because it is such a big framework you test on a partially compiled source and when finished this build program generates a single (actually two, it also generates a loader), compact (and somewhat obfuscated) file for performance. I strongly advise you to give it a try. The following is a small introductory tutorial. It aims to go little further than Hello World. This is not a tutorial explaining object oriented programming concepts, I assume you are already fluent in OOP.

Create The Skeleton

Screenshot of finished application

Screenshot of finished application

We’ll create a simple calculator-like application. I am assuming you have downloaded (latest version is 0.8 for today) and extracted the source into a directory. Let’s call it qxtut. The first thing we will do is to create a skeleton of our application with the following command;

./qooxdoo-0.8-sdk/tool/bin/create-application.py --name basicmath

This command has created the following directory structure under ./basicmath;

qxtut/
  qooxdoo-0.8-sdk/
  basicmath/
    source/
    build/
    cache/
    api/
    config.json
    Manifest.json

Well, some of the directories (build & cache) are not there yet. But as we go on they will appear, I just wanted you to see how the application is organized. config.json and Manifest.json files are configuration files for the build tool. We don’t need to change them for this tutorial, but you are welcome to check their contents.

Let us build our source for the first time;

cd basicmath
./generate.py source

Now if you open ./source/index.html in your browser you can see a Hello World application in action. We’ll replace this with our own program. But before we proceed I’d like to point out a few things;

  • When we define our classes, we call a class method define on qx.Class and pass our class’ name (along with its namespace) and its contents as arguments. We don’t define our classes using prototypes, in order to take advantage of Qooxdoo’s object oriented programming features.
  • Qooxdoo supports single inheritance (with mixins). We define our base class, if we have one, using extend key.
  • Instance members are defined inside the members key and class members are defined inside the statics key.
  • construct and destruct are two special functions for initialization and cleanup of the class.
  • Qooxdoo supports [properties](http://en.wikipedia.org/wiki/Property_(programming)) as well, but it is outside of this tutorial’s scope.
  • Finally, “#asset(basicmath/*)” line tells the build program to include assets (images etc) in qxtut/basicmath/source/resource/basicmath directory.

Custom Classes

Let’s start building our application now. Here is a compact version of Application.js;

/* ************************************************************************
#asset(basicmath/*)
************************************************************************ */

qx.Class.define("basicmath.Application", {
    extend: qx.application.Standalone,
    members: {
        main: function()
        {
            this.base(arguments);
            if (qx.core.Variant.isSet("qx.debug", "on")) {
                qx.log.appender.Native;
                qx.log.appender.Console;
            }
            // Our code will come here
        }
    }
});

Now we will create a custom class named Operation. This class will take two operands and perform an operation on them, and then later we will add it the ability to report the result of the operation. Paste this as Operation.js;

/* ************************************************************************
#asset(basicmath/*)
************************************************************************ */

qx.Class.define("basicmath.Operation", {
    extend: qx.ui.container.Composite,
    construct: function() {
        this.base(arguments);
        var layout = new qx.ui.layout.HBox(6);
        this.setLayout(layout);
        this.operand1 = new qx.ui.form.TextField("0");
        this.operator = new qx.ui.form.SelectBox();
        this.operator.add(new qx.ui.form.ListItem("add"));
        this.operator.add(new qx.ui.form.ListItem("subtract"));
        this.operator.add(new qx.ui.form.ListItem("multiply"));
        this.operator.add(new qx.ui.form.ListItem("divide"));
        this.operand2 = new qx.ui.form.TextField("0");
        this.result = new qx.ui.basic.Label("0");
        this.add(this.operand1);
        this.add(this.operator);
        this.add(this.operand2);
        this.add(this.result);
    },
    members: {
        operand1: null,
        operator: null,
        operand2: null,
        result: null
    }
});

The code should be self explanatory. Notice here, we define operand1, operator, operand2 and result as members of the class. Also notice we initialize those members in the constructor and not in the class body1. This is because their respected values (classes) are derived from the non-primitive Object type. Therefore if we have assigned them a non-primitive type (such as the array [1, 2, 3]) in the members section; all instances would point to the same object.

Let us now plug this object in our application. Replace the comment line “// Our code will come here” with the following;

this.getRoot().add(new basicmath.Operation);

Now, when we compile the source and run index.html we should see our widgets in place.

Screenshot of an Operation widget we have just created

Screenshot of an Operation widget we have just created

Simple Behaviour

We want our widget to calculate the operation and show us the result. Let’s update the members section of Operation with the following;

members: {
    operand1: null,
    operator: null,
    operand2: null,
    result: null,
    updateResult: function() {
        var v1 = this.cleanField(this.operand1);
        var v2 = this.cleanField(this.operand2);
        var r;
        switch(this.operator.getValue()) {
            case "add": r = v1+v2; break;
            case "subtract": r = v1-v2; break;
            case "multiply": r = v1*v2; break;
            case "divide": r = v1/v2; break;
        }
        this.result.setContent(String(r));
        this.operand1.setValue(String(v1));
        this.operand2.setValue(String(v2));
    },
    cleanField: function(field) {
        var val = parseInt(field.getValue());
        return isNaN(val) ? 0 : val;
    }
}

We have added two functions here updateResult and cleanField. Now we make use of them, change the construct with the following;

construct: function() {
    this.base(arguments);
    var layout = new qx.ui.layout.HBox(6);
    this.setLayout(layout);
    this.operand1 = new qx.ui.form.TextField("0");
    this.operand1.addListener("input", this.updateResult, this);
    this.operator = new qx.ui.form.SelectBox();
    this.operator.add(new qx.ui.form.ListItem("add"));
    this.operator.add(new qx.ui.form.ListItem("subtract"));
    this.operator.add(new qx.ui.form.ListItem("multiply"));
    this.operator.add(new qx.ui.form.ListItem("divide"));
    this.operator.addListener("changeValue", this.updateResult, this);
    this.operand2 = new qx.ui.form.TextField("0");
    this.operand2.addListener("input", this.updateResult, this);
    this.result = new qx.ui.basic.Label("0");
    this.add(this.operand1);
    this.add(this.operator);
    this.add(this.operand2);
    this.add(this.result);
}

We just added these three listeners to update the result when an operand or the operator changes;

this.operand1.addListener("input", this.updateResult, this);
this.operator.addListener("changeValue", this.updateResult, this);
this.operand2.addListener("input", this.updateResult, this);

The last parameter (this) for addListener (even though it is sometimes unnecessary) set the scope within the listener code (the second parameter). Qooxdoo handles most of the binding automatically, I think this is included for flexibility.

Let’s compile and run again. The result of the operation should update as you change the values now.

Events To Tie All Together

I’ll give you the finished code first and then we can go over the details. Here is Operation.js;

/* ************************************************************************
#asset(basicmath/*)
#asset(qx/icon/Oxygen/*)
************************************************************************ */

qx.Class.define("basicmath.Operation", {
    extend: qx.ui.container.Composite,
    construct: function() {
        this.base(arguments);
        var layout = new qx.ui.layout.HBox(6);
        this.setLayout(layout);
        this.operand1 = new qx.ui.form.TextField("0");
        this.operand1.addListener("input", this.updateResult, this);
        this.operator = new qx.ui.form.SelectBox();
        this.operator.add(new qx.ui.form.ListItem("add"));
        this.operator.add(new qx.ui.form.ListItem("subtract"));
        this.operator.add(new qx.ui.form.ListItem("multiply"));
        this.operator.add(new qx.ui.form.ListItem("divide"));
        this.operator.addListener("changeValue", this.updateResult, this);
        this.operand2 = new qx.ui.form.TextField("0");
        this.operand2.addListener("input", this.updateResult, this);
        this.result = new qx.ui.form.TextField("0");
        this.result.setReadOnly(true);
        var close_button = new qx.ui.form.Button(
            null,
            "qx/icon/Oxygen/16/actions/application-exit.png"
        );
        close_button.addListener("execute", function(e) {
            this.fireDataEvent(
                "changeResult",
                0,
                parseFloat(this.result.getValue()),
                false
            );
            this.destroy();
        }, this);
        this.add(this.operand1);
        this.add(this.operator);
        this.add(this.operand2);
        this.add(new qx.ui.basic.Label("="));
        this.add(this.result);
        this.add(new qx.ui.core.Spacer(8));
        this.add(close_button);
    },
    events: {
        "changeResult": "qx.event.type.Data"
    },
    members: {
        operand1: null,
        operator: null,
        operand2: null,
        result: null,
        updateResult: function() {
            var v1 = this.cleanField(this.operand1);
            var v2 = this.cleanField(this.operand2);
            var r;
            switch(this.operator.getValue()) {
                case "add": r = v1+v2; break;
                case "subtract": r = v1-v2; break;
                case "multiply": r = v1*v2; break;
                case "divide": r = v1/v2; break;
            }
            this.fireDataEvent("changeResult",
                r,
                parseFloat(this.result.getValue()),
                false
            );
            this.result.setValue(String(r));
            this.operand1.setValue(String(v1));
            this.operand2.setValue(String(v2));
        },
        cleanField: function(field) {
            var val = parseInt(field.getValue());
            return isNaN(val) ? 0 : val;
        }
    }
});

And Application.js;

/* ************************************************************************
#asset(basicmath/*)
#asset(qx/icon/Oxygen/*)
************************************************************************ */

qx.Class.define("basicmath.Application", {
    extend : qx.application.Standalone,
    members : {
        main : function()
        {
            this.base(arguments);
            if (qx.core.Variant.isSet("qx.debug", "on")) {
                qx.log.appender.Native;
                qx.log.appender.Console;
            }
            var layout = new qx.ui.container.Composite(
                new qx.ui.layout.VBox(8)
            );
            var layout_footer = new qx.ui.container.Composite(
                new qx.ui.layout.HBox(6)
            );
            var total = new qx.ui.basic.Label("0");
            var add_button = new qx.ui.form.Button(
                "Add New",
                "qx/icon/Oxygen/16/actions/list-add.png"
            );
            add_button.addListener("execute", function(e) {
                var new_operation = new basicmath.Operation();
                layout.addBefore(new_operation, layout_footer);
                new_operation.addListener("changeResult", function(e) {
                    var old_total = parseFloat(total.getContent());
                    var new_total = old_total - e.getOldData() + e.getData();
                    total.setContent(String(new_total));
                }, this);
            }, this);
            layout_footer.add(add_button);
            layout_footer.add(total);
            layout.add(layout_footer);
            add_button.execute();
            this.getRoot().add(layout);
        }
    }
});

Let’s go top-down and begin with the changes in the Application.js;

var layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(8));
this.getRoot().add(layout);

We don’t necessarily need to subclass everytime we need specialized behaviour. Since I had intented to re-use Operation I have it as a seperate class. But for the layout of the application I just instanciated some classes and tweaked them inside Application.main. layout here is the topmost widget, we will put everything else in it. Basically one or more Operation‘s and a footer to dispay the grand total. VBox layout is by the way a layout manager that stacks children vertically, and a HBox stacks horizontally.

var layout = new qx.ui.container.Composite(new qx.ui.layout.VBox(8));
var layout_footer = new qx.ui.container.Composite(new qx.ui.layout.HBox(6));
var total = new qx.ui.basic.Label("0");
var add_button = new qx.ui.form.Button(
    "Add New",
    "qx/icon/Oxygen/16/actions/list-add.png"
);
add_button.addListener("execute", function(e) {
    var new_operation = new basicmath.Operation();
    layout.addBefore(new_operation, layout_footer);
    new_operation.addListener("changeResult", function(e) {
        var old_total = parseFloat(total.getContent());
        var new_total = old_total - e.getOldData() + e.getData();
        total.setContent(String(new_total));
    }, this);
}, this);

We define a label total to hold the grand total of all operations and an add button for new operations. Notice the closures work on 7th line. Although we limit ourselves a little bit to take advantage of OOP, we are still in a dynamic environment. Finally we tie all these components together and finally add the layout to the application root.

add_button.execute();

This instantiates the first Operation for us. We execute the button instead of creating the widget programmatically to avoid the code duplication (see the “execute” listener on the add_button).

Now let’s take a look at the changes in Operation.js. I have replaced the result Label with a TextField (remember to run “generate.py source” each time dependencies change). I wanted to take advantage of the getOldData function on TextField‘s changeValue event. But appereantly it doesn’t supply the old data. But I kept it as a TextField anyway, setting it read-only.

Then I decided that Operation should signal for a result change (maybe this is more politically correct) and added a custom event changeResult on it.

events: {
    "changeResult": "qx.event.type.Data"
}

This event is fired inside Operation.updateResult;

this.fireDataEvent(
    "changeResult",
    r,
    parseFloat(this.result.getValue()),
    false
);

The second parameter is returned from e.getData() and the third is from e.getOldData(). Therefore we can calculate the grand total without iterating all Operations;

var new_total = old_total - e.getOldData() + e.getData();

An important point here is to fire changeResult to correct the grand total before we destroy an Operation;

close_button.addListener("execute", function(e) {
    this.fireDataEvent(
        "changeResult",
        0,
        parseFloat(this.result.getValue()),
        false
    );
    this.destroy();
}, this);

Wrapping Up

Now it should work correctly, if I haven’t made a typo of course. Let us build it with;

./generate.py build

It generates a loader (~150kb) and an application script (~400kb). You don’t need the Qooxdoo source anymore, you can just upload the contents of the build directory and your application would run.

This concludes my a little further than Hello World tutorial. If you find errors or typos, or have any questions please leave a comment here or contact me at muhuk@jabber.org.


1: Here is an explanation in Qooxdoo manual.

Bookmark and Share

My Little Distractions

October 16th, 2008

I am not one of hyper-connected people who uses ten different micro-blogging services and maintains their profile in six different social networks and, just for the old times sake, frequents in certain forums. No, I’d rather just finish my work and then if I have time I can read a blog post or two (play). But most of the time I have my IRC client (#fazlamesai @ irc.freenode.net) and Plurk open. For short distractions. I don’t have a very long attention span, and I need short distractions frequently or else I get a headache.

I think RSS is one of the most helpful tools of web. It is easy, efficient and non-distractive. If you choose who you follow well, you would have an endless flow of useful information. I think everyone should use RSS. If you are not already following any feeds, you can start with Fazlamesai (in Turkish) or Slashdot.

First thing IRC reminds me is thousands of people sitting in front of computers asking each other ASL? or doing some other useless small talk. Let me add almost none of those people in this picture knows for sure whether the others are actually who they say they are. And a significant number of them just fake their identity (such as a thirty nine year old guy impersonating a eighteen year old girl). Not a healthy environment. But if you can turn your head from all the fun these chat rooms offer you can find chat rooms, for example, where open source people gather and help each other. Just find and enter your favourite software’s chat room, #python, #firefox, #debian…

I started using Plurk, first becase I was curious about amix‘s startup. Then I decided to stay a little longer to further analyze how they have integrated game elements in their application. Then I kept on Plurking to get to know the people (early adapters) and to witness the evolution of a social network. I still use Plurk almost everyday, because it gives you the feeling that it is working1.

Plurk is a so-called persistent chat application. You can think of it as a cross-breed between instant messaging and e-mail. It is possible to carry on real-time conversations, but when you come back later you can still find (and participate) older discussions. I think you can do this with other micro-blogging applications (such as Twitter), at least with their desktop clients. With Plurk this is possible in the browser.

It is not just AJAX that makes Plurk an attractive service. It has an elegant and feature rich interface. Posts are laid out (horizontally) on a timeline that you can scroll with your mouse. When you click on one of the posts it expands itself down to show you the comments and other details. No page loads. You can also fully customize the interface, change the appereance, hide some elements, add new ones… If you check out Plurk, you will see it is designed to be fun.

I think introducing this fun element is very important. We don’t do micro-blogging because we need to, it doesn’t solve any of our everyday problems. We do it because we want to socialize and have fun. Fun is normally a product of social interaction. But if you can make the interaction itself fun as well, that is so much better. Plurk has badges and a karma system. Basically you earn karma if you plurk frequently and in quality. The more karma you earn the more you quirks you get, such as adding a profile title or unlocking smileys. Now, this may sound silly to you. Why should we care? Well, you might not care, but in general people do care. Playing is the first complex skill we have learned, so we have a natural tendency to play even in adulthood.

There is one last thing I would like to mention. I think this is important too, for a successfull web service. Plurk changes, (naturally) it changes in place, and it changes to conform your usage. One example for that is when I noticed the “US Elections 2008″ tab. It just appeared next to “All Plurks”, “My Plurks”, “Private” tabs one day. That’s a nice thing. But, wait there’s more. The next day an X appeared on the right of that tab. Not being an U.S.A citizen I clicked it and the tab disappeared. Which is also nice. But, wait there’s even more. The next day the elections tab didn’t show up again. Nothing, fancy. Just as one would have expected. But very important. Does many of your everyday services pay special attention to details like this. Users do, at least in a sub-conscious level. It is like having a new piece of clothing and feeling that you have been happily wearing it for years. It just fits.

So, what was I saying? Little distractions. But you need to manage them. You want a short cool off period, not losing focus all together. Plurk is a nice nice service. IRC still has something to offer. RSS is the king for me. But I still have work to do now. Introducing distractions without proper discipline quickly turn into inefficiency.


1: This deserves a post of its own.

Bookmark and Share

Happy Birthday Fazlamesai!

October 9th, 2008

Fazlamesai.net, the geek hub of Turkey, is now 8 years old. Happy anniversary, we love you!

Bookmark and Share

V For Volatile

September 30th, 2008

Let’s assume we are buying a business software that will meet some business needs for our business. It is a multi user enviroment, so we opt for a product with a Client/Server architecture. What to ask for, other than the obvious requirements for the task?

I would, first of all, ask for ease of deployment. What good is a software if you can’t run it? Ease of deployment for both the clients and the server. But clients here are more important for two reasons; they will be many, and they will be installed on desktop computers in the wild. As opposed to one server, which is (hopefully) maintained by professionals. So I would like my new software to be easily installed, uninstalled and upgraded on any computer connected to my network.

I would also like to be able to access the software through any client connected. It is a bad bad security model to rely on authentication against a client installation. Each and every client should be identical and the authentication should be done against some form of credentials1.

As a user I would also like to know that the client software will not mess with the rest of my system. Software breaking2 other software, or the entire operating system is useless, no matter how much utility it has otherwise.

I guess you have realized by now where I am going with this. The system I have described above is unlikely a big over-burdened application that radiates seriousness from your screen and clings itself into the bowels of your Operating System. I am actually talking about a volatile client and server architecture. Where the client almost doesn’t exist. It exists of course. It resides on the server until you connect with your browser3, then get downloaded and run. This model is called Rich Internet Application, RIA for short.

The key characteristics of RIA are;

  • No install or uninstall4.
  • Automatic remote upgrades.
  • [Sandboxed](http://en.wikipedia.org/wiki/Sandbox(computersecurity))

Rich Internet Applications are powerful and considerably easier to deploy. They do have one drawback; the platform (HTML+CSS+JavaScript) they run on is not designed for applications from the beginning. It is initially designed for documents and then extended to support for applications. The main practical problem is that the JavaScript implementations are not as fast and reliable (on high loads) as native code or other popular interpreted languages. But we have started to see a new breed of browsers specifically built for applications. I am sure JavaScript virtual machines will be improved significantly in the near future.

On the other hand benchmarking and measuring performance by actually using and application typically yields different results. In our example, business applications do not involve heavy operations that require a lot of processing power. Non-linear video editing does, image synthesis does, desktop publishing does, business applications doesn’t. So the RIA would still be my first choice even with the possible performance problems mentioned above.

More on this later.

Bookmark and Share

Does Django Need An Overhaul?

September 26th, 2008

When I wrote Freedom is Power, I was pissed off because of the extra work of extracting all the info into the context for Django’s templating system doesn’t support enough logic. You have to create many little variables to be inserted only once in the template, while you could have just passed the model instance itself. Coding and maintenance becomes harder.

I have done some thinking over this but I am still not sure. If you’re doing both the views and templates yourself then it can be overkill. Just like static typing, or public/private/protected discrimination. On the other hand for design-heavy projects, where there is a seperate template designer who doesn’t know/care much about programming it makes sense. Still the possibility of having adequate presentation logic in templates is a good thing. It shouldn’t hurt anyone.

Last week (jinja dev) Armin Ronacher has posted an article named “Why Jinja is not Django and why Django should have a look at it“. It is a comprehensive comparison of internal workings of both templating engines. Inevitably it is a little biased, but I think each argument is valid and important. If you are into Django, it is a good read. There is also some speed comparison to back it up.

I am currently working on a project using Django. But I don’t touch the templating engine for this one. Everything served as JSON. :D

Bookmark and Share

Social Networking Done Right

September 22nd, 2008

Let me just say that the social media, or more technically loser generated content is inherently flawed. That is of course a polite way of saying it is screwed. But I am not a polite person, so there you have it. I have found a CNET article explaining why. It is a short, good read. So I won’t go into details why social networking sites are crap.

But I would like to take the first argument from the article and elaborate on it a little bit; There’s nothing to do there (in a social network). Social networks like MySpace, Facebook and Orkut are based on creating a profile and associating other profiles with it. Thus you get to show everybody how many friends you have, you get to sell them and poke them and bite them (via Facebook applications), you get to rate them (in Orkut) and finally you get to send them private (and otherwise) messages. You can do all this stuff, so these social networks must be invaluable tools for our social life, right? Let’s take a closer look.

Last time I checked, relationships were more about quality than quantity. Who cares if you have 10 or 1000 friends in your profile? Having a huge friend count doesn’t even make you a popular person as far as I’m concerned. Because I know people who just pop out of nowhere and request to be friends with me. Unsurprisingly they have a high friend count themselves, go figure!

I think relationships are about sharing experiences. Social networks like Facebook give you that opportunity. In a twisted sick way though. For example you can poke your friends. Think about how much you can strenghten your relationships by poking people. And not only that, thanks to the application API you can buy and sell your friends as pets or you can suck their blood till they become vampires. Just the constant stream of invitations makes up a great experience. But I doubt it is the kind of experience Facebook wants you to have.

But hey, you can use social networks to communicate with people too. Isn’t that a good thing? You can publish your status, you can send your friends private messages. You can even join groups to meet like minded people! Isn’t that cool? …well, no. If you think that’s cool you must have missed the news about something called Internet! You can do all these without a social apparatus and almost always more effectively. You can email people for example. I would suppose people check their email more often and with greater attention than their social web2.0 gadgetry Mainstream social networks are not much more than profile association tools. There is nothing to do there. And noone, other than your friends, cares about your profile decoration. Perhaps not even your friends…

This huge mass of loser generated content reminds me of all that wasted bandwidth over once popular and useless e-mail forwards. When I lashed out to the senders they would be offended and surprised at the same time. How could I reject these wonderful delights Internet has to offer us. Do they still forward? That was before we had hyper-super-wall applications and send-poop applications. Actually I am a big fan of user generated content. There are many blogs for example, not only worth reading but their content is so precious that you can’t just possibly buy a book or take a course to get to that information. These people genuinely have something to tell and they have spent the effort to set-up a proper channel for their valuable voice. There are lame blogs as well, but of course if you’re reading this you already know that. But the bad ones are not strongly connected with the good ones, therefore you don’t even have to notice them. In other words they can not publish stories in your news feed.

There are also social networks that are built around another application. So there is a common goal, or at least something solid to talk about. I divide them into three and a half categories; building, sharing, bookmarking and business.

  • Building applications with social networking are in my opinion most sophisticated and most valuable. Common example is Wikipedia. If you find it hard to spot social networking elements in Wikipedia that is probably because you have only seen the frontend. Wikipedia is a big community, and they form a social network with a wide communication bandwidth. Just google it to find out about how they operate and edit. Another good example to this category is open source software development. They also form a social network, and the networking aspect is much bigger this time.
  • Sharing applications with social networking include Youtube, Flickr, 8tracks and the like. I won’t deny most of the content here is loser generated, just go to a random Youtube video and try to read the comments. But that may be, on an end user level, irrelevant. In these applications networking is not pushed too hard and the sharing mission is fully accomplished.
  • Bookmarking is actually a special case of sharing. Bookmarking applications such as Reddit, Digg and Stumbleupon are built on an incredibly powerful idea of sharing bookmarks. Today we are using WWW for many different things, but generally surfing is still the most prevalent. And hyperlinks and search engines fail to serve well enough for general purpose surfing. Social bookmarking does. You can choose a general or specific category and enjoy an almost endless stream of human reviewed websites.
  • Business oriented social networks form half a category. They are not very different than mainstream friend portfolios. They emphasize business networking and I hear they can be useful. People are more open to meet new people in business context and these sites copy real world interaction successfully, so I don’t put them together with the other time wasters.

If you want a web presence the best way in my opinion is blogging. If you don’t have anything to say, no matter how many social profiles you have and how many times you can twit a day doesn’t really matter, nobody cares. You can also incorporate social media if you like, you can import your RSS into your Facebook profile or ping.fm some your posts. There is no need to spend a lot of time tweaking your profile, it is pointless.

On the other hand social networking is an important concept. It adds value when it is built around another, related service. Take Kongregate for example. I mentioned about how it is structured as a game which forms social network with unique dynamics. Check it out, if you also believe profile association is not the highest point for social networks.

Bookmark and Share

Best Way To Shut Your Customers Up

September 17th, 2008

I have seen two significantly different approaches to the sales problem. The prevalent one is to push it onto the customers even if your product is crap. The other approach is to try to perfect the product, perfect the marketing, perfect the whole user experience. The latter is seldom seen, and for a reason.

I am business illiterate, I won’t even pretend I know the first thing about sales. But inevitably, just like everybody else I am a customer, I try hard to get it right. So I try to understand what happens behind the counter. I don’t think it is as simple as I have the good you have the money let’s trade.

The common business model or sales model looks just like that from the outside. Me Tarzan, you Jane simplicity. But first of all you should notice that the sales person doesn’t have an emotional relationship with the product. I am not talking about romance here, maybe just a strong feeling of the product being really useful, or some personal experience. The reason might just be that the sales person doesn’t try hard enough, or it might be that the product is not really cool anyway. So they either fake it or they enlighthen you with “uhm, I don’t know actually. But…”s.

Another characteristic of the conventional model is that you don’t get proper service after sales. This is mostly because the product is crap and therefore the service costs are higher. Or more dramatically because the seller thinks that the market is a set of buyers. And a subset of those buyers will buy her product. And a subset of that subset of buyers will be dissatisfied. Now if that dissatisfied group is not effective enough to reduce the potential buyers that’s fine. Whether they are satisfied or not she has cashed in. Once this market is saturated, there are other crappy products and new markets for them. I am not making this up, I was explained this and I was assured that it works. Furthermore I have seen it in action personally.

The other, brave model of making it right is rarely seen. Let us be honest here, financially it is not as rewarding as the conventional model. It is difficult to implement and even more difficult to keep alive. But from my point of view this model should be the only one. I don’t care if the seller has earned ten times her investment. As a customer I only care if the product is as good as it was advertised and if I will get proper customer service after the purchase.

When I find a supplier for a certain product that does business the right way, I stop looking around. This prevents you from learning what is new, but it also saves you time otherwise wasted. For some things you just don’t get much from being an expert.

I used to move from one hosting company to another, but for several years I am using Webfaction (formerly python-hosting.com). I am 100% satisfied with their services, and it is not expensive as some of you might think. I don’t know much about the competition now, for the reason I have explained above, but I know there are too many cheap, as in cheap imitation, hosting companies you just regret you have ever known them. Webfaction does it right. And they are still alive and probably growing up[1].

So what do they do right;

  • Their plans are no BS. They give you shell access (for shared hosting too) and all the tools you need. They have installers for popular programs for quick and painless setup. If you need a library the server doesn’t have they kindly install it for you. Finally you can install and run your custom application.
  • They are open and proactive; when a server goes down it is immediately posted to status blog. And posts are updated when the problem is fixed, usually with detailed information about the problem and/or solution. I think being open is huge on customer satisfaction.
  • Their technical support is great. I don’t remember a single issue when my problem isn’t solved somehow. Response times are very good, support people really know their stuff[2] and they are always kind. I have asked many stupid and/or irrelevant questions but they have never disdained or told me to search the forums. This is the biggest reason why I don’t keep an eye out for hosting companies.

Webfaction has started showcasing sites they are hosting. This is a simple, easy way to strenghten the community. Why do I care? Because Webfaction users are like-minded. So there is a community to begin with. Maybe the most important consequence of the do it right model is that you end up creating a community around your product. Let me re-phrase it; a community with you in the center.

Before I sign off I would like to mention another company that know how to shut their customers up, in a good sense. Neosante is an online store for supplements. I have purchased from them many times. They do nothing extraordinary, they just do it right. Everytime. Even when there are many bigger stores selling a wide range of products, this niche store does near-perfect compared to all of them.

I used to work for a price comparison site so I have a rough idea about the market. But those figures shouldn’t mean anything to customers at all. We should have a different set of heuristics for the success of a seller. Take Neosante; I give my orders, they ship it, they ship it right, they ship it on time, I get what I have ordered, I am offered a satisfactory solution if I have a problem, I say this is successfull. Anything else, I stay away.


1: This is because they are smart people.

2: No “have tried turning it off and then on again?” stunts.

Bookmark and Share

Just Enough Chrome?

September 2nd, 2008

Google has announced Chrome as its new open source web browser. Is the much rumoured Google OS? Or just giving back to the open source community as they state it. In any event I think it is good news.

They have a nice comic e-book highligthing the features of chrome. Here is a list of the features I find most interesting (and promising):

  • It is a multiprocess application (instead of the multithreaded approach modern web browsers use). Interesting thing is not only tabs (or HTML renderers) but JavaScript VM’s and plugins are seperate processes too. This is the most important feature IMHO, read more about this in the comic.
  • The interface is almost non-existent. Of course we need to try it out before we decide if it is unobtrusive or literally non-existent. But web browsers are all going towards the direction of minimal interfaces, and I think this is good.
  • It is said to be faster. Faster page rendering, faster JavaScript, faster everything… I don’t know if it will be really considerably faster; but if they implement those enhancements over JavaScript VM it would at least be smoother. The comic has info about the new garbage collection strategy and compilation and running of bytecode.

I am pretty content with the Firefox 3, its new full-screen mode and somewhat intelligent addressbar. It runs quite fast, crashes only once in a while (and almost all the time because of the stupid flash plugin). But Google’s web browser is still exciting news, I will be looking forward to its launch. Don’t forget to check out the presentation.

Bookmark and Share

Sum Of All Your Choices

August 25th, 2008

I used to have a discussion with an old friend about intellectually challenged and destitute of knowledge being happier and whether we should stop trying to become conscious and admit to nearest brainwashing center (TV?). I was the fool to advocate gnothi seauton (know thyself) and a meaningful life. Obviously he had a greater understanding of life then.

There are people (almost everybody)1 who are so oblivious about their miserable life, they are happy. They are not really happy but they are content. Judging by their actions, it is OK for them. Even if they continuously complain. Taking control of their life is simply not a viable option for them. When confronted they would either say they are already in control or give you an endless list of reasons why they can’t. So? Who cares?

I have recently finished reading In the Beginning was the Command Line. I didn’t like it too much in the beginning, but then as I read through I discovered it is a masterfully crafted article and the topic relevance for me is just an artifact. I suggest anyone, not just computer/internet enthusiasts, to read it. It is fun.

The article is about operating systems as interfaces. It has lots of colorful analogies and even some anectodes about interfaces and interaction. It explains that computing is all about building complexity. How UNIX builds complexity from simple components and GUI operating systems, such as Windows and Mac OS, hide complexity behind an interface. The distinction is that you control complexity in command line or you delegate control to your GUI. When you delegate anything you have much less control. You know this, right?

When you are working in the command line all the tools are available, but unmaterialized until you summon them. In a GUI environment all the tools are usually presented as choices and you simply pick one or the other. This reminds me Barry Schwartz’s TED talk; Paradox of Choice. The problem, sometimes, is not not having the right tools but the difficulty of choosing amongst too many2. It is the way of thinking suggested3 by your environment that makes the difference.

Before I say WYSIWYG is a joke, let me explain what it stands for; what you see is what you get. How honest and straightforward isn’t it? And it sometimes even deliver that promise. No, no, to be honest WYSIWYG implementations deliver most of the time, meaning the result is what you intended it to be. But what about those rare occurences of data4 loss or when your entire document corrupts when you try to add or change a small thing?

It drives me crazy when a application WYSIWYG starts to act like what you see is what you will but not necessarly what you want. At that point all the time saving and slick icons and so called wizards don’t mean anything to me. All I can think of is my computer doesn’t follow my orders, this is frustrating. I choose not to use WYSIWYG whenever possible. Explicit is better than implicit.

Before we move on let me be clear on one thing; being in control doesn’t necessarily mean having to do more. On the contrary ditching WYSIWYG should make to process more practical and efficient. You just have to use more brainpower and maybe to a little bit more planning than before. But once you have your setup in place, I assure you it will feel more like home.

I have two plugins loaded for this blog; google-analytics plugin and Markdown. Markdown is a text to HTML conversion tool. You write your document in a specific but intuitive5 format in plain text. All you need is a text editor. You get to work directly on the source, whereas you work on a representation of source in WYSIWYG. So it never blows up, no data loss can occur because you tried to change the formatting. I write my blog entires on Kate (my text editor) using Markdown format. I rarely need to check because it is easy to guess how it will look like when rendered.

So I can’t easily integrate charts6 and stuff, do fancy numbering or change my paper size7. Smart Ascii surely has its limitations. But do you really have to have those features? Just because the neighbour’s kid has?

Choosing plaintext doesn’t only help me keep my sanity, being a simple protocol is a great advantage. Let me give you some examples;

  • You can use regex search and replace on the source, meaning format together with the content. For example you can change all third level headings (“^### (.*)$“) into second level headings (“## \1“).
  • You can render into multiple formats easily. Or you can simply chain; using a HTML to PDF converter you can easily create PDF documents from the same source.
  • Plaintext can be diffed. You can see exactly what changed between two versions of a document. If you use a revision control system, with a little more effort, you get persistent undo (persistent between editing sessions).

This is not an exhaustive list of course. I just wrote what came to my mind at the moment. I use plaintext intensively and daily. It never breaks. You can open a plaintext document on any computer and you will probably be able to do so in the future as well. It is almost a trivial task to open a plaintext document, (very unlikely but) even if you can’t find an application you can write your own. Try Markdown, or any other Smart Ascii format. Once you get the initial (pyschological) barrier I hope you will like being in control.

1: Except of course whoever you are and all the people you know and care.

2: Have you ever worked on 3D Studio Max or Maya? If you are interested in interface design I would recommend you to spend some time on them. And then a little more with Blender.

3: Forced might be truer. And if you disagree here is Shelley’s blog.

4: Usually you lose formatting and not the content. But as far as I am concerned formatting is data as well.

5: Markdown and similar formats are called smart ascii. It is not necessarly ascii, I always use UTF8. But it is IMO smart in the sense that you can both read and edit the source document with the same ease.

6: Actually it is pretty easy. Markdown text can include HTML and using something like Google Charts API you can easily add charts.

7: If I were to produce a PDF or print out; I just pass my Markdown text through Dingus and paste the rendered text into OpenOffice Write. Doesn’t even take 1 minute.

Bookmark and Share

Omnisio

August 22nd, 2008

Omnisio is a video hosting application, one of Y Combinator startups, acquired last month by Google.

Looking at aqcuisitions like this one, I see two things;

  • Improving end user experience (or conversation in some cases) is more important than technical superiority. Omnisio didn’t host uber quality HD videos or 3D sound or anything fancy like that. What made me fall in love with the service is that they simply synchronized (vector) keynotes with the actual video.
  • Execution is king for investors of any kind. This should be no surprise though. Not because they risk their money. But because startup businesses are usually making up their specific business as a whole as they go. So there is not really any substantial know-how, unless you are a copycat.

Check out Startup School 2008 videos on Omnisio.

Bookmark and Share

I Had Everything, And More

August 1st, 2008

It all started with a small serving of my usual whining. I said; “When I eat I gain weight (fat, of course), when I don’t I can’t lift (weights)”. He said; “You need to diet”. I was in the gym, and Bertan was the instructor there. From our previous conversations I knew he was taking my training too seriously. I was working out with intensity, but just for the fun. So I felt the need to clarify my goals; “I just want to eat like an animal”[1]. So it started.

We quickly drafted a plan and decided to start immediately. A 2 month diet period. I was enthusiastic because I was thinking all that food I can ingest, and I had done dieting before (I had lost 16 kg). Easy, right? I wonder if it would mean anything had he told me the truth; that I would suffer so much and feel so bad to the point the hunger would be comforting. I guess it wouldn’t make a difference.

Difficulty Is Not Incidental

At the end of 2 months I have lost 7 kg and took 11 cm off my waist. I would like to note that I lost almost no muscle (only 2 cm from my arms). It is of highest importance that you lose fat and retain muscle mass. So I am very happy with the results, much better than I had expected.

But make no mistake; it was not easy for me. It was especially difficult to sleep. Because you stop eating early (7~8 p.m.). Also because you increase your metabolism to the point where you hear a loud thumping noise when you go to bed. That is your heart rate at rest. This is the key to diet.

Many people believe that the exercise itself is burning the fat. NO! You exercise to increase your metabolism. You typically burn 300 calories per hour with the (aerobic or anaerobic) exercise. How long can you keep doing it? Your basal metabolic rate is likely to be around 1700 KCal/day. Do the math. When you start cutting calories your body reacts trying to utilize energy efficiently. Your metabolism decreases. This is where the cardio training becomes important. You keep yourself active and work against your body to keep burning fat.

Learn Through Pain

Everybody has a different physique, and react differently to individual types of food. I mean junk food, and unhealthy fats in general. But even with the most convenient genetics and lifestyle; sugar messes with your insulin levels and saturated fats find a way to stay within your fat tissues. Then why do we do this to ourselves, why do we keep eating the wrong stuff? I guess besides the obvious reasons[2], we find comfort in our ignorance.

I love cheese, for instance. Not that I am saying it is bad food. But good cheese has a lot of saturated fat. After this last diet, I started eating much less cheese. Being the Pavlov’s dog is nothing to be proud of, but it works. Even the thought of triggers your brain to remember all that pain you had to suffer during the diet. So subconciously you eat less junk. After that it is up to you to conciously eat healthy food. Remember; nothing ventured, nothing gained. Or should I say; no pain no gain. :)

I still workout for fun. But I plan to eat clean and stay in shape. Diet if necessary. Because I am not getting any younger. Next time you are in the supermarket, take a good look at the fat on meats. Some of it is chopped of course. But you can still see the white tissue that serves no real purpose[3]. It gives me creeps when I think I was carrying that 10 kg of fat everywhere, just like the fat on that meat only much bigger. And I still have at least that much on…


1: Ok, this might sound funny now. But come see me working out one day, and you will understand why animal.

2: I am one of those people who eats more when depressed.

3: Have you heard of a drought nearby recently?

Bookmark and Share

Kongai

July 18th, 2008

Kongregate‘s collectable card game Kongai is online. For those who don’t know about Kongregate; it is a flash game portal with social networking with some game elements. Games are (well, some of them) connected to Kongregate through its API and then authenticated users level up (in Kongregate) and win badges for achievements. So I think we can say Kongregate is a game of some sort.

I played Kongai a couple of times. It looks really nice, obviously more time spent on visuals than average flash game. Game is simple. I mean elementary school kid simple. You don’t even need to do basic math most of the time. And the AI is quite stupid. When I have a long range only card and the bot has a short range only card, it just doesn’t occur her to step closer and she keeps getting hit. But, I guess they can easily fix that.

The major flaw of the game, for me, is when you are playing against the computer (the bot) it runs on the server. So you wait. And you wait a lot, something like 8 seconds every turn on my computer (compare this with your action time; <0.1 secs). Even if the bot becomes more intelligent (and fast) this is a big turn off. When you are playing with someone else you will have to double the waiting time and double the discomfort.

But this is all not very important for me. Becuse the game is too simple to be enjoyable anyway. I have been expecting something more like Castlewars. Think about it a collectible Castlewars with a wide selection of possible cards. That would be something I can come back to play again and again.

By the way, there is one game I came back to play every now and then. You know these flash games are all casual games. But this one is so beautifully designed (and executed) you just don’t lose interest so soon. I recommend everyone who likes games to try Gemcraft, I guarantee you money back that you will like it. :)

Bookmark and Share

Freedom is Power

July 14th, 2008

If one sticks too rigidly to one’s principles, one would hardly see anybody. (Agatha Christie)

Web applications are thin client (your browser) applications you access through an intranet or the Internet. Due to initial design of WWW, to create rich applications you need to deal with quite a large number of protocols/languages/concepts. To name a few; you need to know about JavaScript, CSS, HTML (preferably XHTML) and (possibly) JSON to write an AJAX application. This is only client side, the front end.

To preserve sanity, a layered approach that divides and isolates different parts of the application as much as possible is usually preferred. MVC pattern, which stands for Model-View-Controller, is a popular method for layering. Model is the layer where your data storage and manipulation occur. View is the layer where the presentation of model occur. The controller layer is basically the glue between both, where your so-called business rules should be. MVC pattern allows us to change the user interaction or to adapt different back ends with minimal modifications.[1]

I would like to summarize how a web application using MVC pattern works. But before that I need to state that this is an overly simplified model I will present.

  1. When an HTTP request comes to our server a request object is created. This request object might have come modified according to our configuration before we have access to it. For example we might have wanted our request body to be decoded to unicode.
  2. The request object is passed to a view function, depending on our configuration again. URL patterns might be matched against view functions or, rarely, against HTTP method (GET, POST, etc) or an entirely different method can be used to determine which view function to call.
  3. The view function communicates with the model, adds, deletes or modifies the models as needed. Or it might just pull some data out and present it to user.
  4. We can generate the response from our view but, it is preferred to use another template layer. Because that way you can seperate application logic and presentation logic. In that case the view function returns the necessary information into a template object.
  5. The template object receives the data from the view and renders the final response (an HTML page, an XML document, etc).

We can pass data from view to template in a number of ways. We can use dictionaries (hashmaps) with arbitrary objects as values for example. When we return this dictionary from our view function the template objects picks it up and makes the data available by reference to its keys.

What we pass depends on the presentation logic in the template. Just to give you an idea, say we have a query page, where the user simply asks for a particular piece of data and we present it;

  • It will surely need a reference to that data. (Maybe a model instance if you are using an ORM)
  • It may present data conditionally, some parts might be available only to the administrators and invisible to normal users for example. So we would need to pass something to determine these conditions. I’ll come back to that something later.
  • It may have other elements (on the page) that need references to other data. (There might me a dynamically generated menu on certain pages for instance)

This means we would pass three kinds of references to a template; primary data, conditinal information and secondary data. I have mentioned that when we have templates we can seperate application logic (business rules?) and presentation logic. In (beautiful) web framework Django‘s documentation it is said;

…the template system is meant to express presentation, not program logic.

and

We see a template system as a tool that controls presentation and presentation-related logic — and that’s it. The template system shouldn’t support functionality that goes beyond this basic goal.

If we wanted to put everything in templates, we’d be using PHP. Been there, done that, wised up.

I read presentation as laying out output and program logic as inserting/querying/modifying/deleting. This takes us back to the something I would like to discuss. How do we pass information related to conditional rendering. What we pass depends greatly on the functionality of the template engine. Let me illustrate with an example;

We want to display a tip if;

  • A user who is registered for at least 2 weeks ago.
  • Who has less than 5 friends.
  • No other warning, such as a form validation error, is present on the page.

My intuition tells me I would need a reference to the list of errors and the (authenticated) user object in my template. I would like to have a reference to my user object because if I get anything less I might have to modify the view if the requirements change. So the registration date with the friend count would not do. After all we are deciding whether or not to show a tip, we are not doing inserting/querying/modifying/deleting I mentioned earlier.

Suppose we have user and error passed into our template, how do we use them; [2]

{% if user.registered.today().toordinal()
    -user.registered.toordinal() >= 14
    and user.friends.count < 5
    and not errors %}
    <div class="tip"> ... </div>
{% endif %}

Of course this does not work in Django. Django template engine’s if doesn’t have greater-than-or-equal operator, less-than operator and can not combine with truth testing (Instead has a seperate ifequal statement for truth testing). There are two workarounds;

  1. We calculate conditions at the view and pass them as simple boolean values.

    {% if user_registered_for_at_least_two_weeks
        and user_has_less_than_five_friends
        and no_errors %}
        <div class="tip"> ... </div>
    {% endif %}
    
  2. We pass user reference and add checking functions to the models.

    {% if user.registeredAtLeastTwoWeeksAgo
        and user.hasLessThan5Friends %}
        {% ifequal errors None %}
            <div class="tip"> ... </div>
        {% endif %}
    {% endif %}
    

The first one is actually not a solution, since we calculate the conditions in the view the presentation code is spanning both layers. The second workaround allows us to decouple the template and the view but adding more and more functions on models for such simple operations is just displacing the problem.

As far as I understand Django template engine has very simple control flow functionality for a reason. To ensure logic stays in the view; limit functionality in the template (to truth testing and equality testing only). But eventually you want a somewhat complex template and either the view or the model gets cluttered.

The first sample, the one with the complex if, is jinja code. It’s conditional staments are almost as powerful as Python. This allows bad programming of course. You can do almost everything you are supposed to do in the view. But you if you follow decoupling principle you can have your code working, clean and maintanable.

I think Django is a special project (as an open source project, like Blender, like Inkscape in this sense). If we look carefully there are many lessons to be learned. Both in the API level and in the source code itself. I try to learn as much as possible from Django. But on my personal projects I would like to take full advantage of the expressive power of jinja (and sqlalchemy similarly). I try to assemble best components available, but I also do my best to follow best practises (learned from Django or other sources).

If you are considering Python for web development, I would say learn Django first. And then go ahead and study other frameworks and libraries. Python is extremely powerful and suits web development well. Once you found the setup (framework or whatever) that works best for you, I am sure you will enjoy it.


1: There are different interpretations of MVC model. What happens in each layer might slightly change depending on the application type and of course implementors.

2: I know these line breaks look stupid. But it blows the page away otherwise.

Bookmark and Share

What You Need Might Not Be What You Find

July 7th, 2008

I love Internet, not just because it entertains. But mainly because it enables. You can express yourself in many different ways. But I think this is not the best thing about Internet. Yes, the there are tools that are cheap and available to (almost) anyone. But the best thing is that there is a huge audience hunting for your content. Big audiences is nothing new (see T.V.). It is at the intersection of the diversity of the audience and interactivity of web you find the usefulness; no matter how far you fall from the mainstream, there is almost always someone interested.

I found out about Shelley The Republican about 3 years ago. I was fascinated to find out how different, ok how radical and illogical, her ideas were. I had read an article where the writer explained the evils of linux and advised us to call Business Software Alliance in case we found out a company using it. I had a lot of fun reading STR. And I also learned a lot from STR, in a negative way of course.

It is not all about learning about different point of views, or debating, or marketing our own ideas. It is also about sharing. And what is better to share than your experiences? Take a look at Schneier on Security, Joel on Software or The Database Column. How many of us would have the chance to listen what they say?

You don’t even have to be an acknowledged expert in your field to share. I read everything at Scooby’s Home Bodybuilding Workouts the first time I have visited the site. It is a no-nonsense website, not another advertisement-disguised-as-sincere-article. I have read it with joy.

Internet made it possible for me, someone who is on the opposite side of the planet. Not everything need to be branded and barcoded and packaged. If you make smart choices, Internet has a lot to offer. We tend to overlook this because of our daily Internet usage habits.

Now, I will go find some more funny cat pictures.

Bookmark and Share

Mandatory First Post

July 3rd, 2008

I’ve replaced my old website with wordpress. I needed a blog for some time and I was trying to integrate blog functionality into spg (such as automatic index and archive pages, RSS). But at my first trial spg collapsed. So spg needs refactoring, here I am using wordpress. :) WordPress is cool, despite being written in PHP.

I also switched to English. There are two reasons;

  1. I can communicate with a wider audience for obvious reasons.
  2. I suppose my Turkish readers already know good English.

I will write about Python, OSS/F software, Computer Graphics and random junk I find from Internet. Nothing too interesting, but I hope it to be fun sincerely.

So. Here we go…

Bookmark and Share

Where Have I Been Investing My Time

May 18th, 2012

I used to partition my workdays by different tasks. It worked for a short while. Then constraints overweighted the productivity offered. I gave up.

I wanted to make sure I was spending my time effectively. So I started recording time I was spending on different tasks with KArm. It really doesn’t improve your working habits one bit. But I have collected the data and summarized it below anyway. It seems my time is divided reasonably between tasks:

Percentage of time spent on each task group

Percentage of time spent on each task group

Data is not 100% accurate, but accurate enough. In average I have been spending 73% of my time working. The remaining time is spent on reading and writing in roughly equal amounts. I am happy with the work percentage. But I think I should be spending more time reading than writing.

Bookmark and Share