18 December 2008

A Simple Javascript Slider

HTML5 might well have a native slider element (as part of Webforms 2.0); jQuery and MooTools have slider components, and the StackOverflow slider page points to these and other implementations.

In the spirit of NIH, I wrote my own. Rolling your own is one way to learn to appreciate a language; afterwards you get bored and start trusting the libraries. Here's a screenshot:

This slider invokes a callback function (onchange) when necessary, with the percentage value of the slider. It's up to you to translate that into dates, numbers, colours, or whatever it is you're sliding. Use like this:

<table>
  <tr>
    <th>
      Slider
    </th>

    <td id="foo_slider_min">
      <img src='/images/slider_min.png'>
    </td>
    
    <td valign="top" style="padding-top:5px;">
      <div id="foo_slider_bar" style="width:128px;background-image:url('/images/slider-bar.png');background-repeat:repeat-x;position:relative;">
         
        <div id="foo_slider_button" style="position:absolute;top:0;left:95%;">
          <img src="/images/slider-button.png" alt="editor pixel size controller"/>
        </div>
      </div>
    </td>

    <td id="foo_slider_max">
      <img src='/images/slider_max.png'>
    </td>

  </tr>
</table>

Initialise this with

var onFooChange = function(percent) {
    // update foo so it's at percent%
  }
  var sliderControl = new Slider("foo_slider", onFooChange);

If your slider's name (the 1st argument to new Slider()) is "foo_slider", it expects the following DOM elements to exist:

  • foo_slider_bar - the object whose width is considered a value of 100%
  • foo_slider_button - the object whose position relative to foo_slider_bar determines the value of the component
  • foo_slider_min - an object which sets the slider to 0% when clicked
  • foo_slider_max - an object which sets the slider to 100% when clicked

Here's the actual Slider code, use as you please -

function Slider(name, onchange) {
  var sliderBar = $(name + '_bar');
  var sliderButton = $(name + '_button');
  var sliderMin = $(name + '_min');
  var sliderMax = $(name + '_max');
  var min = 0;
  var self = this;

  var max = function() {
    return parseInt(sliderBar.style.width) - 2;
  };

  var setButtonPosition = function(px) {
    if (px > max()) {
      px = max();
    } else if (px < min) {
      px = min;
    }
    sliderButton.style.left = "" + (px - 8) + "px";
    onchange(px / max());
  };

  sliderBar.onmousedown = function(event) {
    document.onmousedown = returnFalse;

    offsets(event, sliderBar, function(y, x) {
      setButtonPosition(x);
    });

    document.onmousemove = function(event2) {
      offsets(event2, sliderBar, function(y, x) {
        setButtonPosition(x);
      });
    }

    document.onmouseup = cancelMouse;
  };

  sliderMin.onclick = function() {
    self.resetTo(0);
  };

  sliderMax.onclick = function() {
    self.resetTo(1);
  };

  this.resetTo = function(proportion) {
    setButtonPosition(proportion * max());
  }
}

10 December 2008

Objects in Javascript

As a consequence of building iconfu.com, I've used more javascript in the past four months than in all the previous ten years combined. Javascript is not difficult, but its superficial similarities with java can be misleading for someone, such as I, coming from that language. Javascript is also, surprisingly, a respectable language, tarnished only by browser incompatibilities and buggy implementations (I'm thinking of one browser implementation in particular).

Here are the three worst stumbling blocks I faced on the slow path of figuring out how objects work in javascript: (1) what does "this" refer to? (2) how to write a constructor, and (3) how to define functions on objects.

If you're just scripting mouse events on a web page, you won't need much of this, but it's handy if you're doing anything serious with javascript (like, writing an image editor, ahem).

1. "this" refers to the object on which the function was called, not necessarily the place where the function was defined

Unlike java, javascript lets you copy methods about from object to object. So, suppose you have

var Truck = {
  fuel : 100,

  drive: function(km) {
    this.fuel = this.fuel - km;
  }
}

You can legitimately write

var Car = {
  fuel: 100
};

Car.drive = Truck.drive;

And then,

Car.drive(10);
alert(Car.fuel); // alerts 90

The "this" reference in the drive function references the callee's object, in this case Car, even though "this" originally referred to Truck.

"this" can be a source of much grief when setting up event listeners on objects. Suppose you want to install an onclick handler on a DOM element that allows your user interact with your vehicle, thus:

<a id='drive_truck'>drive the truck!</a>

and

var Truck = {
  // same as before, plus

  init : function() {
    document.getElementById('drive_truck').onclick = function(event) {
      // this.drive();  // FAIL: "this" is the span element, not the Truck object
      Truck.drive(); // this works, but it's ugly!
      someOtherStuff();
    };
  }
}

Truck.init();

the moral of the story: this inside an event handler function refers to the DOM element on which the event was fired - NOT the place where the function was defined. Fortunately, there are better ways to declare objects so that this ugliness can be avoided.

2. Two ways of creating objects: direct declaration (hash), or via constructor

The Truck example above shows a simple, direct way of creating objects, and it's common to do this all over the place in javascript. Mostly, it's the right thing to do, if you want something that's similar to a HashMap in java.

But, javascript also has constructors, and the funny thing about a javascript constructor is that it looks just like an ordinary function.

function Truck() {
  this.fuel = 100;

  this.drive = function(km) {
    this.fuel = this.fuel - km;
  }

  var self = this;

  document.getElementById('drive_truck').onclick = function(event) {
    // this.drive();  // FAIL: "this" is the span element, not the Truck object
    self.drive(); // this works, but you need the "self" local variable
    someOtherStuff();
  };
}

var myTruck = new Truck();
myTruck.drive(60);
alert(myTruck.fuel); // alerts 40

The Truck() function is, in fact, the Truck constructor. When you call new Truck(), instead of just calling Truck(), the "this" keyword within the function references a new object, for which this instanceof Truck returns true.

The difference between this approach and the previous one is that now you have a typed object, and you can have multiple instances of the same type of object.

3. Two ways of defining functions on objects: in constructor, or via prototype

So there are a few ways of associating functions with objects: you can declare them in the constructor, as we did above, or you can just add them later, as we did with the "Car" example. A problem arises with the constructor method if you define functions on your objects within the constructor: each object instance you create will have its own unique instance of each function as well. This can potentially introduce memory issues if you're creating a lot of objects.

The prototype approach eliminates this issue. Every object has an associated "prototype" object, to which you can add properties. Truck.prototype is just another object, but one treated with special respect by all Truck objects.

function Truck() {
  this.fuel = 100;

  var self = this;

  document.getElementById('drive_truck').onclick = function(event) {
    // this.drive();  // FAIL: "this" is the span element, not the Truck object
    self.drive(); // this works, but you need the "self" local variable
    someOtherStuff();
  };
}

Truck.prototype.drive = function(km) {
  this.fuel = this.fuel - km;
}

var myTruck = new Truck();
myTruck.drive(60);
alert(myTruck.fuel); // alerts 40, same as before

When the javascript interpreter searches a Truck instance for a "drive" property and finds nothing, it will consult "Truck.prototype" and see if there's anything appropriate there. If there is, the interpreter behaves as if that property were originally defined on the Truck instance itself. So the "this" reference remains intact for all properties inherited from the object's prototype.

Don't confuse the "prototype" concept with the "prototype.js" library. Javascript is a "prototype-based" object-oriented language, unlike java, which is a "class-based" object-oriented language. The prototype.js library relies heavily on the prototype feature of javascript, that's all.

HTH, let me know how it works for you ...

08 December 2008

Do You Really Want Analytics?

Kontra's article on Google, linking Android and Youtube via video search, speech recognition and phoneme harvesting, made a lot of sense, and I realised there's even more cleverness in that whole google product suite. I have Google Analytics on this site, and enjoy watching my bounce rate bounce and my visitors jump up and down and pages per visit and time on site and all this statistical goodness generally so much that I risk carpal tunnel from continuously hitting the refresh button on my permanently-open multiple analytics tabs in firefox.

I don't know why Analytics is free, but here's a possible reason: when google sends a visitor to your site from a search result, the only way they can judge the quality of that result (the relevance of your site to the search query), without analytics, is whether and how soon the user clicks on another result for the same search. Now that you have analytics installed, they have much richer and deeper information: how long does the user stay on your site, how many and which pages to they visit? Having this information allows them decide whether that particular result was in fact a "good" hit.

I have no idea if google correlates search data with analytics data. But the point is, they have the data. And if I were google, I would do it. Wouldn't you? It's not necessarily a Bad Thing, but it's nice to know what you're paying for when it's free.

24 November 2008

Your Apache May Be Cheating You

In a fit of premature optimisation I figured the java/jetty/mysql part of iconfu should not be serving images, even if the database is the primary point of reference for image content, and have apache serve these images instead. Here's the plan: the server gets a request for an icon. The corresponding file doesn't exist, so the java application creates the file and also serves the content. On subsequent requests for the same file, apache should serve the file directly without invoking the java application. Here's the VirtualHost configuration I ended up with:

NameVirtualHost *:80

<VirtualHost *:80>
        ServerName example.com
        ServerAlias www.example.com
        DocumentRoot /apps/example.com/
        RewriteEngine On

        RewriteCond "%{DOCUMENT_ROOT}%{REQUEST_FILENAME}" !-f
        RewriteRule "^/(.*)$" "http://127.0.0.1:8000/$1" [P,QSA,L]

        ProxyPassReverse / http://127.0.0.1:8000/
        ProxyPreserveHost on

        <Location />
          Order allow,deny
          Allow from all
        </Location>

        <Location /WEB-INF >
          Order allow,deny
          Deny from all
        </Location>

        ErrorLog /var/log/apache2/example.com.error.log
</VirtualHost>

You will notice that in this configuration, you don't need to specify all the static stuff you want apache to serve - star dot ping, star dot js, star dot css, star dot etc etc. The rule is quite simple: if the file exists, serve it, otherwise assume it's a request for a dynamic resource. The RewriteCond line does that for you. "!-f" is mod-rewrite-speak for "test that the file does not exist"

It took two days of twiddling and tweaking to get here, which was all the more shameful because I had thought this static-serving-apache stuff was all already working. I was so wrong. In particular, I had included the ProxyPass / http://127.0.0.1:8000/ directive, which apparently overrides the RewriteRule directive, causing everything to go through the proxy, regardless of whatever rewrite rules you have configured. I made this error as a result of reading other peoples' blogs, something you should never try at home. If I had written this entry before reading all those others, I would have been spared all the hassle. Fortunately however, now you are.

Another error I had made: the accursed trailing slash. Note carefully the difference between these two:

        DocumentRoot /apps/example.com/
        RewriteCond "%{DOCUMENT_ROOT}%{REQUEST_FILENAME}" !-f

and

        DocumentRoot /apps/example.com
        RewriteCond "%{DOCUMENT_ROOT}/%{REQUEST_FILENAME}" !-f

Both of these work, but the following doesn't:

        DocumentRoot /apps/example.com/
        RewriteCond "%{DOCUMENT_ROOT}/%{REQUEST_FILENAME}" !-f

In this last case, apache will always believe that the requested file does not exist, and will always forward to the proxy. And that sucketh, as Shakespeare might have said.

Here's an example of a blog entry you must never take rewriterule advice from (although the load-balancing suggestions appear to be accurate) - http://www.conandalton.net/2008/08/apache-2-modproxy-load-balancer.html. On the other hand, http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html is the fountain of apache-fu, read that before reading anything else.

17 November 2008

risible-db: database migrations in java: release 0.2

It is with joy and delight that I announce risible-db-0.2, our beloved risible migrations project, now with support for Oracle!

Risible-db is an ultra-simple database schema migration utility, not as fancy as Rails, you just write plain SQL (really, how many times do you change DB vendor mid-project and think "wow, it's lucky we used a platform-independent DB abstraction layer!" ?). By convention, it looks for SQL scripts in the migrations subdirectory of your WEB-INF directory, and will execute any that have not previously been executed.

So the only reason it needs Oracle support is to create the initial migrations table.

Usage has changed little, this is how to configure it with Spring (you can imagine the equivalent java code) -

applicationContext.xml
<bean id="migrations" class="risible.db.WebInfMigrations" init-method="start">
  <property name="dataSource" ref="mysqlDataSource"/>
  <property name="migrationTableName" value="migrations"/>
  <property name="migrationTable">
    <bean class="risible.db.MySqlMigrationTable"/>
  </property>
</bean>

The migrationTable property specifies the db-specific table creation strategy. You can use risible.db.MySqlMigrationTable, risible.db.OracleMigrationTable, or roll your own implementation of risible.db.MigrationTable - a one-method interface whose implementation creates a table if necessary.

download here!

Enjoy, and let us know how it works for you ...

11 November 2008

little javascript trap

Sometimes the Spartan simplicity of programming with java interfaces has a lot of appeal, especially when faced with the way Rails treats each class as an enormous hold-all for a gaggle of methods. Pros and cons yadda yadda yadda.

Having got that out of the way: Rails' to_json rocks - instant serialisation of your Ruby objects into Javascript objects. We need a JSR to do this to java.lang.Object (I mean, how many times have you needed wait() and notify()?), so Java may become Web-2.0 enabled/compliant.

Anyway, there I was merrily scripting freemarker to do what Rails gives us for free, and everything's honkey-dorey on Firefox and Safari, but Opera is choking. After the requisite banging-head-on-wall, I spot a trailing comma.

<html>
  <head>
  </head>
  <body>
    <script type="text/javascript">
      var x = {a:1, b:2, c:3, d:4,}; // trailing comma here. This isn't the original code, btw, in case you're thinking, "duh!"
      for (var i in x) {
        document.write("<div>" + i + " is " + x[i] + "</div>");
      }
    </script>
  </body>
</html>

Warning: Opera don't like trailing commaz. Solution: switch to a language that provides to_json, or keep suffering. Or drop support for Opera. Speaking of not supporting stuff, I've no idea what IE does. How dangerous is that? Here are my browser stats for the last 30 days:

FF64%
IE24%
Chrome4%
Safari3.9%
Opera2.7%

Chrome? Google so pwns you dudes lol

cheers, and good luck with the code generation ...

07 November 2008

Acknowledge Feelings

Acknowledging the feelings of another person is a powerful technique we learned from Faber-Mazlish.

One day Sapphire, our delightful daughter, about a year old at the time, was climbing over Bingo the Dog (a noisy plastic toy), fell flat on her face, and started screaming. I picked her up to comfort her, but she struggled and screamed even more. I thought, hopeless father, why did I ever have children, I'm not cut out for this, etc ... and all of a sudden realised she wasn't crying out of pain, she wanted to get back to her toy and I was preventing her! Her tears dryed up pretty fast once I put her back on the floor. But my understanding of crying was forever changed. It wasn't simply "crying => pain" anymore, crying is now an ambiguous message calling for emotionally-sensitive context-dependent interpretation. This wasn't going to be easy for my INTP personality.

Fast forward 18 months. Sometimes she trips over a toy and bashes her face on the floor - actually hurting herself. As usual, my (hopelessly wrong) intuition would suggest I say it's nothing, don't worry about it, you're not really hurt, it'll get better, stop whining, hey, get over it, it's only a bruise, it's not the end of the world, for god's sake! Here's the updated alternative:

Conan(picks Sapphire up, cradles her in arms, adopts a concerned, caring-parent expression)
Sapphire(screaming in agony)
ConanSapphire, are you hurt?
Sapphire(screams some more)
ConanIs it the end of the world?
Sapphire(screams continue, but she appears to be nodding in agreement)
ConanDid you bash your face on the floor?
Sapphire(screaming) Yes!!
ConanThat must hurt a lot!
Sapphire(screaming less) Yes!!
ConanDid you crack your skull open?
Sapphire(a little calmer) Yes!!
ConanIs your nose all squashed?
Sapphire(crying, still) Yes!!
Conanouch!
Sapphire(sobs gently)
Conan(points to the floor where she fell) Is this where you hit the floor?
Sapphire(sobbing gently) Yes
ConanOh dear, it must have hurt you a lot.
Sapphire(moaning) Yes. Urting.
Conan(pointing to the totally wrong place, her shoulder for example) Does it hurt here?
Sapphire(points to her head) No, here. Ed ache.
ConanOh, your head hurts?
SapphireYes, ed ache. My ed ache.
ConanSapphire, tell me, what do the wheels on the bus do?
Sapphire(sniff) Go wound and wound.
ConanAnd the horn on the bus?
SapphireGo beep-beep-beep.
ConanIndeed ... do you want to play some more?
SapphireYes (Gets down. Surveys the situation. Wipes her nose in her sleeve. Resumes activity. All is well)

NLP teaches us that 90% of our communication is non-verbal, which is why you can get away with "did you crack your skull open?". Empathise, let your tone of voice and facial expression reflect how you feel, and the words don't really matter so much. Once you establish empathy, you can change the tone of the conversation, and her mood will follow. The wheels on the bus, or whatever her favourite daytime song is, makes a great distraction, and then she's ready to get back to clambering over Bingo.

It doesn't seem to work so well among adults, at least not if you're unsubtle or the victim knows what you're up to. Sincerity is really the key here.

 (late one evening)
Conan(despairingly) This PayPal developer API is really complicated, I don't know how I'll ever make money :(
Sabrina(tongue firmly in cheek) Oh, Conan, you must be really frustrated, it sounds so difficult ...
ConanDon't try your Faber-Mazlish stuff on me, dammit!
 (all burst out laughing)

30 October 2008

When to use Interfaces

The simplest answer: never. Use Ruby or any other dynamic language instead! But if you're still stuck in javaland, well, I promised this article in Programming for the Stone Age after witnessing some serious interface-itis. My university Software Engineering courses all talked about "always code to an interface" and articles abound on this insidious meme. So you get interface-implementation couples, where every method signature on the class is duplicated on the interface, pointlessly. I remember with horror a proprietary ORM I was obliged to use that actually required this pairing for every persistent class. It was unfortunately the backbone of our application, and hurt more than raw SQL. But that was in the days before Hibernate and the whole POJO movement, so let's call it a learning experience. Let us end the tedium of obligatory signature copying for every little property we add to our applications!

Right. So you're building your application, you decide your domain objects will include Customer, Supplier, Product(*). You already have a concept: a Domain Object, a kind of a thing that is unlike a Controller, or Service, or a Repository. A Domain Object has nothing in common with a HashMap or String or JdbcConnection. Major distinguishing features of Domain Objects are that each instance can be uniquely identified from among all Domain Object instances; each Domain Object is subject to CRUD operations and may be in an unsaved or deleted state. You might have decided for your application that objects are never deleted from your database, but rather they are tagged as deleted. You might decide that all domain objects should have a human-readable name. If any of this is the case, it is possible, depending on the nature of your application, that an interface resembling the following may be useful:

DomainObject.java
interface DomainObject {
  UniqueIdentifier getUID(); // this isn't necessarily the database id of the object
  String getName();
  boolean isSaved();
  boolean isDeleted();
}

With this interface in place, you can write some methods whose behaviour will be consistent for all of your domain objects:


  public void update(DomainObject o) {
    if (o.isDeleted()) {
      throw new CantUpdate("object " + o.getUID() + " is already deleted");
    }

    ...
  }

  public String renderLink(DomainObject o) {
    return "<a href='/object/" + o.getUID() + "'>" + o.getName() + "</a>";
  }

Customer and Supplier have a feature in common: they are contactable. That is, they have addresses, phone numbers, emails and so on. It would be nice to handle contact information in a uniform way: to do so, the Contactable interface might be handy:

Contactable.java
interface Contactable {
  String getAddress();
  void setAddress(String address);
  String getEmail();
  void setEmail(String email);

  ... etc ...
}

Armed with Contactable, you can manage contact information in a uniform way:


  public void verify(Contactable c) {
    ...
  }

  public String renderContactInfo(Contactable c) {
    ...
  }

Last example: Auditable : for those Domain Objects for which all changes need to be audited. If your application requires all objects are audited, you might as well merge this with DomainObject (and you might make it a superclass rather than an interface).

Auditable.java
interface Auditable {
  User modifiedBy();
  Date modifiedOn();
  void updated(User by, Date when);
}

Auditable works well in combination with a Hibernate interceptor or event handler so that every object that is persisted and requires auditing, is guaranteed to be audited with no more effort than adding "implements Auditable" to your class definition. This way, together with the magic of hibernate, your business-logic code can deal with modifying your object in a clear and readable fashion, without a heap of audit-code clutter. For example:

MyHibernateInterceptor.java
class MyHibernateInterceptor {
  boolean onSaveDirty(Object o, Stuff... otherStuff) {
    if (o instanceof Auditable) {
      ((Auditable) o).updated(getUser(), new Date());
    }

    ... etc ...
  }

  ... etc ...
}

So you end up with domain classes looking a little like this:

Customer.java
class Customer implements DomainObject, Auditable, Contactable {

  ... interface methods ...

  ... other methods unique to customer ...
}

Java interfaces allow you slice up a big class into smaller, managable chunks so that you can write well-targeted pieces of code that guarantee consistent behaviour over a range of different objects.

Lastly and most importantly: don't create the interface until you really need it. If you have only one implementation, and there is only one possible implementation (at least for the moment), then you don't need an interface. The day you have a second implementation of the same concept, hit Refactor -> Extract Interface in Intellij (**) to introduce your interface quickly and painlessly. And I really mean painlessly. Intellij will hunt down all references to the old class in your code, and convert them where possible into references to the interface. And you get to decide whether the class keeps its name and the interface gets a new name, or the other way around.

There is a whole universe of refactorings out there: "extract interface" is so trivial and easy to understand that it can be automated. So use it. When you have the freedom to evolve your code in any direction at the last minute, you implicitly have the freedom to keep your codebase much simpler by not evolving it until the last minute.

Good luck, and let me know how it goes.

(*) It looks like e-commerce is the "hello world" of web-app bloggers (**) I'm sure Eclipse and Netbeans have this refactoring too.

25 October 2008

Tales of the Interview

Recruiting is not only for colleagues: we recruit everybody. It is the world's biggest problem: getting the right people into the right jobs, eliminating the waste and drag of unproductivity engendered by poorly matched people and positions. We recruit babysitters, bankers, plumbers, spouses, presidents, even if the recruitment process is completely different in each case. The only people we can't recruit are our blood relations.

This post was triggered by two recruitment-horror-story posts from the world's best blog: A Problem at the Personal Level, and The Receptionist Test. They're worth a read.

It scares me how some software organisations hire coders on the basis of nothing more than their CV, a handshake, and a smile. If you do nothing else in a programming interview, please do this: sit in front of a computer with your candidate, and ask her, or him, to write some code. This alone is the best filtering tool you have, it's so powerful you won't want to look at CVs ever again.

Tips for programming interviews:

  • Take time at the beginning of the interview to help your candidate relax so they can put on their best performance. If you've done this, and they're still nervous and stressed, you need to think about having a nervous and stressed person on your team. If you haven't done this, and they perform poorly due to stress, you might have missed your best candidate.
  • The more developers from the team are involved, the better (perhaps a rotating three-person interviewing team for example).
  • Over time, build up a collection of programming exercises. You will need a string of performances in order to be able to judge comparatively, but you will also need a variety of exercises so that you can choose, for a given candidate, the exercise that allows their strengths to shine.
  • Don't waste time on candidates that you haven't pre-screened with some basic technology questions first. (Really basic. Eg, What's the difference between a Set and a List?)
  • There are many kinds of programming exercise.
    • Pattern-oriented: because you (probably) need these in your project: you can find or design an exercise that requires Command, or Strategy or example
    • For someone claiming to be a java "expert", design an exercise that shows a mastery of exception handling and inner-class use.
    • Find (or write) some particularly nasty code that your candidate can refactor and write tests for.
    • Best of all: don't interrupt your work, turn your current project task into an exercise. No interview exercise can be more relevant than this!
    • Avoid algorithm-oriented problems, (sorting, searching, packing, etc) - because libraries exist for this kind of thing so these exercises don't test real-world experience so well.
    • Avoid framework-specific problems (eg Swing, Hibernate): an exercise requires too much setup and context, and ordinary spoken Q&A are best for evaluating technology-specific knowledge.
  • Incorporate test-automation. A solid candidate will grasp testing concepts even if they are not familiar with the frameworks you use.
  • It's not just about coding and technical skills. This is your chance to sit next to your candidates, to work with them, as if they were already on your team. Do they have a sense of humour? The right kind of humour for your team? Are they comfortable, relaxed, confident? Do they listen, take the time to understand you, and verify that they understood you, before launching into the problem? Do you really want to work with this person?
  • You're not a born interviewer. You will make mistakes. I did. Get over it. Interview more.

Gladwell argues in Blink that although we make snap judgements all the time, whether we can trust these judgements is a matter of experience. With a few years of interview practice, your snap judgements will gradually become more and more accurate. And maybe, at that point, a firm handshake and a quick, friendly, confident smile will be enough?

Now if there was a way to do this for presidents, and plumbers ... the world might be a different place ...

24 October 2008

CSS can replace many lines of Javascript

Here's a trick that has saved me dozens of lines of javascript. Say you need to toggle your page between two modes - for example beginner/expert, verbose/concise, update/read-only, screen/printer. You could set a cookie or request parameter and reload the page, but that may not be efficient, it calls for a lot of code server-side. More practically, you could write some javascript that iterates over your DOM, identifies target elements, and toggles some aspect of their state in conformity with the desired mode. But again, it's a lot of code, only this time on the client-side.

Or you could do the following. Use a CSS classname to identify elements that should be visible in one mode or the other. For example, ifVerbose and ifConcise.

any-page.html
  <body class='verboseMode'>
    <a href='/blah/blah' class='ifVerbose'>
      This is a link to /blah/blah where you 
      will discover a lot of blah.
    </a>
    <a href='/blah/blah' class='ifConcise'>/blah/blah</a>
  </body>

The CSS is trivial: any ifVerbose element on a conciseMode page is invisible, and vice-versa.

application.css
  .verboseMode .ifConcise {
    display: none;
  }

  .conciseMode .ifVerbose {
    display: none;
  }

All you need now is a tiny bit of javascript to toggle the classname of your root element between conciseMode and verboseMode.

application.js

  var verbose = function(v) {
    if (v) {
      $(document.body).addClassName('verboseMode');
      $(document.body).removeClassName('conciseMode');
    } else {
      $(document.body).addClassName('conciseMode');
      $(document.body).removeClassName('verboseMode');
    }
  }

Much less work than toggling with javascript in a big loop, no? Let the browser's CSS engine do the work!

21 October 2008

Sending binary data from Freemarker

There are probably many better alternatives to writing binary content with Freemarker: for example write your data to the filesystem and let apache serve it. But sometimes you have no choice and you need to serve your png or jpg or doc or pdf from a freemarker template.

The solution involves setting the content type, like when sending javascript from freemarker. Then we need an object having access to the data with a method to write it for us.

send-png-image.ftl
${res.setContentType("image/png")}${action.writeData(res.getOutputStream())}
The framework you are using should have injected the HttpResponse object into the Freemarker context. For example, Webwork/Struts2 creates a "res" variable which you can access from your Freemarker templates. The writeData implementation need not differ much from this:
SendImage.java
 ...

public void writeData(OutputStream os) throws IOException {
   os.write(this.getByteArray(this.image));
 }

...

It would be nice to simply write ${res.outputStream.write(byteArray)} in Freemarker, and dispense with the Java method - but Freemarker wraps the byte[] byteArray in a SequenceAdapter (ooh, a Design Pattern), and doesn't unwrap it again when we just want the byte array. So we need a writeData method or equivalent in java.

The same principle works for other kinds of binary content - photos, movies, documents, pdfs, etc - set the right content-type. If you want the browser to open a "Save As ..." dialog instead of displaying the content inline, add this after the content-type declaration:

${res.setHeader("Content-Disposition", "attachment; filename=${fileName}" )}

Special Note for writing Excel

Set your content-type as follows: ${res.setContentType("application/vnd.ms-excel; charset=UTF-8")}, and then write your data as a HTML table. That's all! Trivial! No POI, no hacks, no bridging to VB. The only thing you need worry about is escaping some of the data so Excel doesn't misinterpret it.

His Serene Highness

This is Ferdinand-Philippe, the Duke of Orléans, who died in a car accident in 1842. (*).

His statue reminds me of someone I worked with briefly a few years ago. Pompous, arrogant, disdainful, and overdressed. If you're a duke, and the king's son, you can probably get away with it.

If you're a low-ranking manager, try another strategy.

* It was actually a "voiture à cheval", a horse and carriage. Modern vehicles kill people much more effectively, but it is interesting that the culture of death on the roads was already in place so long ago.

16 October 2008

risible-db: database migrations in java

Jean-Philippe Hallot, and I, are pleased to offer the world risible-db, a new, free, really simple, 100% java open-source utility library for managing database schema migrations in a manner not wholly unlike rails migrations. The class of interest is (somewhat unimaginatively) called Migrations, with a convenience subclass called WebInfMigrations that does a lot of the convention-over-configuration thing for you. Use as follows:

in your Spring applicationContext.xml config
<bean id="migrations" class="risible.db.WebInfMigrations" init-method="start">
  <property name="dataSource" ref="myDataSource"/>
</bean>
You can of course auto-wire the datasource. There is no other property to set. When Spring calls the init-method, start(), risible-db will look in a directory called migrations directly under your application's WEB-INF directory, for *.sql files, order them lexicographically, and execute any that have not already been executed against the current datasource. Here's an example:
/WEB-INF/migrations
001-add-widgets-table.sql
002-add-users-table.sql
003-add-permissions.sql
003-add-widget-tags.sql
004-rename-widgets-to-woggles.sql
select * from migrations;
+---------------------------+
| name                      |
+---------------------------+
| 001-add-widgets-table.sql |
| 002-add-users-table.sql   |
| 003-add-widget-tags.sql   |
+---------------------------+
In this situation, risible-db migrations will execute 003-add-permissions.sql, and 004-rename-widgets-to-woggles.sql, because they are not already installed in the migrations table. Unlike Rails, risible-db considers the whole name of the migration, not just the number. So you can have multiple migrations with the same number; in fact risible doesn't care about the numbers at all, but it's a convenient way for you to keep your migrations in order. If you prefer A-add-widgets.sql, B-add-users.sql, etc, that's ok too. We found that the strict numbering approach called for a lot of number-management overhead when working in a distributed team - when the half of the team on this continent wanted to add a migration at the same time as the other half on another continent, we needed a way to ensure we wouldn't both use the same number, while simultaneously guaranteeing that the order of migrations respected schema dependencies. Each .sql file is a plain text file containing one or more sql commands, separated by semicolons. No comments, please. Notes:
  • Support for Oracle is coming in the next few days - right now it works only for MySQL
  • Respect and acknowledgements to Pascal Pratmarty who was the first to say, "well we could do that in java, too!"
  • future enhancements include down migrations like rails, conditional migrations (eg data migrations only to run in a test environment), other ways to express migrations (java code, for example), and SQL comments. Please comment if there's something on this list (or not) that you can't live without.
  • download here
  • Don't search for documentation. This is it.
  • It's LGPL licensed
Please let us know how it works for you, we'll be happy to hear your feedback.

08 October 2008

π is for porn

Today's favourite from reddit: π stands for porn ... and a host of other nasty things you never expected to see in maths classes ... watch out!

Parenting, Politics, and Manipulation

One of our first parenting books was a great little volumne called How to Con your Kid. It offers many toddler-survival tactics, my favourite being the offer-a-choice trick.

Yesterday, I was encouraging my daughter (age 2) to get into her stroller so we might go collect her brother at school.

ConanInto your stroller, sweetie-pie
DaughterI don't want to go into the stroller, I want to walk!
ConanShould I fasten the straps, or do you want to do it?
DaughterI want to do it.
ConanHmm. Well, just this once, ok? Sit down and you can strap yourself in.

And in she hops. This technique is so effective it feels like manipulation bordering on unethical. But if the only alternative is physical coercion, it's not so bad. Fortunately (or otherwise), the child eventually learns countermeasures, and it's much less effective by age 4. But by that age dialogue and mutual understanding largely replace the need for coercion.

Then I noticed an eerily parallel conversation going on the other side of the Atlantic:

Uncle SamMore war, more global warming, more bailouts for rich people, more oppression for the poor.
The PeopleNo, we want transparent, open and fair government!
Uncle SamReplublican or Democrat?
The PeopleUm .. Democrat, this time, please
Uncle SameHmm. Well, just this once, ok?

07 October 2008

HTML: Scroll table contents without scrolling headers

 

I stumbled upon this quite by accident: all you need do is put the content you want scrolled inside a tbody tag, with style='height:nn;overflow-y:auto' where nn is the height you want your content. The tr containing your headers goes outside this tbody. This has been obvious all along to people who understand CSS, but I'm only starting to get to grips with this stuff. Here's an example, hit view source to see how it works:
foobar
0x
1y
2z
3x
4y
5z
6x
7y
8z
9x
Beats a zillion lines of js and css to do the same thing, no? It works for Firefox 3, I haven't tested it elsewhere. If anybody out there still has IE, please let me know if it works! Update : if you want the whole table to scroll horizontally, too keeping the headers aligned with their columns (thanks, Olivier), put the it in a div with style='width:nn;overflow-x:auto'. Thus:
foobarbaz
000.00xxxtoto-titi
100.00yyytiti-didi
200.00zzzdidi-dodo
300.00xxxdodo-toto
400.00yyytoto-titi
500.00zzztiti-didi
600.00xxxdidi-dodo
700.00yyydodo-toto
800.00zzztoto-titi
900.00xxxtiti-didi
It's not so pretty - the vertical scrollbar is visible only if you've scrolled horizontally all the way to the right. But given that you can scroll with the keyboard or mouse wheel, perhaps that's not such a terrible thing ...

Make Freemarker work with Prototype's Ajax.Request

The popular Ajax.Updater from Prototype is great for getting little bits of HTML back from your server and sticking them in your client's page somewhere. But Prototype has some other tricks hidden up its sleeve. The Ajax.Request utility (upon which Ajax.Updater is based) has a very nice feature: if the response comes with content-type "application/javascript" (or equivalent), it evaluates the response body as if it were javascript code. Imagine that: the server can send any arbitrary piece of code back to the client to be executed on the client's machine.

Any kind of ajax-based state change could use this technique: a login, a deletion, an insertion. For example, after a deletion, you might wish to return this response:

widget-deleted.ftl
${res.setContentType("application/javascript")};
alert('widget ${widgetId} successfully deleted');
$('div_for_widget_${widgetId}').remove();

- to provide feedback to the user in two ways: an alert, followed by the disappearing of the dom element representing the deleted object.

You might even find this technique useful to resolve the ages-old dilemma of whether to perform input validation on the server or on the client. Up to now, the correct answer has been (frustratingly) "both". But now you can avoid duplicating validation code in javascript: instead you have a javascript response that provides server-side validation-failed feedback to your user.

Here's another example, for an application that takes comments, and requires a non-empty comment. The first indicates a successful comment submission:

comment-ok.ftl
${res.setContentType("application/javascript")}
alert("Your comments have been noted. Thank you.");
$('commentForm').hide();

And the second, failure:

comment-failed.ftl
${res.setContentType("application/javascript")}
alert("You didn't enter a comment!");

Your controller decides which response template to evaluate after having performed the relevant validation. Webwork/Struts2 makes this decision based on its "struts.xml" configuration and the outcome of your action.

The first line in each of these examples sets the content type of the response so that Prototype knows to interpret it as code. The MVC framework you are using should have inserted some variable in the Freemarker context to represent the response - in the case of Webwork/Struts2, it's called "res".

Don't go overboard with Ajax though - keep in mind Ajax breaks bookmarkability and browsers' back/forward buttons. Distinguish between requests that access and display resources - a bookmarkable url is nice here, and actions that modify state on the server - Ajax.Request might be useful here, unless the state change should take you to a new page.

Let me know how this works for you!

30 September 2008

Social Bookmarking Howto for Blogger

Attentive readers will have noticed the shiny new social bookmarking icon links around the bottom of my entries. I'm catching up with 2005 here. Last month I caught up with 2004 by including ads. You can't imagine how rich I'm feeling. Actually, you can ... but anyway. In 5 years, I'll add a flickr widget and be fully up to date with 2006. For that, I'll need something called a "photostream".

... You know, it's horrible when everyone thinks you're a geek but in fact you haven't a clue what's going on on the internet?

Anyway, I followed Enrico Suarvé's howto for adding these icons to a Blogger template. For someone who claims to be a hopeless blogger, he does an excellent job. Read his howto because I'm not going to repeat it here.

I was able to make only one little improvement: instead of making my own "submitbuttons" CSS class as he recommends, I used Blogger's "item-action" and "icon-action" classes. So I have a little less work to do, and my Blogger template code looks like this:


<span class='item-action'>
  <a Title='digg it' expr:href='"http://digg.com/submit?phase=3&url=" + data:post.url + "&title=" + data:post.title'>
    <img alt=' Digg ' border='0' class='icon-action' src='path/to/digg.png'/>
  </a>
 
  <a Title='delicious' expr:href='"http://del.icio.us/post?url=" + data:post.url + "&title=" + data:post.title'>
    <img alt='Delicious' border='0' class='icon-action' src='http://path/to/delicious.png'/>
  </a>
 
  <a Title='reddit' expr:href='"http://reddit.com/submit?url=" + data:post.url'>
    <img alt='Reddit' border='0' class='icon-action' src='http://path/to/reddit.png'/>
  </a>
 
  <a Title='SlashDot' expr:href='"http://slashdot.org/bookmark.pl?url=" + data:post.url + "&title=" + data:post.title'>
    <img alt='SlashDot' border='0' class='icon-action' src='http://path/to/slashdot.png'/>
  </a>
</span>

Paste this into your Blogger html template somewhere sensible. Tweak it until you like it. And upload the relevant icons somewhere so you can link to them.

At first I thought I should include furl and fark and magnolia and a zillion other SB sites I'd never even heard of, but in the end I figured that was kind of immodest (and you should know that I'm the modestest person you're ever likely to meet), so I retained only sites that I use: reddit and delicious, and occasionally: digg, and slashdot. (Hey, I even have an account on reddit and delicious, now that's so 2007! I'm getting there!)

Damn. I've just broken my resolution to not blog about blogging. Again!

29 September 2008

<center> is deprecated: use CSS instead!

Being from the 1990's, I like to use tables and the good ol' <center> tag to control my layout. Apparently, that's not polite any more. When I get my CSS Guru 9th-Dan Black Belt certification, I'll let you know why.

In the meantime, the totally modern way to centre a block, I learned today, is to give it a fixed width and set left and right margins to "auto". Thus:

.centred {
  margin-left: auto;
  margin-right: auto;
  width: 10em;
}
The block containing this text is centred relative to its parent. Note that the text itself isn't centred. Use text-align:center for that.

The difference, afaict, is that nowadays the element to be centered is responsible for its centredness, whereas in the old days, the element's parent decided how the child would behave. Very modern, them dudes at w3c. I try to let my children decide how to behave, too. Who would have thought CSS teaches family values?

26 September 2008

Keystroke Strikes Again

I wanted to preview my last blog entry just to check everything was ok and I hit control-F9, my intellij shortcut for "build project". This is very, very grave. It's like when I take out my bus pass to get into my house. Or this: image copyright ??? please let me know!

One from the archives

Ten years ago I wrote a little java applet (yes, an applet. Imagine that!) that plays Reversi, aka Othello - as an exercise for a Game Theory course I was taking.

I dug it up, and amazingly it compiles, runs and even works!

You can play against my little Reversi here (and it's totally free).

The program is a fine example of alpha-beta game tree pruning, using an evaluation function which takes into account the positions of the stones as well as the mobility of each player.

I'm sure one could write a much more sophisticated Reversi player, but even with low lookahead (4) it crushes me.

Download and play with the source code too - I'm releasing it open-source (under GPL). Just don't judge me by it - it was ten years ago, ok? There are member variables with names starting with uppercase letters ... there are ... ugh ... comments ... and all sorts of other atrocities in there. OMG there was even a new String("foo"); in there. I got rid of those, but it was too much effort to clean it up entirely. I don't write like that any more. I would like to say I never did, but I wasn't born perfect either ...

24 September 2008

Expand div to contain floated child elements

I am sure I am one among many thousands who will be eternally indebted to Quirksmode for its wealth of browser wisdom. Today I learned how to make a container expand to surround all of its floated child elements. On the container, put

style="overflow:auto;"
That's all! At least, for firefox - other browsers need width as well.

19 September 2008

A simple Javascript function to move elements

The proper way to drag divs or images or other html elements using javascript is, of course, to use a well-known and established framework for doing it - scriptaculous, for example. Even better would be to use Firefox's fancy new drag/drop handlers (as soon as they work, that is). But if you suffer, as I do, from NIH Syndrome, here's a roll-your-own version.

Just so we agree on what dragging is: the user clicks on an object, and while moving the mouse, the object follows the mouse around, until the user releases the mouse button. In the version below, we distinguish between the target (the thing to be moved), and the handle (the thing the user clicks on in order to move the target. A titlebar, for example).

The following has been tested on FF3 and almost certainly won't work on Exploder. But there are plenty of resources out there on hacking IE until it works.

Enough talking, here's the good stuff:

var makeMovable = function(target, handle) {
  target = $(target);

  $(handle).onmousedown = function(event) {
    var initialXOffset = target.offsetLeft - event.pageX;
    var initialYOffset = target.offsetTop - event.pageY;

    document.onmousemove = function(event) {
      target.style.left = event.pageX + initialXOffset;
      target.style.top = event.pageY + initialYOffset; 
    }

    document.onmouseup = function() {
      document.onmousemove = null;
      document.onmouseup = null;
    }

    return false;
  }
}

Once your HTML elements are defined, all you need do is call makeMovable with the relevant params:

<div id="myContent" style="position:absolute;left:100px;top:100px;">
  <img id="myHandle" src="/images/move_handle.png" />
  you can move this text by dragging the handle
</div>

<script type="text/javascript">
      makeMovable("myContent", "myHandle");
</script>

(Of course, you might prefer to take an unobtrusive approach and have all movable elements wired up using identifiers and class names. Exercise for reader).

So here's what's going on. The principle is simple: makeMovable sets a "mousedown" listener on the handle - since that's what we're waiting for our user to click on. Once mousedown is triggered, we calculate how far away the mouse click was from the corner of the target. This gets stored in initialXOffset and initialYOffset. The goal now is just to make sure that the target is always this distance from the mouse, as the mouse moves around.

So we need a mousemove listener. Curiously, mousemove has to be on document - you would think it should go on the handle instead because the mouse is necessarily over the handle when we get the initial mousedown - but unless it's document.onmousemove, Firefox gets all confused and thinks you're trying to select text at the same time as moving stuff around.

The mousemove listener does the real work: it sets the top and left style elements of the target so as to maintain the original offset from the mouse position. Note that the original style declaration of the target div includes position:absolute. Without this, the top and left style elements are completely ignored, and you won't be able to move anything.

Remember to return false; at the end of onmousedown. Without this line, it mostly works, but FF also initiates its internal drag handler as well as yours, resulting in angry users tearing at their screens in frustration, and the resulting drag behaviour isn't at all intuitive or pleasant.

The last word goes to onmouseup: once the user lets go of the mouse, we should restore document.onmousemove and document.onmouseup to whatever they were before. I'm being lazy here and setting them to null. Umm ... exercise for reader ...

If you're not using Prototype, replace $(...) with document.getElementById(...).

You can write similar code for resizing an element - you would adjust the width and height style elements instead of top and left.

Leave a comment if this works for you, or more importantly, if it doesn't. Leave a comment even if there's some other javascript question bugging you. I'm curious.

On Learning and Falling

Pascal Pratmarty's comment in a recent article on conflict resolution triggered some painful yet fond memories of learning ice-skating.

"Remember this: to be a learner, you’ve got to be willing to be a fool."

This will sound very odd if you've only tried skating once or twice: it's very difficult to fall. Once you figure out balance, your body clings to it, not letting your delicate elbows and knees hit that hard, cold ice.

I had learned to go forward, but people all around me were spinning, coasting backwards, jumping, and doing those awesome screech-skid stops. Envy filled me.

Your body learns these moves, not your mind, and can learn only by experiencing them. I had to push my reluctant flesh beyond what it was comfortable with. I had to force myself to fall.

It worked. Now, I can wiggle backwards around the rink, mostly avoiding collisions. I have yet to learn stopping though ...

Curiously, the pain of falling is not what inhibited me the most - you get over that fast. It was the eyes of all those people. I knew they were looking at me thinking: the fool.

I try hard, but I wish I could be a better fool.

17 September 2008

Hosting in France

It's hard to find a decent hosting provider. I used Textdrive for a while, but my rails processes kept on getting killed due to some limitation on memory consumption. I'm sure I was doing something awfully wrong (besides using fcgi), but I switched to slicehost (with apache mod-load-balancer and mongrel, a big improvement already). Still, it feels horribly slow, and again, being a bit of a rails newbie, I'm sure I'm doing something wrong.

So, to get to the point, I looked for a hosting provider in France to see if I could blame the network instead of me. Ouch. One look at the webhostingtalk review discouraged me from ever going near OVH. Dedibox, on the other hand, didn't seem to have such reputation issues, and their offer looks really good (physical dedicated server, 2GB RAM for EUR30/month compared to Slicehost's 256MB RAM VPS at $20/month). They have an absurd restriction on selling only to French residents, but I have a workaround for that. So I started the signup process, expecting to type in my name, email, credit card number, and choice of plan, and then be up and running in a few minutes. Slicehost sets the bar very high in this area. But no - they needed my phone number too, which they needed to confirm by sending me an SMS with a code I needed in order to proceed with signup.

Credit card? No - they want your bank account. With a severe warning in red that they will sue you if you deliberately offer false details. And then you have to print out your bank details and sign an authorisation for Dedibox to charge your account, and mail it to them. Surprisingly, they don't have an option for payment by sheep or goat. I was a little bit uncomfortable typing in all this bank information without numerous assurances in the surrounding text that no charge would be made unless and until I had my server, but I had little choice if I wanted to give these guys a chance.

Ok ok now you're thinking well what do you expect, that's French bureaucracy for you. Well for once in my life I figured I would click on that pesky "terms and conditions" link that I was about to claim to have read, understood, and accepted. Pretty quickly, I had read, understood, and totally not accepted that they "guarantee" that my server will be ready in a maximum of 35 days.

35 days ???? !!!!

"Wtf?", one might think. And: if they fail to deliver in 35 days, I have the right to cancel the contract - by sending them a registered letter to that effect. Double wtf.

So the long and the short of it is, I didn't click on the "I have read and accept the terms and conditions blah blah blah" box, and I didn't click on "proceed", and I'm not getting a Dedibox. I wonder who does? Do you have to hate your customers in order to be successful in France? Is that what they teach in business schools?

Now that the US is falling to pieces, the rest of the world is going to scramble to take its place on the top of the economic dunghill. Does anyone know where I might get my CV translated into Chinese or Hindi?

16 September 2008

An Incredible Thread

A few weeks ago, I read Halting State by Charles Stross. WARNING: potential spoiler follows - It's a novel set in the future, about a bank robbery in a virtual world, said robbery having far-reaching implications for global cyberterrorisim, including compromise of Europe's root servers and digital certificates, handing control of the continent's core infrastructure (police, transport, banking, everything) to a gang of Chinese hackers who possibly think it's all just a game. The highlight is when Jack looks up to the virtual sky at a flock of virtual birds flying past and realises the bad guys are encoding messages in the detaild of their flight paths.

"The traffic looks like game-play to [...] NSA or whoever's sniffing packets; looking in-game for characters run by Abdullah and Salim holding private chat about blowing up the White House garden gnomes won't get you a handle on what's going on because they're not using the game as a lydic universe to chat in, they're using it as a transport layer! They're tunnelling TCP/IP over AD&D!" (p.299)

Oscar Wilde said "Life imitates Art". And today Wired.com describes US Congress and Pentagon fears of terrorists chatting in World of Warcraft to plot an ATTACK ON AMERICA OMG OMG GET THE DUCT TAPE HONEY WHERES MY .32??!!!1!!

A Credible Threat, in order to be useful to a scaremonger, must be easily understood by the population purportedly under attack. A heavy reliance on popolar stereotypes is customary, but by broadening the stereotype to encompass virtual reality, the threat authors postpone threat fatigue and get better ROI on their fiction investment. The problem with Stross's scenario is that it's far too technical. A politucian wouldn't have a clue how to explain it, while a smart terrorist (bad, bad guys) could do a lot worse, but the guy on the street has mostly figured out online chat.

Those bad, bad terrorists wouldn't even have to go as far as Strosss describes. Combine basic cryptography, steganography, and any of the myriad ways of communicating (internet or otherwise), and you can undetectably send any message you want while it looks like you're only sending a picture of the kids to their grandmother in a village in Saudi Arabia. Or tunnel encrypted instructions through common misppellings or bad grammar in bot-generated chat in any of a million online forums. Wake up, CIA, you can't detect this stuff! The only solution is too shut down the world's entire telecommunications infrastructure - but that would be bad for business.

Of course, the online subversion goes he other way, too. Did you know that the CIA runs Facebook? Its Hamming Distance from "FactBook" (as in "The World Factbook") is only 1, so it's much easier for CIA agents to tipe. I don't know whether other Facebook constpiracy theorists had spotted that. Did you spot it?

What is the Truth? Somewhere out there, someone is laughing their arse off. Is it the bad guys, or the badder guys?

Subpixel Rendering

I just read about sub-pixel rendering on good ol' wikipedia. If you've ever needed to draw a vertical line exactly between two pixels, subpixel rendering is what you're looking for.

On an LCD screen, each pixel is three little bars, one red, one green, one blue. All on looks like white, all off like black.

The subpixel trick is to light, for example, the blue bar of one pixel, and the red and green bars of the next pixel to the right. The effect is a white pixel, because you have a red bar, a green bar, and a blue bar, all together, all lit up.

Here's a little image I made with iconfu just now:

Below is how it looks enlarged, so you see each pixel value. Notice that there are no black or white vertical lines. The appearance of black and white is a side-effect of the way sub-pixels are lined up on an LCD screen - and if you're using a CRT or an LCD that doesn't order its subpixels the same way, you'll be wondering what the hell I'm talking about.

Ok, that was the enlarged image, but in case you're having doubts, this is how it looks if you take a traditional screenshot (ie, with your camera). Here you can see how each pixel is lit and where the black and white lines come from.

You can also see that this trick can't work horizontally: there is no alignment of pixels to exploit in this direction. This is the same image, rotated:

And if we rotate it once more, the alignment doesn't work at all, and we lose our black and white lines ...

You can't imagine how many problems this solves. Not world hunger, unfortunately, but let's say you're drawing a vertical arrow in a 16x16 icon. You want a sharp point on your arrow, but if you make the tip 1 pixel wide the arrow won't be centered in the icon. Subpixel rendering is the solution! Similarly, you can control how curvy a curve is by lighting only the pixels that make your curve smoother: a curve going up and right (the top-left corner of a rounded-corners box, for example), would benefit from a little dash of blue on the outside, whereas the curves on the other side would benefit from a little bit of red.

Now, wasn't that fascinating ... back to work ...

please don't fix this

My iPhoto (6.0.6) has an awesome bug (at least I suppose it's a bug) if you adjust exposure down after having fiddled a bit with light levels. Resulting in this magnificent stained glass from St. Merry, next to the Pompidou Centre:

The statue of the saint holding the baby reminds me how intimately connected religion and politics are.

05 September 2008

List foreign key constraints in Oracle and MySQL

Sometimes you want a list of all the associations in your database. You're supposed to know this from your Hibernate mappings, or from your ActiveRecord declarations, but sometimes you just need to dig this stuff out of a legacy database with its cryptic prehistoric naming conventions layered on top of one another like geological rock strata.

Anyway, I digress. As you might imagine, this is a simple select in MySQL, and a four-table join in Oracle.

Suppose you've had the original idea of building a brilliant e-commerce site. You have Clients, Orders, Products, and LineItems, with the usual associations.

This is the kind of output you want:

+-----------------------+-------------+
| foreign key           | references  |
+-----------------------+-------------+
| orders.client_id      | clients.id  |
| line_items.order_id   | orders.id   |
| line_items.product_id | products.id |
+-----------------------+-------------+

With MySQL:

select 
    concat(table_name, '.', column_name) as 'foreign key',  
    concat(referenced_table_name, '.', referenced_column_name) as 'references'
from
    information_schema.key_column_usage
where
    referenced_table_name is not null;

With Oracle (9-ish, probably 10 too, it's ages since I've actually used this)

select
    col.table_name || '.' || col.column_name as foreign_key,
    rel.table_name || '.' || rel.column_name as references
from
    user_tab_columns col
    join user_cons_columns con 
      on col.table_name = con.table_name 
     and col.column_name = con.column_name
    join user_constraints cc 
      on con.constraint_name = cc.constraint_name
    join user_cons_columns rel 
      on cc.r_constraint_name = rel.constraint_name 
     and con.position = rel.position
where
    cc.constraint_type = 'R'

I cry when I behold the beauty of Oracle's meta data model. But we'll hold off the religious wars for another day. I know it's not your fault you're using Oracle.

HTH.

01 September 2008

Faber-Mazlish parenting workshops : coming soon to the capital city of France nearest you

Sabrina will be offering Faber-Mazlish parenting workshops in Paris starting later this year. I have yapped about Faber-Mazlish before, and will again: of all the parenting books we've looked at, this is the only one that is clear, simple, undogmatic, and makes total sense. If you don't want to read the book, then take the workshop. Reserve your place before it's too late !!!

28 August 2008

rails performance and msql driver

I'm sure this is documented all over the internet, except on the pages I happened to look at. I never knew until I was looking in my log for some other problem, and spotted this:

WARNING: You're using the Ruby-based MySQL library that ships with Rails.
This library is not suited for production. 
Please install the C-based MySQL library instead (gem install mysql).

At first I thought, what the hell, the ruby-based library "works on my machine", why bother? And I remembered how suckily my apps were performing. And "gem install mysql" - it couldn't really be easier, could it?

It couldn't except for a dependency on libmysqlclient15, to install which I first needed to update my ubuntu system (a 7.10 on Slicehost), and for the mysql gem I also needed to update rubygems itself. "gem install mysql" just refused to work until these two were done. My version of rubygems was hanging - apparently this happens if it hasn't been run in a while and needs to make a big refresh from the repository. Rubygems 1.2 doesn't have this problem, fortunately.

This was the full sequence:

sudo apt-get update
sudo apt-get install libmysqlclient15

wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
gunzip rubygems-1.2.0.tgz
tar xvf rubygems-1.2.0.tar
cd rubygems-1.2.0
sudo ruby ./setup.rb

sudo gem install mysql

mongrel_cluster_ctl restart

If you're thinking "what the hell, the ruby-based library works on my machine", change your thinking. This could transform your app from barely usable to pleasantly snappy.

27 August 2008

show/hide element using unobtrusive javascript

"Unobtrusive" javascript is all the rage these days, so I had to give it a go. The idea is, instead of sprinkling bits of javascript inside onclick and onmousemove and friends within your HTML, you use a class name or id to identify elements that will need some event handlers attached. Later, your javascript takes care of adding event handlers. Result: cleaner HTML, more control over javascript.

I was about to implement for the n-teenth time a little show/hide link pair when I realised, this is always the same, onclick this and onclick that; there must be a better way.

Enter Unobtrusive Javascript!!

<span style="color:blue" id="show_hidablestuff">show</span>
<span style="color:blue" id="hide_hidablestuff">hide</span>
<p id="hidablestuff" class="hidable">Now you can see this hidable stuff</p>

"Look, ma, no javascript!". I want to hide hidablestuff, so I give it a class name "hidable". There is no CSS associated with this class name, it's just a hook for javascript later on. Its corresponding show/hide links are related via their ids - show_hidablestuff and hide_hidablestuff. The script will calculate these ids in order to find their elements in the document.

The magic is here:

var initialiseShowHideTrigger = function(h) {
  var show_trigger = $("show_" + h.id); // assume the show trigger has the same id
                                        // as the target with "show_" prepended

  var hide_trigger = $("hide_" + h.id); // similarly for hide trigger

  show_trigger.onclick = function() { // add onclick handler to
    h.show();                         // show the target element,
    show_trigger.hide();              // hide the show trigger,
    hide_trigger.show();              // and show the hide trigger
  }

  hide_trigger.onclick = function() { // add onclick handler to
    h.hide();                         // hide the target element,
    show_trigger.show();              // show the show trigger,
    hide_trigger.hide();              // and hide the hide trigger
  }

  hide_trigger.onclick();             // trigger the hide trigger so initially
                                      // the target is hidden
}

// here we search for all elements with classname "hidable", and invoke
// initialiseShowHideTrigger on each. Call this from body.onload(), or
// from a script at the bottom of your page (after everything is loaded)
var initialiseShowHideTriggers = function() {
  $A(document.getElementsByClassName('hidable')).each(function(h) {
    initialiseShowHideTrigger(h);
  });
}

I plan to do a lot more of this.

22 August 2008

Evolution

There's the National Geographic Laurasia dinosaur poster hanging on the wall in front of me, and to the right, outside the window, a little bird is savaging our miniscule window-sill wheat crop. The bird seems perfectly content, as if she's been doing this for millions of years. And I thought, some of the creationist/intelligent-design anti-evolution arguments boil down to this:

4.320432 × 1017 is such a large number. There is no way you could get to that number, starting at zero, and just repeatedly adding 1.

Pyrenees

I took a week in the Pyrenees at the end of July. The advertising campaign launched by Luz St-Sauveur totally seduced me, so I just had to go. How could you resist this:

It was a great opportunity to explore the outdoor office work style. For example:

Alternatively,

And in the shade of the trees:

I thought I would try write code high up, but by the time I got up here -

I was exhausted, and it was too windy and there was no comfortable shady spot. I am possibly the first person ever to lug a heavy macbook up 600 metres of mountain with no food or water. But at least on my way up, I caught a glimpse of my obelisk office - I had found a nice shady spot under the tree directly in front of the obelisk, next to the climbing wall. This is the village of Saint-Sauveur, next to the "Thermes", apparently a kind of hot spa.

From time to time, when I was feeling hungry, I treated myself to a terrace:

of which there are many in Luz, three having free internet access.

Since returning to Paris, I've tried these places:

Montparnasse Cemetery: there is a cosy well-shaded bench next to Baudelaire's grave, as long as you don't mind the tourists. A lot of iconfu.com was written here.

The Louvre: I tried the Dutch Renaissance, but there were no comfortable seats; I found a seat next to a window in Objets d'Art, but I didn't stay long, it was too cold. Finally I ended up among the statues - in the glass-roofed courtyard where it was warm, not too bright, and back support for working comfortably.

St Eustache: I had never been inside this cathedral before, despite having lived next to it for two years. I had reservations about working inside a church, but it started to rain, so the outdoor concept lost some of its shine. And once I was in, I realised it was full of tourists, so a guy in sandals with a laptop was not going to deprive the place of any spirituality. A cathedral is one of only a few kinds of place where you can sit indoors without feeling a moral obligation to purchase a glass or two of fine red wine.

The tip of Île de la Cité: my favourite. Watch the boats go by as you sit and code in the shade of the trees.

Of course, eventually my battery runs out. I try to arrange for this to happen near lunchtime, so I can recharge as I eat. Fortunately I have never found a restaurant in Paris where they objected to me plugging in while enjoying their food and drink.

I hope I can keep this up.

11 August 2008

apache 2, mod_proxy, load balancer, mongrel_cluster, rails, and 404

I don't know where the bug is, but the fix had nothing to do with mongrel or rails. For some pages, most of the time I would get a 404 Not Found error. The apache error log showed this:

[Sun Aug 10 20:54:48 2008] [error] [client a.b.c.d] proxy: error reading status line from remote server 127.0.0.1, referer: http://triplex.softify.com/
[Sun Aug 10 20:54:48 2008] [error] [client a.b.c.d] proxy: Error reading from remote server returned by /user/list, referer: http://triplex.softify.com/

After much googling I ended up on the page that says it all, and more besides: https://issues.apache.org/bugzilla/show_bug.cgi?id=37770

Two config modifications stand out: firstly, to add these lines -

SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1

and secondly, to set the log level to debug:

LogLevel debug

I added both and the situation is much improved. The worst offender is a very slow-loading page.

I'm on apache 2.2.6. I'd love to know what the correct fix is. Interestingly, according to the bugzilla page for bug 37770, this has been an issue since 2.2.2.

This is how my mongrel_cluster apache virtual host proxy load-balancer config looks now:

<VirtualHost *:80>
        ServerName triplex.softify.com
        DocumentRoot /u/apps/triplex/current/public
        RewriteEngine On

        <Proxy balancer://mongrelcluster>
         BalancerMember http://127.0.0.1:8000
         BalancerMember http://127.0.0.1:8001
         BalancerMember http://127.0.0.1:8002
        </Proxy>

        # Redirect all non-static requests to Mongrel
        RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
        RewriteRule ^/(.*)$ balancer://mongrelcluster%{REQUEST_URI} [P,QSA,L]

        ProxyPass / balancer://mongrelcluster/
        ProxyPassReverse / balancer://mongrelcluster/
        ProxyPreserveHost on

        SetEnv force-proxy-request-1.0 1
        SetEnv proxy-nokeepalive 1

        <Proxy *>
         Order deny,allow
         Allow from all
        </Proxy>

        ErrorLog /var/log/apache2/triplex.softify.com.error.log
        CustomLog /var/log/apache2/triplex.softify.com.access.log combined
        LogLevel debug
</VirtualHost>

24 July 2008

Au Revoir, Banque

Bye-bye BNP Paribas. We had a great time together for three and a half years, more than your own rules allow. We might even meet again.

Next week: the Pyrennees. Watch this space for photos, assuming there's an internet connection down there somewhere.

After that: freelancing and maybe some startupping.

Beyond that: Hong Kong? Australia? India? Japan? Switzerland?

See you this evening in O'Sullivan's !!

17 July 2008

Urssaf considered harmful

Urssaf is a big government body in France that collects money. I don't know what this money is for, but they send nasty letters threatening legal action if you don't pay.

Urssaf sucks in more ways than I can list.

Today (17th July), however, we received what we thought was an opportunity for vengeance. A customer satisfaction survey. We started crossing all of the "not satisfied at all" boxes in heavy black ink. And then noticed on the cover letter: "Please return this form before the 11th of July 2008". Oops. And then noticed the postmark on the envelope: 10th of July 2008

I bet there is some damned smug pointy-haired manager in the bowels of Urssaf somewhere describing to his boss, through yellowing teeth and cigarette breath, as he strokes his balding head, his innocent surprise at how so few surveys were returned.

Merde, merde, and double merde.

If you can recommend a country that's not as completely broken as France, please leave a comment.

Using checkbox for a multiple-value parameter in Rails

This wasn't obvious. Thanks to satish on rails I learned this is the way to do it:

 <input type="checkbox" name="customer[preferences][]" value="yellow"/>
 <input type="checkbox" name="customer[preferences][]" value="blue"/>
 etc

The extra [] at the end of the parameter name makes all the difference. In your controller, you can now reference

  params[:customer][:preferences]

and get a list.

23 June 2008

The End is Nigh - but wait, I haven't checked in my code yet!

I set my alarm, get up at 7:30 or so, walk my son to school (he has a future, you know), and hop on the bus. On the bus to work I practice lisp, because it's the language of the future, you know. At the office, I write more software, helping replace legacy systems with future-oriented web technologies. Helping the bank acquire more future wealth. Gathering the crumbs for my own future wealth. I think about my future projects, what kind of a startup should I build, what kind of wealth I will accumulate, and what will I do with it. Where will I live, who will I know, what will I be doing in and with the world? And my children? What kind of a world are we building for them?

And then I remember: there is no future.

When the rain passes, moving up the coast, we cross the creek on the pontoon, pulling ourselves across the slow flow on a simple, effective contraption of empty oil drums and steel grating, and walk to the beach. Sand after rain seems like hope or the promise of forgiveness—, a reminder that after we've gone, when the last human passes, the world will continue, and will begin to erase the signs of our time on Earth. Like the maze of footprints on the beach and the excavations and constructions of small children with plastic spades and buckets, the traces of our activities will be erased by weather and time and non-human lives.

When and how this will happen, I don't know. I do suspect it will happen not catastrophically and globally but gradually and patchily. Human existence, already grim in much of the world, will become grimmer, then desperate, and the expansion of regions where humans cannot live will accelerate. There, in those deserted and ruined places, the record of human life will begin to fade.

(from pohanginapete, with thanks to Dave Pollard for the link)

22 June 2008

Drive massive amounts of traffic to your blog

Oh, I'm talking about traffic again. Didn't mean to, honest ...

A while back, Dhaval blogged about Google Analytics. I had installed this snippet of javascript on this blog at the beginning of this year and it's better than television. OMG I got two hits yesterday I have *so* made it!!!1! It's the first thing I check in the evening when I get home. Ok, the second thing, after reddit. And the arc forum.

Anyway, I digress. As Dhaval mentions, it's full of charts and analyses and breakdowns. But a powerful little feature that deserves a bit of attention is the "keywords" report. It tells me, for all hits that came from searches, what terms were used in those searches. So even though "conan dalton" is the single dominant search expression, there are many more search expressions containing "onmouseout", "oracledatasource", "hibernate". Because I happened to write short articles describing my experience with problems in those topics.

But I don't think they're my most interesting articles! I'm a human being, I have thoughts, feelings, emotions, I care about things like crossing the road, and what my children can teach me about business. onmouseout isn't really that important, in the grand scheme of things!

Anger and denial over. So, analytics teaches me what drives people to my blog. It looks like I should stop writing about me, and write some more about the behaviour of onmouseout when using Spring to configure Rails with Markaby and ActiveHibernateRecord buzzword buzzword Oracle DSL .NET iPhone buzzword ...

But can I go further? Yes! It turns out that Google have another great little tool, Google Trends. (Yes, I'm expecting a nice cheque from them one of these days). Hot Trends tell me what people are interested in today. Armed with this knowledge, all I need to do is blog about today's top ten search expressions and voilà, Blogger will collapse under the strain. So, here goes:

  1. thomas finchum - never heard of this dude. He's a diver. Some pictures.
  2. david boudia - another diver. What's going on? Is it the olympics or something? Why did nobody tell me?
  3. gwen araujo - transgender woman beaten and murdered. I remembered Boy's don't cry. It made me cry.
  4. radio days - a Woody Allen film. From 1987.
  5. henry klum - Heidi Klum's son. He went to the park.
  6. emphysema - a lung disease. Caught by Amy Winehouse. She's a singer. Ouch.
  7. michelle brown - her identity was stolen
  8. crisco.com/grilling - Seriously? Google, have you checked your trends algorithm?
  9. ironman coeur d alene - a Ford-organised triathlon. This year, with less waste, apparently. Great.
  10. ocean breeze - well, who doesn't want more of that? But ... Top 10?

I'll let you know what happens to my stats. In the meantime, I've learnt some stuff today that otherwise ... otherwise ... I might never have learnt. Thanks, Google!