<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8236785121426515359</id><updated>2012-01-20T13:57:41.246+01:00</updated><category term='plants'/><category term='music'/><category term='photo'/><category term='art'/><category term='piano'/><category term='flot'/><category term='writing'/><category term='links'/><category term='food'/><category term='books'/><category term='politics'/><category term='programming'/><title type='text'>Ruminations</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default?start-index=101&amp;max-results=100'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>103</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4733808734369897733</id><published>2011-10-06T01:26:00.001+02:00</published><updated>2011-10-06T13:51:20.589+02:00</updated><title type='text'>When two becomes three</title><content type='html'>The evening before, when Janne and I were going to bed, at around 11, she complained about a pain in the stomach and got up to take a bath. I was mildly suspicious, but the last couple of weeks she'd had some painful practice contractions and trouble with the back. I was exhausted, having slept poorly the previous week, told myself that if true labor contractions had started, she would tell me, and continued to try to sleep poorly.&lt;br /&gt;&lt;br /&gt;At around 6:00 in the morning, I finally wake up to a meak yell from the bath room - &lt;em&gt;Ole, I think we need to hurry up&lt;/em&gt;. As I later discover, the light of my life has been in labour all night long, not thinking it was a big deal until she can feel the water break and the baby's head coming down. She gets out of the bath room, calls the hospital, they agree to see her at 7:00. As we later learns, she should have made clearer how far she was and they would have sent an ambulance.&lt;br /&gt;&lt;br /&gt;Instead, on her request, I then call my parents who live nearby and get an awake and triumphant mother in the other end at the first ring; she had a bet with my sister on the birth being this very day. My father will come to pick up us. At this point, noone but Janne has any idea how far she is. Yet, my father hurries through the dark empty Sunday morning streets of Aalborg and we reach the hospital safely.&lt;br /&gt;&lt;br /&gt;There we're greeted by first an assistant and then a midwife whose shift is ending. She decides to have a look at J. anyway and finds out the cervix is fully open, declares her the most tough and calm woman in labor she's seen so far and immediately walks her to the nearest delivery room.&lt;br /&gt;&lt;br /&gt;Lots of pressing, sweating, leg-pulling (the midwife recommends a birth on the side so one leg has to be pulled up during contractions by J. and finally me) and about an hour later at 8:45, our son is fully born. Near the end, when the head is just out, there's a funny episode where the three non-labouring women in the room, the mid-wife, assistant and midwife student are rejoicing over the fine baby head and trying to persuade it into not breathing, which isn't possible as the torso is still not out and compressed by J.&lt;br /&gt;&lt;br /&gt;But it is over. Janne gets the child, I cut the umbilical cord, we get to see the placenta and Janne gets some stitches while the midwife try to distract her by talking about breastfeeding.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-O_VN859C_8Y/TozTx9QDxWI/AAAAAAAAALA/MBUX1Bd16zQ/s1600/nyfodt.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="268" width="400" src="http://1.bp.blogspot.com/-O_VN859C_8Y/TozTx9QDxWI/AAAAAAAAALA/MBUX1Bd16zQ/s400/nyfodt.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;The midwife assistant was kind enough to give us a good pull at the foot so we got all the mucus screamed out of the throat&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-pzl8CuoGDcY/TozVQp0KiTI/AAAAAAAAALI/eS-yQ2A0Suk/s1600/nyfodt-i-varmekasse.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="268" width="400" src="http://3.bp.blogspot.com/-pzl8CuoGDcY/TozVQp0KiTI/AAAAAAAAALI/eS-yQ2A0Suk/s400/nyfodt-i-varmekasse.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;One happy new mother a couple of hours after the birth (and some breakfast) - if the little guy looks somewhat baked, that's because somebody forgot to tell the new father who was ordered to dress him that he was supposed to turn off the heating lamp in the box after giving the boy outdoor clothes on, oops - this was after the father nearly managed to burn his own hair by sticking it into the heating lamp&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-qbTB6q7Zex4/TozXMdzWbyI/AAAAAAAAALQ/Vrb2C2j3btM/s1600/nyfodt-tavle.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="400" width="268" src="http://2.bp.blogspot.com/-qbTB6q7Zex4/TozXMdzWbyI/AAAAAAAAALQ/Vrb2C2j3btM/s400/nyfodt-tavle.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;Apple, Google, beware - they had a magic tablet, when we got in they wrote our names on it, and by magic, when the birth was over there were happy flags and everything on it, I swear I never saw anyone get near it (the text is "first child, fine boy :), pregnant for 41+0 [weeks/days], congratulations with him")&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Afterwards, we were kindly allowed to stay for four days at a patient hotel as a special service for new parents. Four days of getting to know the little guy, the three of us alone, apart from occasional visitors, getting help from midwifes to get breastfeeding going (whoever said the nipple is the only intuitive interface obviously wasn't a mother), and lots of good food. It was fantastic!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7mY6jm-8a7Q/TozcfJfy4DI/AAAAAAAAALY/OFb93C_qpP0/s1600/nyfodt-far.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="400" width="268" src="http://1.bp.blogspot.com/-7mY6jm-8a7Q/TozcfJfy4DI/AAAAAAAAALY/OFb93C_qpP0/s400/nyfodt-far.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;One happy, and somewhat tired, new father&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-eaAtawCdY24/TozdHILlOwI/AAAAAAAAALg/LcccqBuS5zA/s1600/nyfodt-transport-vugge.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="268" width="400" src="http://4.bp.blogspot.com/-eaAtawCdY24/TozdHILlOwI/AAAAAAAAALg/LcccqBuS5zA/s400/nyfodt-transport-vugge.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;The little guy in a transportable cradle we used to get him to the hotel restaurant - the book of hypnosis is used to pacify him temporarily&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;It's 12 days ago today. We've already learned a lot, all three of us. He's now looking around, taking in the world, folding his little fingers, trying to use his limbs, practicing looking angry, smiling, grinning.&lt;br /&gt;&lt;br /&gt;One thing that really surprised me, in addition to the enormous wave of happiness which so far has steered us through sleep deprivation, occasional moments of angry, nerve-wrecking screaming; what really surprised me is how funny such a little guy can be. For instance, at one critical moment in the middle of the night we both hold our breath at a pause in the crying, looking at him, his eye brows pulled together, mouth turned downwards, will he accept our attempt at comforting him? And then he farts loudly, gives us a quick happy grin and falls asleep.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-hBvq8VenO0Q/TozeqL1HB4I/AAAAAAAAALo/_sojpzZSUyc/s1600/nyfodt-closeup.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="268" width="400" src="http://2.bp.blogspot.com/-hBvq8VenO0Q/TozeqL1HB4I/AAAAAAAAALo/_sojpzZSUyc/s400/nyfodt-closeup.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;Closeup of the little guy 6 days later, the red marks above the eyes are now fading, they probably come from J. who gave him a good squeeze when the head was half-way out (the midwife ordered a pause)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;One of these days we're going to give him his first bath. And I can't wait to see what he will do.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4733808734369897733?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4733808734369897733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/10/when-two-becomes-three.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4733808734369897733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4733808734369897733'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/10/when-two-becomes-three.html' title='When two becomes three'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-O_VN859C_8Y/TozTx9QDxWI/AAAAAAAAALA/MBUX1Bd16zQ/s72-c/nyfodt.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6351147981822976637</id><published>2011-07-31T03:06:00.002+02:00</published><updated>2011-07-31T13:11:48.978+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Cells and biology</title><content type='html'>I recently borrowed a book from my sister who has a Master's in biology, &lt;em&gt;Zoology&lt;/em&gt; by Dorit, Walker and Barnes, about 900 pages, intended audience is college biology students I believe. I've been reading it over the summer, and I must say it's the most interesting book I've read in years. I can't recall the last time I've learned so much in such a short time span.&lt;br /&gt;&lt;br /&gt;The book goes through what stuff cells are made of, how respiration and cell division works, how protein compounds are made, how it is thought various solutions to problems evolved, how various features of species stem from physical limits and constraints in the environment. For instance, tiny animals don't have lungs. They don't need them because no part of their internal body is ever far from air or water. The complicated blood and lung system of humans is really an extraneous necessity required by our huge size.&lt;br /&gt;&lt;br /&gt;The book then covers the various animal groups one by one. As it turns out, most of the stuff I at least tended to associate with humans and higher animals is really nothing new. Sex was not invented with the monkeys. Also air is a pretty inhospitable environment, as the book puts it, every animal not living in water is constantly living at the risk of desiccation.&lt;br /&gt;&lt;br /&gt;One of the things I've learned is that the world of cells is an extremely complicated world in its own. A big animal, like a human, is really a big blob of cooperating cells, each cell being a living being in its own right, although of course dependent on the others. The cells in your foot are dependent on the cells in your mouth to swallow food and the cells in your intestine for taking in nutrients from the soup that passes through. And vice versa, no feet means no catching food, at least in nature.&lt;br /&gt;&lt;br /&gt;It's a purpose of life, feeding your cells, and a good one I believe. But each cell is actually a pretty complex thing, with small bacteria-like sub-components called organelles like the mithocondria that take in oxygen and various other stuff and output cell fuel, &lt;a href="http://en.wikipedia.org/wiki/Adenosine_triphosphate"&gt;ATP&lt;/a&gt;, not to mention the complex chemical machinery that replicates and executes the DNA code. Cells have a life of their own. For instance some cells in the body feed on the bacteria and other stuff that enters the tissue by engulfing and digesting it; that's an important aspect of the immune system.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Animal_cell_structure_en.svg/500px-Animal_cell_structure_en.svg.png" imageanchor="1" style=""&gt;&lt;img border="0" height="365" width="500" src="http://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Animal_cell_structure_en.svg/500px-Animal_cell_structure_en.svg.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;Structure of a typical animal cell (from &lt;a href="http://en.wikipedia.org/wiki/File:Animal_cell_structure_en.svg"&gt;Wikipedia&lt;/a&gt;)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Likewise, the interaction between the cells is highly developed, not the least keeping in mind that the chemical cell machinery must at all stages be able to self-repair. It is also grown, unlike a human-made machine in the macro world assembled from ready-made pieces, all animals and all their parts must be made from tiny single cells that collect nutrients and out of them build bigger structures.&lt;br /&gt;&lt;br /&gt;And everything is controlled by the laws of physics and long self-replicating double-helix strands of 4-character code that specifies what happens when.&lt;br /&gt;&lt;br /&gt;It is simply amazing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6351147981822976637?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6351147981822976637/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/07/cells-and-biology.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6351147981822976637'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6351147981822976637'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/07/cells-and-biology.html' title='Cells and biology'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8654161658769682553</id><published>2011-06-17T17:11:00.001+02:00</published><updated>2011-06-22T17:05:04.801+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>3D in the browser</title><content type='html'>We've recently done a project where we needed to do 3D visualizations in the browser. It's not incredibly fancy, mostly showing walls and floors of rooms and some stuff in them.&lt;br /&gt;&lt;br /&gt;Before we started coding, we were discussing what platform to run it on. After some going back and forth, we ended up deciding that despite the 3D requirement, going for a web app would probably pay off in the long run because we then don't have the hassle of installed software and perhaps worse, the problem of predicting what particular platform Microsoft will declare obsolete next year.&lt;br /&gt;&lt;br /&gt;So in the end, we settled on the web, going for HTML and canvas for the 2D part and WebGL, the new OpenGL standard for the web, for the 3D.&lt;br /&gt;&lt;br /&gt;There's only one problem, WebGL was so new that at the time, only Chrome had a release with support. Not long after, Firefox joined the ranks. But still, it's a technology you can't rely on unless you can control the user base and ensure they install the right browser.&lt;br /&gt;&lt;br /&gt;Similar problems kept the 2D HTML canvas back until a project like &lt;a href="http://code.google.com/p/explorercanvas/"&gt;excanvas&lt;/a&gt; turned up. Until Internet Explorer support is there, new standards are a no-go for many.&lt;br /&gt;&lt;br /&gt;Thus &lt;a href="http://maq.dk/"&gt;Martin&lt;/a&gt; has been working on and has now released &lt;a href="http://jebgl.com/"&gt;JebGL&lt;/a&gt;, a WebGL emulation layer for older browsers and most importantly Internet Explorer, back to IE 6.&lt;br /&gt;&lt;br /&gt;It relies on the OpenGL support in Java, translating WebGL calls into JOGL calls in a Java applet. We've had a look at other options, such as using the Flash 3D API, but we needed support for some of the fancy stuff in OpenGL so it would be a pretty huge task to try to emulate that in a completely different API (on a lower level, that's what Google is doing with the &lt;a href="http://code.google.com/p/angleproject/"&gt;ANGLE project&lt;/a&gt; to try to work-around bad OpenGL drivers).&lt;br /&gt;&lt;br /&gt;So as long as Java is in, you can now rely on WebGL (well, barring bugs and missing features in JebGL). And if Java is not there, there's at least the option of asking people to install it, most browsers seem to handle that gracefully these days, rather than requiring them to switch to a completely different browser.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8654161658769682553?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8654161658769682553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/06/3d-in-browser.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8654161658769682553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8654161658769682553'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/06/3d-in-browser.html' title='3D in the browser'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3311560153714340904</id><published>2011-05-30T12:40:00.000+02:00</published><updated>2011-05-30T12:40:55.156+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Organizational patterns</title><content type='html'>We went to a seminar not too long ago in a local interest group on agile software development. One of the speakers was Jim Coplien. He's a good at it, speaking emphatically on what to do and not to do.&lt;br /&gt;&lt;br /&gt;Also he had the interesting point that if you're going to try to change what a software development organization is doing, process improvement programmes turn out to be less effective than one thinks because processes tend to come from the underlying structures. So if you don't change them, people fall back after some time.&lt;br /&gt;&lt;br /&gt;For instance, if you have the problem that the developers aren't good enough at coming up with what the users need, then instead of installing a process where they must fill in check lists to try to force them to listen, maybe you need to look at the fact that they aren't communicating directly with the users, but perhaps through a manager who dictates what should happen. If you change the manager's role, maybe the rest will follow automatically.&lt;br /&gt;&lt;br /&gt;Anyway, he talked at some length about some organizational patterns he and others have come up with, which was interesting because many of the seem to describe what we're doing at IOLA. He's collected a big bunch in a book he's written (together with Neil B. Harrison), "Organizational Patterns of Agile Software Development".&lt;br /&gt;&lt;br /&gt;If you're interested in software development and what works when you structure the work, the team and relationships to stakeholders and users, it's worth a read.&lt;br /&gt;&lt;br /&gt;What's interesting in it is that most patterns may be profound in their effect, but are really lightweight in what you actually need to do. For instance, if you have the problem that developers are interrupted too much, you can sacrifice one person and make him a firewall that people have to go through. Simple as that. No big piles of lists and specifications and meeting minutes and memos necessary.&lt;br /&gt;&lt;br /&gt;I think the big downside of the book is that it's not very friendly written. It's in the style of a reference in many places with only little background rationale and so many crossreferences that you need to have read most of the book to understand what's going. This probably works well for Jim Coplien when he travels around on his research and consultancy campaign to fix sick organizations, but I think the audience for a gentler style would be much higher.&lt;br /&gt;&lt;br /&gt;Still, it has material I haven't seen elsewhere and it's actually based on research on succesful software teams rather than just handwaving.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3311560153714340904?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3311560153714340904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/05/organizational-patterns.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3311560153714340904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3311560153714340904'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/05/organizational-patterns.html' title='Organizational patterns'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3563676992886397093</id><published>2011-04-08T23:36:00.000+02:00</published><updated>2011-04-08T23:36:34.113+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>GNOME 3 is nice</title><content type='html'>So &lt;a href="http://www.gnome.org/"&gt;GNOME 3&lt;/a&gt; is out and I decided to give it a try with the Fedora live CD (or should I say USB stick). I must say I'm impressed. Overall it's really clean and just works. The new application chooser is downright brilliant compared to the old menu. I've tested some of the previews of GNOME Shell, and the thing has come a long way.&lt;br /&gt;&lt;br /&gt;Of course, Alt-tab &lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=608946"&gt;is still broken&lt;/a&gt;, the default (and only) theme is a bit top-heavy, window movement wasn't quite smooth on Intel GMA 950, there was the occasional crash of the new settings stuff, but all in all not bad for a .0 release. And the new direction means that there are now people working on the core user experience, something that appears to have been more or less neglected for a very long time.&lt;br /&gt;&lt;br /&gt;Plus much of the new shell stuff is written in Javascript so should be easy to hack on in case the designers don't come to their senses any time soon (there's already an Alt-tab hack).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3563676992886397093?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3563676992886397093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/04/gnome-3-is-nice.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3563676992886397093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3563676992886397093'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/04/gnome-3-is-nice.html' title='GNOME 3 is nice'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-215504132376193181</id><published>2011-03-29T22:19:00.000+02:00</published><updated>2011-03-29T22:19:34.851+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>A take on good, reusable Java design</title><content type='html'>&lt;a href="http://chaosinmotion.com/blog/?p=622"&gt;This&lt;/a&gt; is just too amusing not to pass on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-215504132376193181?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/215504132376193181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/03/take-on-good-reusable-java-design.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/215504132376193181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/215504132376193181'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/03/take-on-good-reusable-java-design.html' title='A take on good, reusable Java design'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2483297235374834027</id><published>2011-03-23T22:03:00.001+01:00</published><updated>2011-03-23T22:04:49.737+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plants'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Spring time!</title><content type='html'>It's that time of the year again here in Denmark.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-JdS9pFA0JvY/TYkjUgzdSzI/AAAAAAAAAKM/axAftfLhcLg/s1600/foraar-2011.jpg" imageanchor="1" style=""&gt;&lt;img border="0" height="268" width="400" src="http://3.bp.blogspot.com/-JdS9pFA0JvY/TYkjUgzdSzI/AAAAAAAAAKM/axAftfLhcLg/s400/foraar-2011.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The crocuses started flowering, and I managed to sit outside for quite a while in a short-sleeved shirt without freezing to death. There's no doubt. Spring arrived this weekend.&lt;br /&gt;&lt;br /&gt;It's been a busy winter. I...&lt;br /&gt;&lt;br /&gt;... managed to fall sick with flue-resembling symptoms and fever three times in 6 weeks, beginning with the Christmas holidays.&lt;br /&gt;&lt;br /&gt;... shoveled a great deal of snow away, like last year.&lt;br /&gt;&lt;br /&gt;... closed probably around 100 issues in the &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt; issue tracker (and had the misfortune not to discover a well-meaning fork until a month after).&lt;br /&gt;&lt;br /&gt;... mapped a big bunch of roads in &lt;a href="http://www.openstreetmap.org/"&gt;OpenStreetMap&lt;/a&gt;, mostly centered around Janne's parents home. Thanks to the effort of volunteers, we've got aerial photos of almost the entire country and will, I think, within this year have a complete free street map of Denmark. I found out mapping is a fine hobby; it's a nice way to relax while gaining the usual open source feeling of contributing to something greater than yourself.&lt;br /&gt;&lt;br /&gt;... read 16 books on local history of Vendsyssel and Himmerland, the regions where I live, told by old people that recount their youth around 1900-1930, mostly with farming, quite an eye-opener, really, and a great complement to the things we were taught in history lessons in school. I also read the Arthur trilogy by &lt;a href="http://www.bernardcornwell.net/"&gt;Bernard Cornwell&lt;/a&gt; (it wasn't quite as good as his Saxon book series, although maybe I'm just biased because I prefer the brought-up-as-a-Dane protagonist from those).&lt;br /&gt;&lt;br /&gt;... wasted some time with &lt;a href="http://tremulous.net/"&gt;Tremulous 1.2pre&lt;/a&gt; after having stopped playing Tremulous for a long time. It is a surprisingly difficult multiplayer &lt;a href="http://en.wikipedia.org/wiki/First-person_shooter"&gt;FPS&lt;/a&gt;. I mostly play the alien side because I like the speed, but since I'm still not very good at it, this means I have to trick other players into coming close enough to be vulnerable.&lt;br /&gt;&lt;br /&gt;... and annoyingly failed to get into the habit of regular piano practicing again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2483297235374834027?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2483297235374834027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/03/spring-time.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2483297235374834027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2483297235374834027'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/03/spring-time.html' title='Spring time!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-JdS9pFA0JvY/TYkjUgzdSzI/AAAAAAAAAKM/axAftfLhcLg/s72-c/foraar-2011.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5600818154558611879</id><published>2011-03-22T13:11:00.000+01:00</published><updated>2011-03-22T13:11:05.002+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flot 0.7 released!</title><content type='html'>I just released &lt;a href="http://code.google.com/p/flot/"&gt;Flot 0.7&lt;/a&gt;! Highlights are multi-axis support, area charts and a pie chart plugin, and a big bunch of bug fixes, including an IE 9 fix and reworked replotting support that reuses the canvas elements to put an end to the memleak troubles. The complete list of changes &lt;a href="http://flot.googlecode.com/svn/trunk/NEWS.txt"&gt;is here&lt;/a&gt;. There are some API changes from the axis rewrite.&lt;br /&gt;&lt;br /&gt;In related news, I've moved the source code repository to &lt;a href="https://github.com/flot/flot"&gt;github&lt;/a&gt; to make it easier for other people to contribute.&lt;br /&gt;&lt;br /&gt;It has been clear to me for some time that more of the things that are currently inside core Flot needs to move to plugins. As time goes, more specific things turn up on people's wishlist, and right now it's just not possible to satisfy them all without bloating the core. Having specific feature sets in a plugin also makes it much easier for people to customize them a bit by hacking the plugin code without having to maintain a delta with the whole of Flot.&lt;br /&gt;&lt;br /&gt;Fortunately, some people have expressed interest in helping out, partly due to me taking a hiatus from the mailing list for some time last year, so with a bit of luck some of these things may happen in a not too distant future. Maybe we'll even get a better hyperlinked format for the documentation, something that has never really managed to climb to the top of my list of priorities so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5600818154558611879?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5600818154558611879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/03/flot-07-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5600818154558611879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5600818154558611879'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/03/flot-07-released.html' title='Flot 0.7 released!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8307880515219390633</id><published>2011-02-25T22:29:00.004+01:00</published><updated>2011-02-25T22:36:55.448+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Lyrics for GNY Beirblakken</title><content type='html'>Janne and I had some trouble finding the lyrics for Beirblakken performed by &lt;a href="http://www.gny.dk"&gt;GNY&lt;/a&gt; (listen to it on their &lt;a href="http://www.facebook.com/pages/Gny/38911797635"&gt;Facebook page&lt;/a&gt;). Turns out it's from a Norwegian folksong Beiarblakkjen (which perhaps originally means a war horse). There are a couple of versions of it. The full reconstructed text sung by GNY is approximately this:&lt;br /&gt;&lt;br /&gt;Der satte tre kjerringar under ei bro&lt;br /&gt;- dei trea gullsko &lt;br /&gt;Dei skapte blakkjen av mannblo', &lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;Dei skapa blakkjen af manne bein,&lt;br /&gt;- dei trea gullsko&lt;br /&gt;og Beiarblakkjen så kalla dei han.&lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;Og då dei hadde blakkjen gjort,&lt;br /&gt;- dei trea gullsko&lt;br /&gt;då sende dei han til kongjens port.&lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;Da kom'e Niklas fride,&lt;br /&gt;- dei trea gullsko&lt;br /&gt;sa: Eg tore blakkjen ride.&lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;Niklas red til han bli' mo.&lt;br /&gt;- dei trea gullsko&lt;br /&gt;Til støvlane hans bli' fulle av blo'.&lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;Dei første steg som blakkjen sprang&lt;br /&gt;- dei trea gullsko&lt;br /&gt;Da kom han sig til helvede frem&lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;Da han kom til himmelriks dør,&lt;br /&gt;- dei trea gullsko&lt;br /&gt;der hadde blakkjen voret før.&lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;Gravde dei ham i vigde jord,&lt;br /&gt;- dei trea gullsko&lt;br /&gt;så inkje ravnane drikke hans blo'.&lt;br /&gt;- så vide fara dei løyndar ord&lt;br /&gt;&lt;br /&gt;This &lt;a href="http://www.dokpro.uio.no/perl/ordboksoek/ordbok.cgi"&gt;Norwegian dictionary&lt;/a&gt; was most helpful in deciphering some words - "kjerring" means wife/old woman, "løyndar" hidden, "trea" tread, "fride" fair, "mo" tired/paralyzed, "vigde" consecrated, and "blakkjen" is a horse (of the &lt;a href="http://en.wikipedia.org/wiki/Fjord_horse"&gt;old Norwegian fjord horse kind&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8307880515219390633?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8307880515219390633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2011/02/lyrics-for-gny-beirblakken.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8307880515219390633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8307880515219390633'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2011/02/lyrics-for-gny-beirblakken.html' title='Lyrics for GNY Beirblakken'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5845954394941091825</id><published>2010-11-09T20:18:00.004+01:00</published><updated>2010-11-09T20:42:15.966+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Bulk inserting Django objects</title><content type='html'>I've added another Django utility member to my &lt;a href="http://people.iola.dk/olau/python/"&gt;garden of Python&lt;/a&gt;, &lt;code&gt;bulkops.py&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Currently, it consists of two functions, &lt;code&gt;insert_many&lt;/code&gt; and &lt;code&gt;update_many&lt;/code&gt;. They are intended to solve the problem of inserting a big bunch of objects in Django. If you use the standard &lt;code&gt;save()&lt;/code&gt; or &lt;code&gt;create()&lt;/code&gt; methods in Django, you get one SQL query per object. With thousands of objects, you'll simply drown in overhead in executing all these queries.&lt;br /&gt;&lt;br /&gt;There are some alternatives out there, but unfortunately they are pretty complex and specific to a previous version of Django. Instead this code simple makes a list of tuples with the fields on the model and feeds that to the executemany() function in the DB API. Usage is really simple&lt;br /&gt;&lt;pre&gt;for x in seq:&lt;br /&gt;    o = SomeObject()&lt;br /&gt;    o.foo = x&lt;br /&gt;    o.save()&lt;/pre&gt;becomes&lt;br /&gt;&lt;pre&gt;l = []&lt;br /&gt;for x in seq:&lt;br /&gt;    o = SomeObject()&lt;br /&gt;    o.foo = x&lt;br /&gt;    l.append(o)&lt;br /&gt;insert_many(l)&lt;/pre&gt;It's tested to work with Django 1.2 and doesn't really touch the ORM internals so should be safe for at least some future versions.&lt;br /&gt;&lt;br /&gt;Sadly, there's no support for associated many-to-many relationships. I actually needed that myself, but I don't think there's an efficient way of getting the primary ids when you do a bulk insert with the Python DB driver API. So one has to use a &lt;code&gt;select&lt;/code&gt; query anyway afterwards, and then it was actually more convenient to do it by hand by rearranging the logic in the caller.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5845954394941091825?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5845954394941091825/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/11/bulk-inserting-django-objects.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5845954394941091825'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5845954394941091825'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/11/bulk-inserting-django-objects.html' title='Bulk inserting Django objects'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-806125728315662309</id><published>2010-11-08T19:42:00.010+01:00</published><updated>2010-11-09T10:22:31.816+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Cache busting in Django</title><content type='html'>One of the really annoying things in web development is dealing with caching. Browsers incorporate an elaborate caching scheme to speed up common browsing operations, which is a really great idea. But a side-effect is that when you release a change on a site, you risk that some people get the new HTML while their browser is still using an old CSS or Javascript file because their cache hasn't timed out yet.&lt;br /&gt;&lt;br /&gt;It has bitten me several times during development too, some browsers have really aggressive caching schemes. Debugging problems caused by a stale CSS/JS file is just not fun.&lt;br /&gt;&lt;br /&gt;One way to fix it is to use a framework to run the CSS and Javascript through a combiner/minifier that at the same time outputs a versioned filename, e.g. by hashing the contents. There are even a couple of Django projects going down this route to make it happen automatically.&lt;br /&gt;&lt;br /&gt;I haven't found one I liked yet, though. You have to modify views or templates and fit your paths into the system, it doesn't help with changing images, if you use third-party code you have to modify that too, and the minification step makes it pretty hard to debug Javascript problems; even if you isolate it to live, problems happen on live too, sometimes.&lt;br /&gt;&lt;br /&gt;Really, if we take a step back from the minify dream, the simple solution here is to append some kind of version number to the file. Then new HTML should have filenames with new version numbers so clients can't used their old cached files because the paths don't match.&lt;br /&gt;&lt;br /&gt;I've found a really simple way to automate this transparently for a project by modifying one line in &lt;code&gt;settings.py&lt;/code&gt;. The idea is to hook into Django with a middleware class, search the generated HTML for local URLs and replace them with URLs with version numbers. This way, we can catch all CSS and Javascript references as well as images no matter where they occur.&lt;br /&gt;&lt;br /&gt;Since common file systems don't provide version numbers on the files they store, we'll use the modification timestamp instead. And instead of renaming files to contain the version number, we'll just append a &lt;code&gt;?_=timestamp&lt;/code&gt; to the URL:&lt;br /&gt;&lt;pre&gt;  &amp;lt;img src="/media/images/logo.png"&amp;gt;&lt;/pre&gt;becomes&lt;br /&gt;&lt;pre&gt;  &amp;lt;img src="/media/images/logo.png?_=4321234"&amp;gt;&lt;/pre&gt;Everything after the &lt;code&gt;?&lt;/code&gt; is ignored when looking up in the file system by the static file web servers I've tested so far. So it just works.&lt;br /&gt;&lt;br /&gt;As a bonus, when this is running, you can safely modify the web server serving the static files to set an expiration date far out in the future for files requested with &lt;code&gt;?_=xxxxx&lt;/code&gt;. This allows an aggressive, yet perfectly safe caching scheme to be employed by any upstream cache, including the browser.&lt;br /&gt;&lt;br /&gt;The above trick can be done in about 50 lines of Python. You can grab the little self-contained middleware module &lt;code&gt;modtimeurls.py&lt;/code&gt; from my brand-new &lt;a href="http://people.iola.dk/olau/python/"&gt;Garden of Python&lt;/a&gt;. It has been in production on several sites for over a year. The only problem I've found so far was with a PNG transparency fixer for IE6 that assumed &lt;code&gt;.png&lt;/code&gt; files would end with &lt;code&gt;.png&lt;/code&gt; rather than &lt;code&gt;.png?_=xxxxxx&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-806125728315662309?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/806125728315662309/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/11/cache-busting-in-django.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/806125728315662309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/806125728315662309'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/11/cache-busting-in-django.html' title='Cache busting in Django'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-245061671587024768</id><published>2010-11-02T15:23:00.001+01:00</published><updated>2010-11-02T15:26:18.059+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Stoicism</title><content type='html'>This is &lt;a href="http://caffeine.shugendo.org/2010/10/31/being-a-joyful-stoic-1-how-to-want-what-you-have/"&gt;philosophy in practice&lt;/a&gt;. One of the most interesting posts I have read this year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-245061671587024768?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/245061671587024768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/11/stoicism.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/245061671587024768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/245061671587024768'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/11/stoicism.html' title='Stoicism'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-161767438447773217</id><published>2010-10-11T14:19:00.003+02:00</published><updated>2010-11-08T18:36:07.619+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Emacs learning curve</title><content type='html'>Think &lt;a href="http://unix.rulez.org/~calver/pictures/curves.jpg"&gt;this is old&lt;/a&gt;. But still funny.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-161767438447773217?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/161767438447773217/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/10/emacs-learning-curve.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/161767438447773217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/161767438447773217'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/10/emacs-learning-curve.html' title='Emacs learning curve'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3468912246701035995</id><published>2010-09-20T00:02:00.006+02:00</published><updated>2011-02-14T19:49:57.116+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='piano'/><title type='text'>Piano synthesis</title><content type='html'>I've bought a &lt;a href="http://www.roland.com/products/en/HP305/"&gt;digital piano&lt;/a&gt;. Its most important feature is head phones (not included). No more neighbour complaints.&lt;br /&gt;&lt;br /&gt;Because of this, I've also been spending some time, actually too much time, on looking into piano sound synthesis recently.&lt;br /&gt;&lt;br /&gt;The problem is that commercially available synthesizers built into digital pianos don't sound very good. Actually, for people who don't play the piano, they're probably fine. But that doesn't help me!&lt;br /&gt;&lt;br /&gt;So I set out to find out what else is out there. Basically, there are two approaches.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Play back&lt;/b&gt;&lt;br /&gt;One approach is that you sit down in front of a real piano with a couple of microphones and record the sound from all of the keys several times.&lt;br /&gt;&lt;br /&gt;You can't just record one note and shift its frequency because the character of the tones change dramatically from the low end to the high end.&lt;br /&gt;&lt;br /&gt;Furthermore, you can't just record each key once because when you press a key harder, the sound is not just louder but also changes character. For a pianist this change of character is very important. I don't know whether this is true for others, but at least for me my brain quickly adjusts itself to changes in loudness so without a change of character, the result is a flat sound inside my head.&lt;br /&gt;&lt;br /&gt;So the end result is that you need a lot of recordings. 88 notes times at least 15 hardness levels and the decay of the low notes can easily last over 30 seconds before they die out. That's a pretty large amount of data to record and process; as I understand it, you need postprocessing to smooth out inconsistencies in the recordings. And still, in my experience, nobody has figured out how simulate a short staccato attack convincingly with a long sample. Not to mention other interesting things you can do with a real piano, once you get it singing.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mathematical models&lt;/b&gt;&lt;br /&gt;Instead of recording a real piano, another approach is synthesizing the sound from some sort of mathematical model, just like using &lt;a href="http://en.wikipedia.org/wiki/Ray_tracing_(graphics)"&gt;ray tracing&lt;/a&gt; a modeled 3D scene to generate realistically looking special effect images in movies.&lt;br /&gt;&lt;br /&gt;Now, some models start from the idea of trying to emulate the sound of the piano itself directly, based on observations like the sound being nearly harmonic so if we just add some &lt;a href="http://en.wikipedia.org/wiki/Sine_wave"&gt;sine waves&lt;/a&gt; at regular intervals, we've almost got a piano sound.&lt;br /&gt;&lt;br /&gt;As far as I can tell these all sound really awful.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaJsEYxsRI/AAAAAAAAAI0/mMjRVjODcFs/s1600/cristofori.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaJsEYxsRI/AAAAAAAAAI0/mMjRVjODcFs/s400/cristofori.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5518749783574032658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;One of the first pianos. It was before the steel frame was invented. From &lt;a href="http://en.wikipedia.org/wiki/File:Piano_forte_Cristofori_1722.JPG"&gt;Wikipedia&lt;/a&gt;.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I think the explanation is that generating sound is easy, even easier when you've got a computer to add all the sine waves you like, but getting an interesting sound is really hard. And piano makers &lt;a href="http://www.speech.kth.se/music/5_lectures/sounds/sound_example_1.mp3"&gt;have worked on this since at least 1720&lt;/a&gt; using wood and metal, materials with plenty of opportunities for interesting sounds. So a fairly simple model is just not going to cut it, not when it's up against a 400 kg carefully refined wooden steel-string monster.&lt;br /&gt;&lt;br /&gt;Another idea is to try to deduce how the various things inside the piano affect each other. If you look closer, the vibration of a string is a periodic push and pull at the ends of the string. If we follow the interactions from a key hitting a hammer that in turn hits the strings, causing them to vibrate, setting the big wooden plate inside the piano, the sound board, into motion, causing the air to vibrate, which is finally perceived as sound in our ears after undergoing reverberations from the room walls; then we can, in theory, reconstruct a perfect sound through the formulas from physics governing the interactions. A similar idea is used to simulate what happens when things collide in games these days, by the way.&lt;br /&gt;&lt;br /&gt;Now, in practice, it's of course not so simple.&lt;br /&gt;&lt;br /&gt;First, as far as I can tell, nobody has an accurate complete physical model from hammers to strings to soundboard to dampers. So the equations are not there - not yet, although there appears to be a few people working on it.&lt;br /&gt;&lt;br /&gt;Second, modeling a whole piano in a naive manner is just not tractable in real-time with commodity computers because you need to simulate the whereabouts of a huge number of points at least 44100 times per second to generate a playable 44.1 kHz sound. Thus it is vital to identify what constitutes the audible important dynamics in the process and spending the time simulating them.&lt;br /&gt;&lt;br /&gt;Despite the challenges, these physical models can generate quite convincing sounds. You can buy them, currently either in the form of &lt;a href="http://www.pianoteq.com/"&gt;Pianoteq&lt;/a&gt; or the &lt;a href="http://www.roland.com/products/en/V-Piano/"&gt;Roland V-Piano&lt;/a&gt;. What's nice about them is that you can get them to sing like a real piano because they're actually simulating vibrating strings, not just turning recordings on and off.&lt;br /&gt;&lt;br /&gt;Unfortunately, the models aren't perfect yet, and it's certainly audible, generally mostly in the middle registers, the reason being, I think, that at the upper end the sound is actually mostly the thump from the key hitting the bottom of the keybed, and at the lower end it's mostly about generating a really big set of partials so errors here are less noticeable.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Research on physical modeling&lt;/b&gt;&lt;br /&gt;When I started looking at synthesizers I was mostly annoyed that neither the company behind Pianoteq or Roland disclose how they do it. Which probably explains why both haven't gotten it quite right yet. So I started googling for research.&lt;br /&gt;&lt;br /&gt;It does not take more than a cursory glance to see that synthesizing achoustic instruments of various kinds is an active research topic. As far as I can tell there are two cooperating groups working on piano synthesis these days, at least in the English-speaking part of the world.&lt;br /&gt;&lt;br /&gt;One group is at Helsinki University of Technology, some papers published &lt;a href="http://www.acoustics.hut.fi/~hml/publications.html"&gt;last year&lt;/a&gt;, some &lt;a href="http://www.acoustics.hut.fi/research/asp/piano/"&gt;earlier&lt;/a&gt;. Unfortunately, I got the impression that their model is not very convincing yet.&lt;br /&gt;&lt;br /&gt;The other group is at Verona University, in collaboration with an Italian company and instrument maker Viscount. It's possible to google up some information about this group, but I think only &lt;a href="http://home.mit.bme.hu/~bank/publist/index.html"&gt;Balázs Bank&lt;/a&gt; who's also been at Helsinki has an up-to-date home page.&lt;br /&gt;&lt;br /&gt;I have spent some time working on a test implementation of the Verona model based on the &lt;a href="http://home.mit.bme.hu/~bank/publist/taslp-piano/index.html"&gt;paper describing it&lt;/a&gt;, and I'll dedicate the rest of this blog entry to my findings.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Modal piano synthesis&lt;/b&gt;&lt;br /&gt;The Verona model is based on modal synthesis where one starts from the equations from physics and from them figure out at which frequencies the strings exhibit standing self-reinforcing waves; which means they last and are audible after several seconds, an eternity in the millisecond world of impacts.&lt;br /&gt;&lt;br /&gt;The physical responses at these frequencies or modes or partials are then simulated with a simple second-order model. Second-order means that the simulation on each time step uses the previous two values. So for instance, for one string partial, the displacement at time n is calculated as y[n] = a*y[n-1] + b*y[n-2] plus a contribution from the hammer if it is present. If you're wondering how this is started when n = 1, well, we just assume the string starts at rest so y[n-1] = 0. The calculated displacement is basically the sound.&lt;br /&gt;&lt;br /&gt;Hence, once the constants a and b have been chosen, the math is actually pretty simple.&lt;br /&gt;&lt;br /&gt;Of course, in reality it is a bit more complex. The model keeps track of the hammer motion, simulates the impact on the string and the resulting forces and motions at the modes which include both transverse modes (up-down or left-right motion) and longitudinal modes (motion back-forth in the length of the string) plus some longitudinal motion stemming from tension changes by the transverse modes. In order to simulate the sound passing through the soundboard (and the room), the motion of the modes are added and convolved with a recorded soundboard impulse response.&lt;br /&gt;&lt;br /&gt;The details are in the &lt;a href="http://home.mit.bme.hu/~bank/publist/taslp-piano/index.html"&gt;paper&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Disappointingly, if you implement what's described in the paper and enter some reasonable values of the constants, it doesn't sound like a piano. This is of course with the caveat that I might have made a mistake in my implementation. &lt;br /&gt;&lt;br /&gt;However, as far as I can tell, there are at least two important things missing from this model. One is the dampers. All pianos have a felt damper connected to each key, the purpose of which is to silence the strings of the key when it is not pressed down. Pianos produce quite powerful sounds so without some way of silencing notes again, it would be hard to make quick pieces sound good. Surprisingly, there does not appear to be much literature on dampers. The only thing I've found is &lt;a href="http://scitation.aip.org/getpdf/servlet/GetPDFServlet?filetype=pdf&amp;id=JASMAN00012600000200EL49000001&amp;idtype=cvips"&gt;this paper&lt;/a&gt; from last year.&lt;br /&gt;&lt;br /&gt;The other thing missing is some feedback loops. From the soundboard back to each string, between the strings, especially between the strings on the same key (most keys on the piano have more than one string), and between the different transverse directions on the same string. These feedback loops cause interesting dynamic effects.&lt;br /&gt;&lt;br /&gt;In the paper the soundboard is simply dealt with by setting the decay rates of the modes according to a simple model. This corresponds to the soundboard stealing energy from each mode and not delivering anything back. I did an analysis of the &lt;a href="http://theremin.music.uiowa.edu/MIS.piano.html"&gt;piano samples available from the University of Iowa&lt;/a&gt;, the decays of the partials are plotted below together with the simple model fitted to the data.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaJslAreYI/AAAAAAAAAI8/odDyLA-o8H8/s1600/decay-estimation.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 310px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaJslAreYI/AAAAAAAAAI8/odDyLA-o8H8/s400/decay-estimation.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5518749792331331970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As you can already see from the plot, this is not a terribly accurate model.&lt;br /&gt;&lt;br /&gt;In the following, the spectrograms show at each frequency the level of sound over time, capped at 5 kHz and 7 seconds, I've generated them with &lt;code&gt;sox&lt;/code&gt;. First a sampled tone, C3 at fortissimo.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaKotVvKcI/AAAAAAAAAJU/CBZKZ_zX588/s1600/C3.ff.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 250px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaKotVvKcI/AAAAAAAAAJU/CBZKZ_zX588/s400/C3.ff.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5518750825359288770" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I think the weird vertical bars might be echos from the initial thump. Now, if you use the simple function derived from the decay data, the result synthesized sound looks like this (&lt;a href="http://people.iola.dk/olau/misc/synthesis/simple-decay.ogg"&gt;sound example&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaJtlgTfmI/AAAAAAAAAJE/gTCh2xfB6H0/s1600/simple-decay.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 250px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaJtlgTfmI/AAAAAAAAAJE/gTCh2xfB6H0/s400/simple-decay.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5518749809643847266" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The thing is that the soundboard doesn't really follow the simple model in the way it responds to the strings, e.g. it has modes of it own. As a quick measure, I tried to set the decay to decay rate of the nearest measured mode, then it looks like this  (&lt;a href="http://people.iola.dk/olau/misc/synthesis/samples-decay.ogg"&gt;sound example&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_PIM6r0YLhk4/TJaJumwpzEI/AAAAAAAAAJM/kb8iDmCWWa0/s1600/data-decay.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 250px;" src="http://4.bp.blogspot.com/_PIM6r0YLhk4/TJaJumwpzEI/AAAAAAAAAJM/kb8iDmCWWa0/s400/data-decay.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5518749827160722498" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'm not sure what the conclusion is. One attractive feature of the simple model is that only has a couple of parameters so it's easy to change around. I think if one has a more complete model of a soundboard, it might be possible to deduce from that more precisely how it absorbs the sound and set the decay rates accordingly. But still there might be some dynamic effects that aren't taken into account.&lt;br /&gt;&lt;br /&gt;A thing one notices in the spectrogram for the sampled tone is the pulsating nature of the partials. They don't just die out exponentially. The pulses come from both the interaction between the different transverse directions on each string and also the interaction between the other strings on the same key.&lt;br /&gt;&lt;br /&gt;The paper has a couple of suggestions for dealing with these. Basically it boils down to adding some extra modes that can produce &lt;a href="http://en.wikipedia.org/wiki/Beat_(acoustics)"&gt;beating&lt;/a&gt;. The thing is that if you add a mode close to another, the difference in frequency causes the sound to pulsate because of the interference between the two.&lt;br /&gt;&lt;br /&gt;But there's no recipe for how to set the beating.&lt;br /&gt;&lt;br /&gt;My conclusion so far is that you can't use a global value for the beats, they need to be specific for each mode. I haven't gotten around to actually measure these values from the samples. As a quick work-around, I'm right now setting a random beat for each partial; this improved the sound considerably. While measuring samples may be one way to fix the issue, I can't help thinking that this is one area where physics could solve the problem much more precise with less hassle if somebody could figure out how.&lt;br /&gt;&lt;br /&gt;I haven't yet gotten to the point where this sounds realistic enough to warrant a full synthesizer that can eat MIDI. But I get the impression that Pianoteq is using modal synthesis too, so it's certainly possible. If you are interested in the code I've written so far, feel free to contact me.&lt;br /&gt;&lt;br /&gt;If you're interested in how the piano works, a good resource is the &lt;a href="http://www.speech.kth.se/music/5_lectures/contents.html"&gt;Five lectures on the acoustics of the piano&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'll conclude with a random note. The Verona paper only operates with one string per key, but actually a real piano has more so if you use physical meaningfully parameters and don't take this into account when computing hammer impacts, it's going to be off by quite a lot; for a hammer, there's a difference between hitting one string and three.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3468912246701035995?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3468912246701035995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/09/piano-synthesis.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3468912246701035995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3468912246701035995'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/09/piano-synthesis.html' title='Piano synthesis'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_PIM6r0YLhk4/TJaJsEYxsRI/AAAAAAAAAI0/mMjRVjODcFs/s72-c/cristofori.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5986960526330852156</id><published>2010-08-29T19:02:00.009+02:00</published><updated>2010-08-31T15:33:04.962+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Software development and the waterfall model</title><content type='html'>&lt;a href="http://ole-laursen.blogspot.com/2010/08/garden-design.html"&gt;As mentioned&lt;/a&gt;, I recently read The Design of Design by Fred Brooks.&lt;br /&gt;&lt;br /&gt;He covers a lot of ground, albeit not in any rigid fashion. As such, it is not a handbook, but more a collection of thoughts about the design process, most grounded in personal experience. Fred Brooks knows his stuff.&lt;br /&gt;&lt;br /&gt;An interesting point he makes is that the &lt;a href="http://en.wikipedia.org/wiki/Waterfall_model"&gt;waterfall model&lt;/a&gt; of software development is completely flawed and should be abandoned.&lt;br /&gt;&lt;br /&gt;In short, the principle in the waterfall model is reason - good solid thinking: first we figure out all what's needed in a given system, then we figure out all aspects of how to build it, then we build it.&lt;br /&gt;&lt;br /&gt;He has several arguments for why the model is flawed. One of the simple arguments is that it doesn't model how good designers work. Naturally, if you want everyone to follow a certain mode of operation, it would seem like a good idea to copy the mode followed by the good ones.&lt;br /&gt;&lt;br /&gt;For me, the biggest flaw of the waterfall model and its companion, the big fixed-price project, is that it requires one to make the decisions up front where the uncertainty is greatest and the consequences of each decision most remote.&lt;br /&gt;&lt;br /&gt;This shows up even in the earliest phases, e.g. when discussing whether to include a certain requirement or not. Inclusion may mean that the project will run over budget and schedule and produce a completely impractical never-used feature. However, the budget overrun may not even be visible in the design phase, but only show up late in development, and the futility of the feature may not show up until years after deployment where it is discovered accidentally that nobody uses it.&lt;br /&gt;&lt;br /&gt;An important point in the book is that the design process is a learning experience, a process where one learns what's needed and what's possible. And what's elegant and what's good in the particular circumstances. I'd add that the same is true of the embodiment of the design, the code itself. Often people won't understand a system properly until they sit in front of it and have to start doing what they need to do. Deployment is always a learning experience. And similarly, it can be hard as a developer to assess the consequences of a paper design. When the code is written, maybe that fancy idea everybody liked beforehand isn't that hot afterall. &lt;br /&gt;&lt;br /&gt;If one doesn't allow for learning, both for the receivers of the system and the people who deliver it, it's really difficult to get the software right.&lt;br /&gt;&lt;br /&gt;By this thinking, the rational model becomes a bureaucratic way of delivering what might just turn out to be the wrong system.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5986960526330852156?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5986960526330852156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/08/software-development-and-waterfall.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5986960526330852156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5986960526330852156'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/08/software-development-and-waterfall.html' title='Software development and the waterfall model'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6117506745160842965</id><published>2010-08-14T22:38:00.018+02:00</published><updated>2010-08-29T21:09:27.661+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plants'/><title type='text'>Garden design</title><content type='html'>I recently read &lt;a href="http://www.amazon.com/Design-Essays-Computer-Scientist/dp/0201362988"&gt;The Design of Design&lt;/a&gt; by Fred Brooks, the guy behind the mythical man month and the magical silver bullet debunking. I'll save further commentary on the book for now, but one of things he mentions is that rules sometimes help the designer because it sets some boundaries within which creativity can blossom.&lt;br /&gt;&lt;br /&gt;I have a couple of rules for my little patch of a garden.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/TGcK9VlvjcI/AAAAAAAAAH0/gptZkWr2QHc/s1600/passiflora.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 268px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/TGcK9VlvjcI/AAAAAAAAAH0/gptZkWr2QHc/s400/passiflora.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5505381118367468994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;A passiflora growing outside in the summer&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;A first simple rule is that there shall be &lt;i&gt;no grass&lt;/i&gt;. None at all. While lawns are nice and everything, it takes a lot of labour to keep a lawn, ideally it needs mowing at least once a week. Even then, unless you have the perfect growing spot for grass (plenty of sun and water), chances are it won't be a pretty one. This is compared to the other beds which I weed maybe two or three times a year while otherwise enjoying the flowers.&lt;br /&gt;&lt;br /&gt;Also everybody has a lawn. Most don't look pretty. No fun at all.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/TGcM2t7OyJI/AAAAAAAAAIU/9Gv29UMdsyQ/s1600/garden2.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 207px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/TGcM2t7OyJI/AAAAAAAAAIU/9Gv29UMdsyQ/s400/garden2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5505383203664218258" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Another rule is that there shall be &lt;i&gt;as little uncovered ground as possible&lt;/i&gt;. Naked fertile soil is a highly unstable labour-requiring condition, nature will have its way. It also looks silly.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_PIM6r0YLhk4/TGcM2WwCDQI/AAAAAAAAAIM/95tvutmlcII/s1600/garden.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 268px;" src="http://1.bp.blogspot.com/_PIM6r0YLhk4/TGcM2WwCDQI/AAAAAAAAAIM/95tvutmlcII/s400/garden.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5505383197443230978" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Yet another, less simple principle is that the garden must &lt;i&gt;grow organically&lt;/i&gt;, chaotically, like nature, as opposed to being a tidy, trimmed show case of culture. As few straight lines as possible. Make no mistake, it's still designed in the sense that I exert control, it's not nature; if I didn't manipulate it heavily, it would turn into a shrubbery in a few years. But I'm attracted to idea of a green wilderness, to seeing things grow vigorously.&lt;br /&gt;&lt;br /&gt;So I tend only to pull up the most annoying weed, the species that are hard to get rid of, and I let the decaying plants stay where they die (from a pull-up or otherwise). It's easy and it works well, most of it is gone when the season is over.&lt;br /&gt;&lt;br /&gt;I try not to have too many of the same species in the same spot. Nature seldomly has 10 similar specimens right next to each other at exactly the same age. This also offers some protection against various pests.&lt;br /&gt;&lt;br /&gt;And most of the plants I've bought are from seeds or small specimens from the local super markets. There's no overall plan to follow, hence I can satisfy sudden impulses without getting into trouble. And I don't mind having to wait to see the results, watching things grow and looking forward to the adult specimen is part of the joy. It's like buying yourself a gift that you cannot in any way unpack until a year or two after.&lt;br /&gt;&lt;br /&gt;A side-effect of the wilderness idea is that my little patch has a lot of small visitors. Which is certainly something to be happy about. Sitting a summer day in the sun, with bees buzzing around collecting honey from the flowers one has put in place many months ago, removing weeds here and there, noticing details one has never seen before, discovering new species that have invaded the soil on their own. I can think of nothing more peaceful.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_PIM6r0YLhk4/TGcUdkSsAFI/AAAAAAAAAIk/496l66bvHD4/s1600/alcea-rosea.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 288px;" src="http://4.bp.blogspot.com/_PIM6r0YLhk4/TGcUdkSsAFI/AAAAAAAAAIk/496l66bvHD4/s400/alcea-rosea.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5505391567674540114" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_PIM6r0YLhk4/TGcUdWP1yTI/AAAAAAAAAIc/nS0fWEiPAWI/s1600/humlebi.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 268px;" src="http://4.bp.blogspot.com/_PIM6r0YLhk4/TGcUdWP1yTI/AAAAAAAAAIc/nS0fWEiPAWI/s400/humlebi.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5505391563904502066" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Of course, I'm learning from this approach. This year I'm beginning to think I need one more rule. Plants on the pathways must be cut down no matter how interesting they look, otherwise it gets too cumbersome to get around, and then I seldomly get to see the outermost corners.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6117506745160842965?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6117506745160842965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/08/garden-design.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6117506745160842965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6117506745160842965'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/08/garden-design.html' title='Garden design'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_PIM6r0YLhk4/TGcK9VlvjcI/AAAAAAAAAH0/gptZkWr2QHc/s72-c/passiflora.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6433381164816587158</id><published>2010-06-11T16:39:00.003+02:00</published><updated>2010-06-11T16:49:06.341+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>On being the the right size</title><content type='html'>Long time, no write. And now I'm just going to link to an article &lt;a href="http://irl.cs.ucla.edu/papers/right-size.html"&gt;about sizes&lt;/a&gt;, from 1928.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6433381164816587158?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6433381164816587158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2010/06/on-being-the-right-size.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6433381164816587158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6433381164816587158'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2010/06/on-being-the-right-size.html' title='On being the the right size'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-1041467895743766971</id><published>2009-11-12T13:25:00.004+01:00</published><updated>2010-01-26T11:54:00.868+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Javascript and Emacs Lisp</title><content type='html'>Warning: if you don't subscribe to the &lt;a href="http://en.wikipedia.org/wiki/Emacs"&gt;One True Religion&lt;/a&gt;, this probably won't interest you.&lt;br /&gt;&lt;br /&gt;With my recent affair with &lt;a href="http://ole-laursen.blogspot.com/2009/11/django-shell-in-emacs.html"&gt;Elisp&lt;/a&gt; and the development in GNOME where some influential people are dropping C in favour of Javascript for GUI programming (we may wonder how many kilolines of boiler-plate C code they've churned out to reach that conclusion), I couldn't help thinking about replacing Elisp with Javascript in Emacs.&lt;br /&gt;&lt;br /&gt;First a bit of introduction. Emacs is an incredibly old and incredibly smart text editor with a smallish C core and most of the intelligent stuff written in a Lisp dialect called Emacs Lisp. Lisp is a very flexible language that intermingles data and code. Making a system built on it extensible is relatively straight-forward. This is probably the main reason behind the success of Emacs, the editor has been extended in literally thousand of directions over the years. If you read my previous &lt;a href="http://ole-laursen.blogspot.com/2009/11/django-shell-in-emacs.html"&gt;blog post&lt;/a&gt;, I'm using what nowadays is dubbed aspect-oriented programming to hack a built-in Emacs Lisp module.&lt;br /&gt;&lt;br /&gt;It's my impression that even within the Emacs community there are few people who think Emacs Lisp is a great language. It's not &lt;a href="http://en.wikipedia.org/wiki/Common_Lisp"&gt;Common Lisp&lt;/a&gt;, it has some weird idioms and as a small Lisp dialect it still lacks the simplicity of &lt;a href="http://en.wikipedia.org/wiki/Scheme_%28programming_language%29"&gt;Scheme&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;However, weird idioms aside, in my humble opinion the big problem with Emacs Lisp is momentum. When Emacs was started, Lisp was &lt;em&gt;hot&lt;/em&gt;. People were thinking that Lisp would be the future. Back in those days, they even had &lt;a href="http://en.wikipedia.org/wiki/Lisp_machine"&gt;hardware that ran Lisp&lt;/a&gt;! The mind boggles. Lisp was an obvious choice for a dynamic extension language.&lt;br /&gt;&lt;br /&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_PIM6r0YLhk4/SvwADjpP96I/AAAAAAAAAHM/-1_CAxkfmCs/s400/453px-LISP_machine.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5403193714045613986" /&gt;&lt;br /&gt;&lt;br /&gt;Today much has changed and few people are using Lisp. The obvious choice for a dynamic extension language today is something else. Basic, Python, Lua, etc. Or &lt;a href="http://log.ometer.com/2008-08.html"&gt;Javascript&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A die-hard Lisp fan would think the solution is another Lisp, but the official GNU Scheme embeddable language &lt;a href="http://www.glug.org/"&gt;Guile&lt;/a&gt; looks pretty dead to me. While learning Lisp is a worthwhile goal that will definitely teach you some lessons, I think there's good reason why most programmers aren't writing parenthesized lists all day long.&lt;br /&gt;&lt;br /&gt;By switching to Javascript, Emacs would once again be ahead of the curve. And the Emacs maintainers would be relieved of having to maintain a language interpreter with its related problems. For instance, and I don't know if things have changed recently, the garbage collector in Emacs used be pretty basic. Probably because when it just sort of works, nobody wants to mess with it anymore, people naturally want to focus on writing the best editor possible.&lt;br /&gt;&lt;br /&gt;I don't think the idea would fly, however, for several reasons. There is an enormous body of Lisp code out there for Emacs. And since Lisp is the extension language for Emacs, everybody contributing to it is a Lisp hacker. It's hard to see how this actually pretty large group of people making up the community would accept anything else than a Lisp. And in the past, even proposals to reform Emacs Lisp or switch to other Lisp dialects have all failed.&lt;br /&gt;&lt;br /&gt;To add to that, Javascript has a bad reputation, especially among people who haven't done any client-side web development lately.&lt;br /&gt;&lt;br /&gt;Thus I think the only way Javascript in Emacs could happen meaningfully is by targeting the interpreter: replace the Emacs Lisp interpreter with a Javascript engine like &lt;a href="http://www.mozilla.org/js/spidermonkey/"&gt;Spidermonkey&lt;/a&gt; or &lt;a href="http://code.google.com/p/v8/"&gt;V8&lt;/a&gt; and a bridge that translates the Lisp and makes Lisp symbols available to Javascript and vice versa. So all Lisp code would run unaltered, and everyone can continue writing Lisp as they do today.&lt;br /&gt;&lt;br /&gt;The aim would be less code to maintain in Emacs and the benefit of a well-maintained optimizing engine that is getting faster every day, with several independent free ones to choose from.&lt;br /&gt;&lt;br /&gt;I think you could maybe sell that idea to the Emacs maintainers. A project like &lt;a href="http://cedet.sourceforge.net/"&gt;CEDET&lt;/a&gt; that replicates the code parsing engines of modern IDEs would definitely benefit from a speedup.&lt;br /&gt;&lt;br /&gt;As a side-effect, it would then be possible to extend Emacs with pure Javascript. And a growing horde of web developers will suddenly find it much easier to extend Emacs.&lt;br /&gt;&lt;br /&gt;I had a very brief look at the C source of Emacs, and it looks like you would have to rewrite and port maybe 10-20.000 lines of C code. So I think it's doable.&lt;br /&gt;&lt;br /&gt;Of course, there are several open questions. How hard is it to do the translation? Can a Javascript engine actually run Lisp-translated-to-Javascript code efficiently? Is it a good idea at all or just a futile exercise in chasing the latest fad of the day? I am not sure. Javascript has been around for a decade and the web is still growing as a platform, so I don't think it qualifies as a fad. And &lt;a href="http://www.gnome.org"&gt;GNOME&lt;/a&gt; is currently betting its future on it.&lt;br /&gt;&lt;br /&gt;Another option would be to just embed the Javascript engine on top of the current core. As far as I know, that has been tried already with &lt;a href="http://pymacs.progiciels-bpi.ca/README.html"&gt;Python&lt;/a&gt;, and didn't work out. I don't think people will use it unless it is bundled with Emacs and sitting right in the core. After all, most customizations start out with a little hack building on existing code in Emacs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-1041467895743766971?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/1041467895743766971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/11/javascript-and-emacs-lisp.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1041467895743766971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1041467895743766971'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/11/javascript-and-emacs-lisp.html' title='Javascript and Emacs Lisp'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_PIM6r0YLhk4/SvwADjpP96I/AAAAAAAAAHM/-1_CAxkfmCs/s72-c/453px-LISP_machine.jpg' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2815240592069586309</id><published>2009-11-11T23:47:00.004+01:00</published><updated>2009-11-11T23:52:27.970+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Django shell in Emacs</title><content type='html'>Yet another recipe. One of the neat things with Python is its &lt;a href="http://en.wikipedia.org/wiki/REPL"&gt;REPL&lt;/a&gt;, the interactive shell. When working with &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; models, you need to setup a couple of things before you can use the Python shell. Django provides a shell command for doing this, but it makes it difficult to use the built-in Emacs Python REPL.&lt;br /&gt;&lt;br /&gt;I was talking to &lt;a href="http://people.iola.dk/arj/"&gt;Anders&lt;/a&gt; about it the other day when it occurred to me that it could be a quite powerful environment.&lt;br /&gt;&lt;br /&gt;The main problem with the Python shell is that it lacks easy reload support. So you start it up, import the module you're writing and run a couple of functions to test it. Discover an error, fix it with Emacs, save, and then you're in trouble because you can't reload the module in the shell. You have to quit the shell, restart it and reimport the module. History helps but it's still silly (well, actually it's a blatantly stupid oversight, maybe in the top 3 of things to fix in Python to speed up development time).&lt;br /&gt;&lt;br /&gt;But with a shell integrated in Emacs, I could solve this problem, right? So I set out to write the necessary Elisp. Dump the following in your &lt;code&gt;.emacs&lt;/code&gt;, and it should magically work:&lt;br /&gt;&lt;pre style="font-size:smaller"&gt;&lt;br /&gt;;; run Django shell when editing Django Python code&lt;br /&gt;&lt;br /&gt;(defun get-file-in-upstream-dir (location filename)&lt;br /&gt;  (let* ((dir (file-name-directory location))&lt;br /&gt;         (path (concat dir filename)))&lt;br /&gt;    (if (file-exists-p path)&lt;br /&gt;        path&lt;br /&gt;      (if (not (equal dir "/"))&lt;br /&gt;        (get-file-in-upstream-dir (expand-file-name (concat dir "../")) filename)))))&lt;br /&gt;&lt;br /&gt;(defadvice run-python (before possibly-setup-django-project-environment)&lt;br /&gt;  (let* ((settings-py (get-file-in-upstream-dir buffer-file-name "settings.py"))&lt;br /&gt;         (project-dir (file-name-directory settings-py)))&lt;br /&gt;    (if settings-py&lt;br /&gt;        (progn&lt;br /&gt;          (setenv "DJANGO_SETTINGS_MODULE" "settings")&lt;br /&gt;          (setenv "PYTHONPATH" project-dir)))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When the Python interpreter is started, the hook above looks for a settings.py file in the parent directories. If one is found, it sets up Django and also sets the PYTHONPATH to the project toplevel.&lt;br /&gt;&lt;br /&gt;Use the code with &lt;code&gt;C-c C-z&lt;/code&gt; to show interpreter window, &lt;code&gt;C-c C-c&lt;/code&gt; to evaluate buffer, &lt;code&gt;C-c C-r&lt;/code&gt; to evaluate region, etc. (they're in the Python menu at top). &lt;br /&gt;&lt;br /&gt;You'll probably want the following snippet too, it disables the warning that a Python process is still active when you quit Emacs:&lt;br /&gt;&lt;pre style="font-size:smaller"&gt;&lt;br /&gt;(add-hook 'inferior-python-mode-hook&lt;br /&gt;          (lambda ()&lt;br /&gt;            (set-process-query-on-exit-flag (get-process "Python") nil)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I think &lt;code&gt;python-mode&lt;/code&gt; should do this by itself, but in the end I didn't have the energy to report a bug and fight with the Emacs maintainers.&lt;br /&gt;&lt;br /&gt;So did it fix the import problem in Python? Of course not. The shell inside Emacs still can't reimport a module. Sigh. But at least it's easier to test the current module because you can easily evaluate the whole buffer (which works fine).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2815240592069586309?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2815240592069586309/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/11/django-shell-in-emacs.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2815240592069586309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2815240592069586309'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/11/django-shell-in-emacs.html' title='Django shell in Emacs'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6639889234737449182</id><published>2009-10-23T15:48:00.004+02:00</published><updated>2009-10-23T16:10:32.437+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flot 0.6 released!</title><content type='html'>I just released &lt;a href="http://code.google.com/p/flot/"&gt;Flot 0.6&lt;/a&gt;! Highlights are a new plugin system, or at least the beginnings of it, lots of new features (most of them in plugins) and bug fixes, including fixes for IE 8. Check out the &lt;a href="http://flot.googlecode.com/svn/trunk/NEWS.txt"&gt;complete list of changes&lt;/a&gt;. Note that there are some API changes.&lt;br /&gt;&lt;br /&gt;There were still some things I would have liked to do before the release, but it has been dragging along for far too long.&lt;br /&gt;&lt;br /&gt;Otherwise the future is looking bright. The plugin system is not complete yet, but I think the general direction is OK, and will allow third parties to extend Flot considerably without going through me.&lt;br /&gt;&lt;br /&gt;On the technological front, canvas support is gaining traction in the browsers and even support for what used to be advanced stuff like text rendering and export to image is getting more common place. I suspect we will soon have to think hard about whether to continue with the current labels-are-HTML approach or switch to rendering them directly on the canvas.&lt;br /&gt;&lt;br /&gt;Completely unrelated to Flot: I haven't blogged for some time. It's entirely due to lack of time, I have been preoccupied, or gone crazy is probably closer to the truth, with another hobby project which is still some effort away from being useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6639889234737449182?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6639889234737449182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/10/flot-06-released.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6639889234737449182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6639889234737449182'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/10/flot-06-released.html' title='Flot 0.6 released!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7948019641671489053</id><published>2009-08-14T19:23:00.019+02:00</published><updated>2010-11-16T11:34:03.409+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Using CSS3 box-shadow with IE</title><content type='html'>At &lt;a href="http://www.yayart.com/"&gt;YayArt&lt;/a&gt;, we show a lot of images. An important part of the presentation is subtle drop shadows.&lt;br /&gt;&lt;br /&gt;My original solution to that problem was a Javascript hack that inserts a couple of 1 pixel wide divs at the edge of any element with a shadow class. However, although I have tweaked it several times, it has always been a bit slow. For instance, for the shop front page we're currently rendering more than 60 elements with shadows which takes a couple of seconds with Firefox and Firebug on my old trusty PIII 950 MHz laptop. I'll note that this is actually an order of magnitude faster than the (more general) code I based it on from somewhere else. The really tricky part was decent-speed IE 6 support because that actually required measuring the DOM elements.&lt;br /&gt;&lt;br /&gt;However, Firefox 3.5 was released about a month ago, and one of the good news was support for the &lt;code&gt;box-shadow&lt;/code&gt; CSS3 property. It's a relatively simple property for adding drop shadows. So given that Safari has it, and has had for some time, and most Firefox users will soon have it, it starts getting interesting. For reference, about half of our visitors are Firefox + Safari (+ Chrome which uses the same rendering engine as Safari).&lt;br /&gt;&lt;br /&gt;If you search for &lt;code&gt;box-shadow&lt;/code&gt;, some are already using it to add a bit of extra embellishment. However, on YayArt, it's an integrated part of the design. So I had to figure out how to make it work on all browsers.&lt;br /&gt;&lt;br /&gt;I ended up with a three-way solution. For recent Firefox/Safari/... I use &lt;code&gt;box-shadow&lt;/code&gt;. For non-&lt;code&gt;box-shadow&lt;/code&gt; supporting browsers, I fallback to the old hack. And for Internet Explorer, I'm using a new hack based on the proprietary filter stuff in IE.&lt;br /&gt;&lt;br /&gt;Here's a quick sketch. I put this in the general stylesheet:&lt;br /&gt;&lt;pre style="font-size:smaller"&gt;.shadowed {&lt;br /&gt;-moz-box-shadow: 2px 2px 3px #969696;&lt;br /&gt;-webkit-box-shadow: 2px 2px 3px #969696;&lt;br /&gt;}&lt;/pre&gt;This makes it work with browsers based on the Gecko and Webkit engines. Some people recommend adding a plain &lt;code&gt;box-shadow&lt;/code&gt; line, but I need full control over which browsers are responding, and besides it seems to defeat the purpose of the browsers using the &lt;code&gt;-engine-&lt;/code&gt; prefix. Here's a demo:&lt;br /&gt;&lt;br /&gt;&lt;div style="-moz-box-shadow: 2px 2px 3px #969696;-webkit-box-shadow: 2px 2px 3px #969696;width:100px;background-color:#eee"&gt;If your browser is recent enough, you can see a shadow here.&lt;/div&gt;&lt;br /&gt;Now for the fallback, I need to discover in Javascript whether the browser is using the CSS:&lt;br /&gt;&lt;pre style="font-size:smaller"&gt;function supportsBoxShadow() {&lt;br /&gt;var s = document.body.style;&lt;br /&gt;return s.WebkitBoxShadow !== undefined || s.MozBoxShadow !== undefined;&lt;br /&gt;}&lt;/pre&gt;The assumption is that the symbols will be defined only if the browser actually draws the shadow. This seems to be about right, the only exception I've found so far is the Webkit port to GTK (there's a &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=27921"&gt;bug report open&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Final point is Internet Explorer. As it turns out, there is a DropShadow filter. It's not terrible useful for this purpose, however. There are no soft edges, and it shadows the content rather than the containing box (it simply redraws the content with an offset in another color). So for my purpose I need to set a background color to ensure it's drawing a rectangle and not a text shadow. You could probably hack the lack of blurring with another filter, but in the end I went with a simple three-one-pixel-wide-lines approach that looks like my old hack but is much faster (&lt;b&gt;updated&lt;/b&gt;, replaced the filter with a more appropriate one as suggested in the comments):&lt;br /&gt;&lt;pre style="font-size:smaller"&gt;.shadowed {&lt;br /&gt;background-color: #fff;&lt;br /&gt;zoom: 1;&lt;br /&gt;filter: progid:DXImageTransform.Microsoft.Shadow(color='#969696', Direction=135, Strength=3);&lt;br /&gt;}&lt;/pre&gt;This is served for IE only. The &lt;code&gt;zoom: 1&lt;/code&gt; is a hack to ensure that the element gets a layout (to work around the usual IE 6 bug).&lt;br /&gt;&lt;br /&gt;So there you have it. Practical drop shadows. I will have to adjust the code a little bit as the other browsers get support, but I can live with that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7948019641671489053?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7948019641671489053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/08/using-css3-box-shadow-with-ie.html#comment-form' title='35 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7948019641671489053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7948019641671489053'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/08/using-css3-box-shadow-with-ie.html' title='Using CSS3 box-shadow with IE'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>35</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6233452970111573938</id><published>2009-08-07T20:01:00.008+02:00</published><updated>2009-11-20T17:55:35.813+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Emacs 23...</title><content type='html'>... is out and has been so for over a week. Why didn't somebody tell me?&lt;br /&gt;&lt;br /&gt;In any case, I upgraded to Emacs 23 on my Debian unstable laptop. This means a new font. I put &lt;code&gt;Emacs.font: Bitstream Vera Sans Mono 9&lt;/code&gt; in my .Xresources file and am enjoying it so far. It's a bit muddy so I wasn't sure at first, but after having stared at it for a while the old font is just too skinny. Note that configuring the font in &lt;code&gt;.emacs&lt;/code&gt; is still broken (sigh), in that it causes the window to jerk when you start Emacs.&lt;br /&gt;&lt;br /&gt;I've been &lt;a href="http://emacsbugs.donarmstrong.com/cgi-bin/bugreport.cgi?bug=1765"&gt;lobbying&lt;/a&gt; for changing the defaults over to make copy-paste between Emacs and other X applications (from this century) work properly. Apparently, &lt;code&gt;(setq x-select-enable-clipboard t)&lt;/code&gt; is now better, but still not according to the spec. And it's not default yet.&lt;br /&gt;&lt;br /&gt;I noticed some Tramp updates (for accessing remote files) in the NEWS and decided to give it a spin for the first time. Try C-x C-f someserver:myfile.txt and with a little patience, you're editing a remote file locally! Very neat.&lt;br /&gt;&lt;br /&gt;Feeling adventurous I also tried &lt;a href="http://www.emacswiki.org/emacs/InteractivelyDoThings"&gt;IDO&lt;/a&gt;, an update of iswitchb that works with file find (so overrides C-x C-f). I'm turning it on with&lt;br /&gt;&lt;pre&gt;(ido-mode 1)&lt;br /&gt;(setq ido-enable-flex-matching t)&lt;br /&gt;(setq ido-use-filename-at-point t)&lt;/pre&gt;replacing iswitchb and ffap settings. We'll see how it goes.&lt;br /&gt;&lt;br /&gt;Finally, &lt;a href="http://people.iola.dk/arj/"&gt;Anders&lt;/a&gt; mentioned a new Javascript mode, &lt;a href="http://code.google.com/p/js2-mode/"&gt;js2-mode&lt;/a&gt;, which I'm definitely going to try out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6233452970111573938?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6233452970111573938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/08/emacs-23.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6233452970111573938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6233452970111573938'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/08/emacs-23.html' title='Emacs 23...'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6048681806727165503</id><published>2009-07-04T15:15:00.005+02:00</published><updated>2009-11-20T17:57:15.820+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Using X11 over high-latency ADSL and an AltGr NX solution</title><content type='html'>X11 has been bad at high-latency links for a long time. Supposedly, the Xlib replacement XCB would fix some of that, but so far no-one seems to have picked it up. I found some work on GTK+ in 2006 but nothing since then. Sigh.&lt;br /&gt;&lt;br /&gt;So there's this other proprietary solution, Nomachine NX with a free companion FreeNX. I decided to give it a try today, and after spending some time contemplating how to get it installed on Debian, it's not in the main repository because the basic architecture is broken (it's including it's own copy of X and SSH and some other stuff), I finally realized that the easiest setup was probably just downloading and installing the .debs on Nomachine's web site.&lt;br /&gt;&lt;br /&gt;I was hoping to get &lt;code&gt;ssh somehost&lt;/code&gt; then &lt;code&gt;emacs &amp;&lt;/code&gt; working, but it's a bit weirder than that. You have to start up a graphical connection thing, in which you can fortunately select a console. This gives you a remote xterm. The rest is simple.&lt;br /&gt;&lt;br /&gt;I must say I'm very impressed with the performance. Of course, I'm only using Emacs but it works really well. It's like being connected through a local network.&lt;br /&gt;&lt;br /&gt;Of course, once I started coding, the first time I needed a curly brace, which happens soon enough with Javascript, it was bam, back to start. AltGr sends an arrow-key left. After some digging and hair-pulling, it turns out I needed to run &lt;code&gt;setxkbmap [layout]&lt;/code&gt; where layout is "dk" or "de" or similar. Except &lt;code&gt;setxkbmap&lt;/code&gt; wasn't installed. So I installed it. Then it was missing it's data. I installed that too. Now everything is fine.&lt;br /&gt;&lt;br /&gt;If someone would now just port this kind of thing to the default X.org setup, then I would be a happy man. No more console Emacs sessions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6048681806727165503?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6048681806727165503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/07/using-x11-over-high-latency-adsl-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6048681806727165503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6048681806727165503'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/07/using-x11-over-high-latency-adsl-and.html' title='Using X11 over high-latency ADSL and an AltGr NX solution'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6989892581771494832</id><published>2009-05-01T15:46:00.009+02:00</published><updated>2009-05-01T16:29:01.462+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Safe truncation of HTML</title><content type='html'>Another recipe, this time for solving the problem of truncating a piece of HTML, i.e. turning "&amp;lt;p&amp;gt;Blah blah blah&amp;lt;/p&amp;gt;" into "&amp;lt;p&amp;gt;Blah ...&amp;lt;/p&amp;gt;". Google didn't really turn anything useful up, except for a suggestion of using a full-blown HTML parser and then simplifying the result, so I thought I would post the snippet here for Google to pick up.&lt;br /&gt;&lt;br /&gt;The code never splits a valid tag or character entity. It should be able to cope with invalid HTML too, but note that it won't sanitize it. So for instance, if there's an unbalanced &amp;lt;a&amp;gt; in the source string, it won't fix it. Character entities are dealt with by counting them as one character.&lt;br /&gt;&lt;br /&gt;The basic idea in the snippet is that we just skip through the string unless we encounter an opening tag. If so, we see if we can find the corresponding end tag and save it for later. When we got enough non-HTML characters, a ... is put in and any saved but not yet used end tags are added to the output.&lt;br /&gt;&lt;br /&gt;Here's the code in Python (it's easily turned into a Django filter), I aimed for readability rather than ultra-regexp ninja tricks:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-size: 11px; line-height: 13px;" class="highlight"&gt;&lt;pre&gt;&lt;span style="color: #008000; font-weight: bold"&gt;import&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;re&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;tag_end_re &lt;span style="color: #666666"&gt;=&lt;/span&gt; re&lt;span style="color: #666666"&gt;.&lt;/span&gt;compile(&lt;span style="color: #BA2121"&gt;r&amp;#39;(\w+)[^&amp;gt;]*&amp;gt;&amp;#39;&lt;/span&gt;)&lt;br /&gt;entity_end_re &lt;span style="color: #666666"&gt;=&lt;/span&gt; re&lt;span style="color: #666666"&gt;.&lt;/span&gt;compile(&lt;span style="color: #BA2121"&gt;r&amp;#39;(\w+;)&amp;#39;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #AA22FF"&gt;@register&lt;/span&gt;&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter&lt;br /&gt;&lt;span style="color: #008000; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #0000FF"&gt;truncatehtml&lt;/span&gt;(string, length, ellipsis&lt;span style="color: #666666"&gt;=&lt;/span&gt;&lt;span style="color: #BA2121"&gt;&amp;#39;...&amp;#39;&lt;/span&gt;):&lt;br /&gt;    &lt;span style="color: #BA2121; font-style: italic"&gt;&amp;quot;&amp;quot;&amp;quot;Truncate HTML string, preserving tag structure and character entities.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;    output_length &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #666666"&gt;0&lt;/span&gt;&lt;br /&gt;    i &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #666666"&gt;0&lt;/span&gt;&lt;br /&gt;    pending_close_tags &lt;span style="color: #666666"&gt;=&lt;/span&gt; {}&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #008000; font-weight: bold"&gt;while&lt;/span&gt; output_length &lt;span style="color: #666666"&gt;&amp;lt;&lt;/span&gt; length &lt;span style="color: #AA22FF; font-weight: bold"&gt;and&lt;/span&gt; i &lt;span style="color: #666666"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color: #008000"&gt;len&lt;/span&gt;(string):&lt;br /&gt;        c &lt;span style="color: #666666"&gt;=&lt;/span&gt; string[i]&lt;br /&gt;        &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; c &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #BA2121"&gt;&amp;#39;&amp;lt;&amp;#39;&lt;/span&gt;:&lt;br /&gt;            &lt;span style="color: #408080; font-style: italic"&gt;# probably some kind of tag&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; i &lt;span style="color: #AA22FF; font-weight: bold"&gt;in&lt;/span&gt; pending_close_tags:&lt;br /&gt;                &lt;span style="color: #408080; font-style: italic"&gt;# just pop and skip if it&amp;#39;s closing tag we already knew about&lt;/span&gt;&lt;br /&gt;                i &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #008000"&gt;len&lt;/span&gt;(pending_close_tags&lt;span style="color: #666666"&gt;.&lt;/span&gt;pop(i))&lt;br /&gt;            &lt;span style="color: #008000; font-weight: bold"&gt;else&lt;/span&gt;:&lt;br /&gt;                &lt;span style="color: #408080; font-style: italic"&gt;# else maybe add tag&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;                i &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #666666"&gt;1&lt;/span&gt;&lt;br /&gt;                match &lt;span style="color: #666666"&gt;=&lt;/span&gt; tag_end_re&lt;span style="color: #666666"&gt;.&lt;/span&gt;match(string[i:])&lt;br /&gt;                &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; match:&lt;br /&gt;                    tag &lt;span style="color: #666666"&gt;=&lt;/span&gt; match&lt;span style="color: #666666"&gt;.&lt;/span&gt;groups()[&lt;span style="color: #666666"&gt;0&lt;/span&gt;]&lt;br /&gt;                    i &lt;span style="color: #666666"&gt;+=&lt;/span&gt; match&lt;span style="color: #666666"&gt;.&lt;/span&gt;end()&lt;br /&gt;  &lt;br /&gt;                    &lt;span style="color: #408080; font-style: italic"&gt;# save the end tag for possible later use if there is one&lt;/span&gt;&lt;br /&gt;                    match &lt;span style="color: #666666"&gt;=&lt;/span&gt; re&lt;span style="color: #666666"&gt;.&lt;/span&gt;search(&lt;span style="color: #BA2121"&gt;r&amp;#39;(&amp;lt;/&amp;#39;&lt;/span&gt; &lt;span style="color: #666666"&gt;+&lt;/span&gt; tag &lt;span style="color: #666666"&gt;+&lt;/span&gt; &lt;span style="color: #BA2121"&gt;&amp;#39;[^&amp;gt;]*&amp;gt;)&amp;#39;&lt;/span&gt;, string[i:], re&lt;span style="color: #666666"&gt;.&lt;/span&gt;IGNORECASE)&lt;br /&gt;                    &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; match:&lt;br /&gt;                        pending_close_tags[i &lt;span style="color: #666666"&gt;+&lt;/span&gt; match&lt;span style="color: #666666"&gt;.&lt;/span&gt;start()] &lt;span style="color: #666666"&gt;=&lt;/span&gt; match&lt;span style="color: #666666"&gt;.&lt;/span&gt;groups()[&lt;span style="color: #666666"&gt;0&lt;/span&gt;]&lt;br /&gt;                &lt;span style="color: #008000; font-weight: bold"&gt;else&lt;/span&gt;:&lt;br /&gt;                    output_length &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #666666"&gt;1&lt;/span&gt; &lt;span style="color: #408080; font-style: italic"&gt;# some kind of garbage, but count it in&lt;/span&gt;&lt;br /&gt;                    &lt;br /&gt;        &lt;span style="color: #008000; font-weight: bold"&gt;elif&lt;/span&gt; c &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #BA2121"&gt;&amp;#39;&amp;amp;&amp;#39;&lt;/span&gt;:&lt;br /&gt;            &lt;span style="color: #408080; font-style: italic"&gt;# possible character entity, we need to skip it&lt;/span&gt;&lt;br /&gt;            i &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #666666"&gt;1&lt;/span&gt;&lt;br /&gt;            match &lt;span style="color: #666666"&gt;=&lt;/span&gt; entity_end_re&lt;span style="color: #666666"&gt;.&lt;/span&gt;match(string[i:])&lt;br /&gt;            &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; match:&lt;br /&gt;                i &lt;span style="color: #666666"&gt;+=&lt;/span&gt; match&lt;span style="color: #666666"&gt;.&lt;/span&gt;end()&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #408080; font-style: italic"&gt;# this is either a weird character or just &amp;#39;&amp;amp;&amp;#39;, both count as 1&lt;/span&gt;&lt;br /&gt;            output_length &lt;span style="color: #666666"&gt;+=&lt;/span&gt; &lt;span style="color: #666666"&gt;1&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #008000; font-weight: bold"&gt;else&lt;/span&gt;:&lt;br /&gt;            &lt;span style="color: #408080; font-style: italic"&gt;# plain old characters&lt;/span&gt;&lt;br /&gt;            skip_to &lt;span style="color: #666666"&gt;=&lt;/span&gt; string&lt;span style="color: #666666"&gt;.&lt;/span&gt;find(&lt;span style="color: #BA2121"&gt;&amp;#39;&amp;lt;&amp;#39;&lt;/span&gt;, i, i &lt;span style="color: #666666"&gt;+&lt;/span&gt; length)&lt;br /&gt;            &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; skip_to &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #666666"&gt;-1&lt;/span&gt;:&lt;br /&gt;                skip_to &lt;span style="color: #666666"&gt;=&lt;/span&gt; string&lt;span style="color: #666666"&gt;.&lt;/span&gt;find(&lt;span style="color: #BA2121"&gt;&amp;#39;&amp;amp;&amp;#39;&lt;/span&gt;, i, i &lt;span style="color: #666666"&gt;+&lt;/span&gt; length)&lt;br /&gt;            &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; skip_to &lt;span style="color: #666666"&gt;==&lt;/span&gt; &lt;span style="color: #666666"&gt;-1&lt;/span&gt;:&lt;br /&gt;                skip_to &lt;span style="color: #666666"&gt;=&lt;/span&gt; i &lt;span style="color: #666666"&gt;+&lt;/span&gt; length&lt;br /&gt;                &lt;br /&gt;            &lt;span style="color: #408080; font-style: italic"&gt;# clamp&lt;/span&gt;&lt;br /&gt;            delta &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #008000"&gt;min&lt;/span&gt;(skip_to &lt;span style="color: #666666"&gt;-&lt;/span&gt; i,&lt;br /&gt;                        length &lt;span style="color: #666666"&gt;-&lt;/span&gt; output_length,&lt;br /&gt;                        &lt;span style="color: #008000"&gt;len&lt;/span&gt;(string) &lt;span style="color: #666666"&gt;-&lt;/span&gt; i)&lt;br /&gt;&lt;br /&gt;            output_length &lt;span style="color: #666666"&gt;+=&lt;/span&gt; delta&lt;br /&gt;            i &lt;span style="color: #666666"&gt;+=&lt;/span&gt; delta&lt;br /&gt;                        &lt;br /&gt;    output &lt;span style="color: #666666"&gt;=&lt;/span&gt; [string[:i]]&lt;br /&gt;    &lt;span style="color: #008000; font-weight: bold"&gt;if&lt;/span&gt; output_length &lt;span style="color: #666666"&gt;==&lt;/span&gt; length:&lt;br /&gt;        output&lt;span style="color: #666666"&gt;.&lt;/span&gt;append(ellipsis)&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000; font-weight: bold"&gt;for&lt;/span&gt; k &lt;span style="color: #AA22FF; font-weight: bold"&gt;in&lt;/span&gt; sorted(pending_close_tags&lt;span style="color: #666666"&gt;.&lt;/span&gt;keys()):&lt;br /&gt;        output&lt;span style="color: #666666"&gt;.&lt;/span&gt;append(pending_close_tags[k])&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000; font-weight: bold"&gt;return&lt;/span&gt; &lt;span style="color: #BA2121"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style="color: #666666"&gt;.&lt;/span&gt;join(output)&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6989892581771494832?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6989892581771494832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/05/safe-truncation-of-html.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6989892581771494832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6989892581771494832'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/05/safe-truncation-of-html.html' title='Safe truncation of HTML'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-111815539698978408</id><published>2009-04-17T17:19:00.008+02:00</published><updated>2010-11-08T18:35:01.526+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Test if uploaded file is JPEG, PNG or TIFF</title><content type='html'>I've been looking at some of uploads that went wrong on &lt;a href="http://www.yayart.com/"&gt;YayArt&lt;/a&gt; lately, and it turns out that people sometimes submit images with the wrong extension, e.g. "someimage.png" when it's really a JPEG. This confuses the image backend we're using to process large images, &lt;a href="http://www.vips.ecs.soton.ac.uk/index.php?title=VIPS"&gt;VIPS&lt;/a&gt;, so it reports back an error.&lt;br /&gt;&lt;br /&gt;I did a bit of googling, and it seems the easiest way out is to simply check the first few bytes of the file for magic numbers. So here's a bit of Python code for checking for whether the file data belongs to a JPEG, PNG or TIFF image:&lt;br /&gt;&lt;pre style="font-size: smaller"&gt;def is_jpg(data):&lt;br /&gt;    return data[:2] == '\xff\xd8'&lt;br /&gt;&lt;br /&gt;def is_png(data):&lt;br /&gt;    return data[:8] == '\x89PNG\x0d\x0a\x1a\x0a'&lt;br /&gt;&lt;br /&gt;def is_tiff(data):&lt;br /&gt;    return data[:4] == 'MM\x00\x2a' or data[:4] == 'II\x2a\x00'&lt;br /&gt;&lt;/pre&gt;If the file is already on disk, you can grab the first few bytes with&lt;br /&gt;&lt;pre style="font-size: smaller"&gt;f = open("somefile.jpg", 'r')&lt;br /&gt;data = f.read(11)&lt;br /&gt;if is_jpeg(data):&lt;br /&gt;    ext = ".jpg"&lt;br /&gt;elif is_png(data):&lt;br /&gt;    ext = ".jpg"&lt;/pre&gt;Of course this won't test that the whole file is valid. But it's easier to do that afterwards with an image library once the extension is correct.&lt;br /&gt;&lt;br /&gt;The magic numbers are documented in the specifications for the formats. You can also find some help for other formats in &lt;a href="ftp://ftp.astron.com/pub/file/"&gt;the source code&lt;/a&gt; of the &lt;code&gt;file&lt;/code&gt; command on Unix systems.&lt;br /&gt;&lt;br /&gt;Update: I'm liking this so much that I ended up putting it in a separate file and making a convenience function for getting an extension like '.jpg'. Grab the &lt;a href="http://people.iola.dk/olau/python/imagedetect.py"&gt;Python file here&lt;/a&gt;. I also added support for GIF. Here's another &lt;a href="http://www.astro.keele.ac.uk/oldusers/rno/Computing/File_magic.html"&gt;easy reference for magic file numbers&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Second update: I've updated the code, there was a bug detecting JPEGs from certain digital cameras that put Exif data in the first segment. Suffice to check the two first bytes of the JPEG, then the problem does not occur.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-111815539698978408?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/111815539698978408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/04/test-if-uploaded-file-is-jpeg-png-or.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/111815539698978408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/111815539698978408'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/04/test-if-uploaded-file-is-jpeg-png-or.html' title='Test if uploaded file is JPEG, PNG or TIFF'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3451178901281755821</id><published>2009-04-08T12:07:00.015+02:00</published><updated>2009-04-08T13:15:15.694+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='art'/><title type='text'>Good art and bad art</title><content type='html'>There's a couple of things I've learned in the process with &lt;a href="http://www.yayart.com/"&gt;YayArt&lt;/a&gt; that I'd like to share. First, the key question: &lt;i&gt;what is good art?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;If you're like me, you'll probably think that this is impossible to say, or at least so difficult to answer that you need to have studied art for many years, be part of the art elite, to answer in a qualified way.&lt;br /&gt;&lt;br /&gt;That's right. But in my opinion also wrong.&lt;br /&gt;&lt;br /&gt;One approach to definition of good art is the Aristotelian approach. We try to pinpoint the common traits of the subject. This is a slippery path, but we can still do some.&lt;br /&gt;&lt;br /&gt;First, art is a result of craftmanship which is performed with the purpose of engaging you in some way, by moving you, touching your feelings. It must be more than just an everyday thing.&lt;br /&gt;&lt;br /&gt;If you're part of the art elite, you would add to this that good new art must bring something fresh to the table every time. &lt;i&gt;It must be innovative&lt;/i&gt;. The past is important, to the point that new works that in the past would have been considered good art seize to be interesting.&lt;br /&gt;&lt;br /&gt;If you're not part of the art elite but like me, you're probably more interested in the looks than prior art. &lt;i&gt;It must be pleasing to the eye&lt;/i&gt;, or at least pleasingly unpleasant. The small details that are hard to define, but nevertheless obvious, must be right.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.yayart.com/shop/2129/"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_PIM6r0YLhk4/SdyE4IsgWYI/AAAAAAAAAGs/APxZw4i-WpI/s320/2129_618x618.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5322274959586777474" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;Good or bad art?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;There's a conflict between these opposing views, and I believe this conflict leads directly to museums and obscure works like the infamous diamond skull by Damien Hurst, which most people are likely to agree is pretty ugly.&lt;br /&gt;&lt;br /&gt;Why do most people hardly ever go to museums for inspiration? Is it because they're not susceptible to art? They lack the gene that enables them to experience art? This seems like a dubious explanation. Witness the commercial success of photography and Hollywood. Pictures work. And good art is, by definition, capable of engaging people.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.yayart.com/shop/2113/"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://4.bp.blogspot.com/_PIM6r0YLhk4/SdyE4CElltI/AAAAAAAAAGk/Y9b0_A6ZNik/s320/2113_618x618.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5322274957808735954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;Good or bad art?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;The explanation is probably rather that going to a museum is cumbersome, and when you finally get there you might only see art that is definitely different, but doesn't engage you. It doesn't speak to you. Art that doesn't engage you is bad art, by definition - for you.&lt;br /&gt;&lt;br /&gt;With YayArt we're trying to make it less cumbersome to see the art. And we are trying to replace the elitist notion of good art as being innovative art by something else. It's not that having an art elite is bad. It's just that we think there's a large proportion of people who could enjoy and benefit from art if the current art market would serve them. Or a new art market emerged.&lt;br /&gt;&lt;br /&gt;The definition of good art we're using is a platonic definition, i.e. it works by examples. For instance, to define a chair to native from the jungle we wouldn't talk about furniture and four legs but instead point at the six chairs around the dining table and say, now do you understand?&lt;br /&gt;&lt;br /&gt;At YayArt we show you a piece of art and ask, is it good or bad?&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.yayart.com/shop/1660/"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://1.bp.blogspot.com/_PIM6r0YLhk4/SdyE3w3vc8I/AAAAAAAAAGc/FAfRUMgQdso/s320/1660_618x618.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5322274953191453634" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;Good or bad art?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;This is not abstract, so it is easy for everyone, not just art pros, to answer. What we're implicitly asking are questions like, does it move you? Do you like it? Everybody can answer that.&lt;br /&gt;&lt;br /&gt;And when you think about it, this is really what good art is about.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3451178901281755821?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3451178901281755821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/04/good-art-and-bad-art.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3451178901281755821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3451178901281755821'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/04/good-art-and-bad-art.html' title='Good art and bad art'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_PIM6r0YLhk4/SdyE4IsgWYI/AAAAAAAAAGs/APxZw4i-WpI/s72-c/2129_618x618.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-1509373155702445567</id><published>2009-03-26T15:30:00.004+01:00</published><updated>2009-11-20T17:59:24.282+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Ketil Bjørnstad</title><content type='html'>I finished reading &lt;i&gt;To Music&lt;/i&gt; by &lt;a href="http://www.ketilbjornstad.com/author/author_frame.htm"&gt;Ketil Bjørnstad&lt;/a&gt; the other day.&lt;br /&gt;&lt;br /&gt;It's mixing a realistic presentation of an young aspiring pianist and his chaotic life before his carrier starts with a sneaky, subtle weirdiness, a bit like &lt;a href="http://en.wikipedia.org/wiki/Twin_Peaks"&gt;Twin Peaks&lt;/a&gt;; very unnerving but at the same time funny. Of course, once I started on it, it was impossible to put down. For any interested in their general health condition, I recommend staying away. Seriously.&lt;br /&gt;&lt;br /&gt;It makes me wonder what I look for in a book when I go to the library. My previous book was by &lt;a href="http://en.wikipedia.org/wiki/Alistair_MacLean"&gt;Alistair MacLean&lt;/a&gt;, which today requires an humorous attitude towards his anachronistic a-man-is-man world views to read, at least for me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-1509373155702445567?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/1509373155702445567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/ketil-bjrnstad.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1509373155702445567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1509373155702445567'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/ketil-bjrnstad.html' title='Ketil Bjørnstad'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6948953547903565656</id><published>2009-03-20T20:25:00.013+01:00</published><updated>2009-03-21T19:30:39.151+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Beautiful code</title><content type='html'>&lt;a href="http://people.iola.dk/arj/"&gt;Anders&lt;/a&gt; pointed me to a &lt;a href="http://rubyconf2007.confreaks.com/d1t1p1_what_makes_code_beautiful.html"&gt;talk by some Ruby guy&lt;/a&gt; (Marcel Molina) speaking about beautiful code. I must admit that I was a bit sceptical, but Anders was very convincing.&lt;br /&gt;&lt;br /&gt;The verdict?&lt;br /&gt;&lt;br /&gt;When it comes to ruminating on software design, I think there's a big nasty trap which he unfortunately walked straight into (as foreseen) with few extenuating circumstances (not expected).&lt;br /&gt;&lt;br /&gt;The problem is that the field is hidden in a fog of mysteries, buried in what's governed by intuition and tacit knowledge rather than explainable ration. Good programming, hah, that's an art, nobody can tell you how to do that!&lt;br /&gt;&lt;br /&gt;What we need here is to be able to talk about the thing. More ration, less intuition. Trying to explain things as beautiful or not is a step in the wrong direction. It's romantic self-indulgence, like when you look at people younger than you and think, people these days... An operational set of values for evaluating code is an essential thing for an aspiring programmer. How can beauty be operational if you don't even know how to argue about it with a fellow programmer?&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.yayart.com/shop/875/"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 380px; height: 380px;" src="http://1.bp.blogspot.com/_PIM6r0YLhk4/ScP9Eeh-fpI/AAAAAAAAAGU/7KFbnA1Li60/s400/majestic-yayart.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5315370238584192658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Beauty? Digital graphic art from YayArt.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In any case, I think his main point can be summed up to: ensure that the code is as small as possible, as clear as possible and does what it's supposed to do. As I mentioned last time I wrote about &lt;a href="http://ole-laursen.blogspot.com/2007/06/software-design-part-i.html"&gt;software design&lt;/a&gt;, I think this can be simplified to make it &lt;span style="font-style: italic;"&gt;easy to understand&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Save the interesting but tricky beauty discussions for things like &lt;a href="http://thefairest.info/top.html"&gt;this&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6948953547903565656?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6948953547903565656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/beautiful-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6948953547903565656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6948953547903565656'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/beautiful-code.html' title='Beautiful code'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_PIM6r0YLhk4/ScP9Eeh-fpI/AAAAAAAAAGU/7KFbnA1Li60/s72-c/majestic-yayart.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7049518355177096769</id><published>2009-03-14T12:52:00.007+01:00</published><updated>2009-11-20T18:03:32.004+01:00</updated><title type='text'>The repair shop</title><content type='html'>One of the annoying things about modern home electronics is that they are black boxes. Like a microwaver, cold food in, push buttons, warm food out. Who knows what happened inside?&lt;br /&gt;&lt;br /&gt;Most people are probably happy they don't know. But a life without curiosity is a life with less passion. I was fortunate enough to get an excuse to take apart two home appliances recently because they broke. And it wasn't even my fault.&lt;br /&gt;&lt;br /&gt;The first successful repair was Janne's younger sister's laptop. The power chord had been loose for some time, at some point the laptop simply stopped working with symptoms of no power.&lt;br /&gt;&lt;br /&gt;The anatomy of the repair is not too far from debugging software&lt;a href="http://ole-laursen.blogspot.com/2007/11/debugging.html"&gt;&lt;/a&gt; you don't know (I've previously talked about &lt;a href="http://ole-laursen.blogspot.com/2007/11/debugging.html"&gt;debugging software&lt;/a&gt; you've written yourself). Take the system apart, examine the individual components, collect information, reason. Identify the faulty component and apply the easiest fix you can think of.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbuePPGdOMI/AAAAAAAAAFs/ziN8nnOnBgE/s1600-h/reparation-af-baerbar.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 269px;" src="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbuePPGdOMI/AAAAAAAAAFs/ziN8nnOnBgE/s320/reparation-af-baerbar.jpg" alt="" id="BLOGGER_PHOTO_ID_5313014170002471106" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Taking apart the laptop&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_PIM6r0YLhk4/SbuePLJi_DI/AAAAAAAAAF0/o4c-kbIJh_I/s1600-h/reparation-af-baerbar2.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 239px;" src="http://4.bp.blogspot.com/_PIM6r0YLhk4/SbuePLJi_DI/AAAAAAAAAF0/o4c-kbIJh_I/s320/reparation-af-baerbar2.jpg" alt="" id="BLOGGER_PHOTO_ID_5313014168941689906" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The faulty component, the switchboard that the power chord plugs into (visible at the finger tip)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I had an edge here. I'd heard about this problem before. So instead of giving up beforehand, it got me thinking that if it was a common problem, the remedy would probably be well-known among electro-hobbyists. A bit of googling revealed that some people had success with soldering off the small house the power jack is inserted into, and replacing it. When we examined the house, something was in fact wrong with it, as witnessed by a simple currency test with a multimeter.&lt;br /&gt;&lt;br /&gt;So we set out to solder it off. Unfortunately, that didn't work out. The soldering metal wouldn't melt properly. Instead we ended up replacing the whole component, i.e. the small part of the mother board that the ports were sitting on. The web is extremely handy here. Just jot down the spare part number and search for it, or parts of it.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_PIM6r0YLhk4/SbuePm9tSRI/AAAAAAAAAF8/BW4KdxuI_Pg/s1600-h/research.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 239px;" src="http://1.bp.blogspot.com/_PIM6r0YLhk4/SbuePm9tSRI/AAAAAAAAAF8/BW4KdxuI_Pg/s320/research.jpg" alt="" id="BLOGGER_PHOTO_ID_5313014176408226066" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We ordered a spare part which ended up costing less than 1/10 of the price of a new laptop, and put it in.&lt;br /&gt;&lt;br /&gt;In reality, this was a bit harder than it sounds, because the first two places that turned up on the web didn't actually have the part when we tried ordering it. Also because taking apart a laptop is a bit complicated because of all the tiny screws and chords and plastics that have to be bent in awkward positions, sometimes more violently than you'd like to think about.&lt;br /&gt;&lt;br /&gt;Last week, I had a much nicer experience changing the BIOS battery on my trusty old Pentium III laptop. Ugly looks, but nice internals.&lt;br /&gt;&lt;br /&gt;Continuing on this saga, I've also fixed our microwave oven. Microwave ovens are a bit more complicated in the feature set than I hinted above. They also make the food turn around, slowly.  But our oven stopped doing that. So I took it apart, hoping that it would be a bad connection.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbumA5tShnI/AAAAAAAAAGE/r4RhjyI86HI/s1600-h/reparation-af-mikro.jpg"&gt;&lt;img style="cursor: pointer; width: 206px; height: 320px;" src="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbumA5tShnI/AAAAAAAAAGE/r4RhjyI86HI/s320/reparation-af-mikro.jpg" alt="" id="BLOGGER_PHOTO_ID_5313022719834621554" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;One non-rotating Samsung microwave oven&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_PIM6r0YLhk4/SbumA6zfUwI/AAAAAAAAAGM/28onFnsbTT0/s1600-h/reparation-af-mikro2.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 239px;" src="http://4.bp.blogspot.com/_PIM6r0YLhk4/SbumA6zfUwI/AAAAAAAAAGM/28onFnsbTT0/s320/reparation-af-mikro2.jpg" alt="" id="BLOGGER_PHOTO_ID_5313022720129061634" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The faulty component in the microwaver&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;However, there was nothing wrong with the connections inside the oven. The funny thing about hardware is that as soon as you take off the shell, it looks complicated and futuristic, but in reality it's just a set of interconnected smaller components. In this case, the sealed turntable motor was broken. Again, with the component number it wasn't hard to find a spare part on the web.&lt;br /&gt;&lt;br /&gt;However, this presented me with an interesting real-life dilemma. Is it worth 22 £ to me to be able to see the food inside the oven carouseling past? My first answer was no. I already got to see the oven inside, it's actually pretty simple, and identify the problem. I didn't have to actually &lt;span style="font-style: italic;"&gt;fix&lt;/span&gt; it.&lt;br /&gt;&lt;br /&gt;A couple of weeks later, the oven fried a very useful corn bag we're using to loosen stiffened neck muscles, a useful cure for some kinds of head-aches. The oven had burnt a hole at one particular spot in the non-rotating bag.&lt;br /&gt;&lt;br /&gt;So today I installed the spare part. Works like a charm.&lt;br /&gt;&lt;br /&gt;This may sound silly, but fixing supposedly unfixable things is really rewarding. You feel powerful and virtuous.&lt;br /&gt;&lt;br /&gt;A couple of extra garden pictures:&lt;br /&gt;&lt;br /&gt;&lt;a style="font-weight: bold;" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/SbueO595sAI/AAAAAAAAAFc/QXe5vSKPNrk/s1600-h/foraar1.jpg"&gt;&lt;img style="cursor: pointer; width: 239px; height: 320px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/SbueO595sAI/AAAAAAAAAFc/QXe5vSKPNrk/s320/foraar1.jpg" alt="" id="BLOGGER_PHOTO_ID_5313014164329443330" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Enjoying the spring sun at noon&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/SbueOwYh-PI/AAAAAAAAAFk/4L6oW3q5uC0/s1600-h/foraar2.jpg"&gt;&lt;img style="cursor: pointer; width: 320px; height: 239px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/SbueOwYh-PI/AAAAAAAAAFk/4L6oW3q5uC0/s320/foraar2.jpg" alt="" id="BLOGGER_PHOTO_ID_5313014161756780786" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Next day it's snowing&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7049518355177096769?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7049518355177096769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/repair-shop.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7049518355177096769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7049518355177096769'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/repair-shop.html' title='The repair shop'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_PIM6r0YLhk4/SbuePPGdOMI/AAAAAAAAAFs/ziN8nnOnBgE/s72-c/reparation-af-baerbar.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4965447781392923539</id><published>2009-03-10T19:21:00.013+01:00</published><updated>2009-03-10T20:58:24.555+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plants'/><title type='text'>Spring!</title><content type='html'>Spring is upon us here in Denmark! The signs are subtle but sure.&lt;br /&gt;&lt;br /&gt;My snowdrops are flowering.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/Sba-nsGjAFI/AAAAAAAAAEc/3fzuTU_1Iig/s1600-h/DSCN5435.JPG"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/Sba-nsGjAFI/AAAAAAAAAEc/3fzuTU_1Iig/s320/DSCN5435.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5311642399592874066" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_PIM6r0YLhk4/Sba_i2B5y2I/AAAAAAAAAEk/KGlhlAfSCbQ/s1600-h/DSCN5447.JPG"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://1.bp.blogspot.com/_PIM6r0YLhk4/Sba_i2B5y2I/AAAAAAAAAEk/KGlhlAfSCbQ/s320/DSCN5447.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5311643415870032738" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_PIM6r0YLhk4/Sba_jCgBQMI/AAAAAAAAAEs/dSCJpBCrz8w/s1600-h/DSCN5466.JPG"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://2.bp.blogspot.com/_PIM6r0YLhk4/Sba_jCgBQMI/AAAAAAAAAEs/dSCJpBCrz8w/s320/DSCN5466.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5311643419217576130" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;Galanthus, snowdrops&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;The garden looks as if it is almost dead, but upon closer inspection small green leaves are shooting up. And the ground is soft and wet, not hard and frosty.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbbAdgA4ObI/AAAAAAAAAE0/rqyYziJ24A8/s1600-h/DSCN5438.JPG"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 239px; height: 320px;" src="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbbAdgA4ObI/AAAAAAAAAE0/rqyYziJ24A8/s320/DSCN5438.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5311644423572437426" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;My mostly dead, but not quite!, garden&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;It's getting warmer, my trusty winter coat and wide scarf is getting too warm for driving up the hill on bike.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbbBrGzK6qI/AAAAAAAAAE8/yDKOMaYnWa0/s1600-h/DSCN5236.JPG"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://3.bp.blogspot.com/_PIM6r0YLhk4/SbbBrGzK6qI/AAAAAAAAAE8/yDKOMaYnWa0/s320/DSCN5236.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5311645756833852066" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;Me and a tame deer in Arden in December&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Yesterday a pair of ducks had occupied the pond in the park. This morning two coots were grassing when I passed downhill. And in the evening blackbirds are singing.&lt;br /&gt;&lt;br /&gt;Yay!&lt;br /&gt;&lt;br /&gt;PS: If you're quick, there's still time for our special offer for &lt;a href="http://www.yayart.com/"&gt;new art on paper and canvas on YayArt&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4965447781392923539?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4965447781392923539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/spring.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4965447781392923539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4965447781392923539'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/spring.html' title='Spring!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_PIM6r0YLhk4/Sba-nsGjAFI/AAAAAAAAAEc/3fzuTU_1Iig/s72-c/DSCN5435.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-425946984676746609</id><published>2009-03-08T12:58:00.006+01:00</published><updated>2009-03-08T14:49:32.100+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>Ideology and pragmatism</title><content type='html'>Perhaps I failed to mention the most important point in the thing I posted &lt;a href="http://ole-laursen.blogspot.com/2009/01/market-economy.html"&gt;about capitalism&lt;/a&gt; the other day. Actually, I've been thinking about analogies between distributed software architecture (big systems) and society (a big system) for long, but I regret having spent most of the post about it because that kind of ego-centered pattern-matching is usually in itself pretty tiresome to read about.&lt;br /&gt;&lt;br /&gt;A more interesting point is that of ideology versus pragmatism.&lt;br /&gt;&lt;br /&gt;Interestingly, there's a similarity to them. When you believe in an ideology, you'll defend your choices with the end justifying the means. We may leave the poor bastards in the mud or we may kill half a million Iraqis or we may suppress people who think different from us and destroy the historical monuments of our past, but in the end we're defending freedom or equality and that's more important than anything else. It will prevail, and everything will be better than it once were.&lt;br /&gt;&lt;br /&gt;When you believe in pragmatism, you'll defend your choices with the end justifying the means. We may spend ten times as much fixing the minds of violent criminals as the traumatized minds of their victims, we may have to see half of our help to poor nations disappearing in corruption, we may have to accept that people with big salaries get the lion's share of a tax cut. But in the end, we can prove that everyone is generally better off than with the alternative.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.flickr.com/photos/cinnajess/2737850896/"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 500px; height: 375px;" src="http://farm4.static.flickr.com/3019/2737850896_31ea0f4026.jpg?v=0" border="0" alt="" /&gt;&lt;/a&gt;&lt;i&gt;Religious pragmatism (from Flickr)&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Pragmatism is discomforting. A true pragmatist will defend actions that are against the values that person believes in. Of course, ideology is discomforting too, as it requires you to suppress the truth.&lt;br /&gt;&lt;br /&gt;This is all fine in theory. Most people will probably agree that we should base political decisions on ration and facts rather than beliefs.&lt;br /&gt;&lt;br /&gt;However, in practice nobody knows for sure what the future outcome of a decision is. This is worse in some fields than in others. There's also the aside that without strong personal beliefs, lots of the valuable work being done in this world would probably cease to happen. Ideologically founded people will put lots of energy to an unselfish end in satisfying their drive.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.flickr.com/photos/27729739@N05/2590420884/"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 500px; height: 375px;" src="http://farm4.static.flickr.com/3293/2590420884_960b3755b4.jpg?v=0" border="0" alt="Windmill" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'm personally ideological when it comes to environmental issues. I would like to think, self-indulgently, that it's because I'm thinking further ahead than people in general but the truth is that I don't know why. As the windmill production in Denmark has grown to an international leadership, this has become more of a pragmatic decision. To my dismay, the right-wing in Denmark then blocked the whole windmill programme for most of the 00s out of what I see as ideological issues (money rests better in people's own pockets).&lt;br /&gt;&lt;br /&gt;However, in other areas I find it easier to be pragmatic. Dismissal of known facts is something not to be taken lightly. This is especially true when it comes to issues where the ideological argument is persuit of justice or equality. The world is not just (sometimes children are molested), and we're not born equal, and although there's nothing wrong with building a system that tries to ameliorate these things, it's important to keep focus on reality, not the ideas.&lt;br /&gt;&lt;br /&gt;Actually, that leads me software design again, but I think I'll save that for another blog post. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-425946984676746609?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/425946984676746609/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/ideology-and-pragmatism.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/425946984676746609'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/425946984676746609'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/03/ideology-and-pragmatism.html' title='Ideology and pragmatism'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7142108143459043502</id><published>2009-01-27T17:49:00.006+01:00</published><updated>2009-01-27T19:51:08.755+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Caching on the web</title><content type='html'>When you build a web application intended for a large scale, there are two separate performance issues when it comes to generating the HTML the server spits out.&lt;br /&gt;&lt;br /&gt;One is server load. How many visitors can the server cope with?&lt;br /&gt;&lt;br /&gt;The other thing is response time as seen by each visitor. How long must I wait for the page to load?&lt;br /&gt;&lt;br /&gt;When using a relatively slow dynamic web framework (as Python + Django), the issue with server load is mostly related to CPU time. If you read the Django docs, the recommended solution is caching, i.e. instead of generating the HTML on each hit, we generate it once, store it for a while, handing out the stored snippet until it times out. It's a simple idea that works incredibly well.&lt;br /&gt;&lt;br /&gt;First question is where we do the caching. To reduce the workload as much as possible, we want it to happen as close to the browser as possible. For a low-cost solution that works for everyone, we need to stick to the same physical location as the web server, however.&lt;br /&gt;&lt;br /&gt;With Django on Apache, you've got some relatively fast C code serializing the requests to separate Python processes running the Django code. The process model limits the concurrency level possible because each process gobs a fair amount of memory, and Python is just generally an order of magnitude slower than C. So while putting the cache in the web framework is arguably very convenient, it's also pretty slow. From my experiments, one or two orders of magnitude.&lt;br /&gt;&lt;br /&gt;Ideally, we'd just run a simple process written with speed in mind in front of Apache to do the caching. Afterall, the idea is relatively straightforward. Enter &lt;a href="http://varnish.projects.linpro.no/"&gt;Varnish&lt;/a&gt;, a web cache written in C.&lt;br /&gt;&lt;br /&gt;With Varnish in place, you know that the server only has to generate a page once per timeout. Some of the pages on &lt;a href="http://www.yayart.com/"&gt;YayArt&lt;/a&gt; take about 1 second to generate because they need to do multiple complex database queries. Let's say we have a beefy server that can do 10 of these in parallel. Then we could support 10 requests/s. With caching in Varnish, we can scale up to whatever it takes Varnish to retrieve a string from its cache and serve it. You can easily reach 6000 requests/s. For comparison, 100 requests/s is 8.6 million hits/day.&lt;br /&gt;&lt;br /&gt;However, as it turns out, while simple whole page caching can solve a large part of the server load problem with one blow without giving up the convenience of a dynamic web framework, it's not necessarily the solution to the response time. If the page is in the cache, the response time is close to perfect. But what if it's a slow day with few visitors and it's not?&lt;br /&gt;&lt;br /&gt;The general solution is to precompute the answer. Unfortunately, how to do this is more application specific.&lt;br /&gt;&lt;br /&gt;You might be wondering how to combine caching with personalized pages. On YayArt I use Javascript to integrate the per-user data into the page through AJAX. So the per-user server load is reduced to processing the actual per-user data instead of a whole page.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7142108143459043502?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7142108143459043502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/01/caching-on-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7142108143459043502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7142108143459043502'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/01/caching-on-web.html' title='Caching on the web'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8394459697473834669</id><published>2009-01-25T11:02:00.003+01:00</published><updated>2009-01-27T17:48:48.673+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>Market economy</title><content type='html'>For Christmas, my parents bought me 3 months of subscription to &lt;a href="http://www.information.dk"&gt;Information&lt;/a&gt;, a small Danish newspaper with a focus on analysis and criticism of the daily politics. The audience is mostly intellectual (and also mostly left-wing).&lt;br /&gt;&lt;br /&gt;Information is interesting for several reasons, one of them being that major politicians in Denmark, the people who actually make the rules for the rest of us, regularly post in the newspaper, and thus presumably also read it. For an old internet-addict, that's a whole new experience.&lt;br /&gt;&lt;br /&gt;There's been a lot of talk lately about capitalism in the face of the current financial crisis. The thing is that over the past 20 years, almost everyone in the political landscape has been pursuing a the-more-market-economy-and-deregulation-the-better strategy to a some degree. And now it turns out that too few rules for the gamblers in the financial sector have put us into a global recession. So people are, again, beginning to question whether capitalism is such a hot idea after all.&lt;br /&gt;&lt;br /&gt;When I went to high school, I always thought it was possible to do better. Because there is such an obvious waste in our market economy. For an example, go to your nearest supermarket and look at the shelves with shampoo and hair products. Or the shelves with soft drinks. Or the shelves with morning cereals for children. And ask yourself, how much value does all these colourful and overly expensive things bring to our society? Or the really classy ads that never mention any factual qualities in their products, but instead try to install in us an irrational idea of their products, because of the packaging so to speak, bringing improvements to our lives.&lt;br /&gt;&lt;br /&gt;I ask you, how can drinking water with added sugar and various brown chemicals make you cool? Because a large company has invested enormous efforts in convincing everyone that it is so.&lt;br /&gt;&lt;br /&gt;Surely ration can do better than that.&lt;br /&gt;&lt;br /&gt;However, at university I spent a couple of years working with distributed systems. The most important lesson I learned is that centralized systems are bad, unless the scale is very small. Decentralized (peer-to-peer) systems are more adaptive to change, more robust and much more efficient - &lt;em&gt;several orders of magnitude as the system scales&lt;/em&gt;. Yes, there's waste, redundancy, suboptimal behaviour.&lt;br /&gt;&lt;br /&gt;But it's self-organizing. If I want something from a peer, I just ask - I don't need to contact a central authority which then has to decide how to respond to the change. Overall it just works incredibly better.&lt;br /&gt;&lt;br /&gt;I think that the same is true of society. With a market economy, decision-making &lt;em&gt;is&lt;/em&gt; decentralized, in spite of the tendency for old industries to advance towards monopolies. In the large scale, this decentralization is unbelievably effective compared to a centralized control because of the complexity involved.&lt;br /&gt;&lt;br /&gt;It's also a lot more free than a democracy. In a democracy, 55% can decide for the remaining 45% (that's why we haven't built any new windmills in Denmark lately). With a market economy, everyone decides for himself. Within the limits of the market, of course.&lt;br /&gt;&lt;br /&gt;However, not all is good. One of the more peculiar problems of market economy is the drive towards monopolies, i.e. a collapse of the market (read a treatise of Karl Marx's works to understand why). Like any game we set up, it needs rules.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://upload.wikimedia.org/wikipedia/commons/f/fc/Karl_Marx.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 480px; height: 563px;" src="http://upload.wikimedia.org/wikipedia/commons/f/fc/Karl_Marx.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;i&gt;Karl Marx&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;And the market cannot do long-term thinking. As we have just seen, it can happily drive over the cliff edge because of a phenomenon called &lt;a href="http://en.wikipedia.org/wiki/Tragedy_of_the_commons"&gt;the tragedy of the commons&lt;/a&gt;. It doesn't hurt me a lot that I exploit the system: I get the whole benefit, the downside is shared between everyone.&lt;br /&gt;&lt;br /&gt;The tragedy is that even if some market players want to stop, they face the competition from the others. If price is the only factor, a short-term thinking competitor can drive the others off the market. So they are forced to follow, unless a force beyond the market sets down rules that cannot be ignored. The same reasoning goes for unethical behaviour.&lt;br /&gt;&lt;br /&gt;So the issue here is how we change the rules of the game to ameliorate the bad things without throwing the basic idea out of the window. For in spite of the waste, it's working better than the alternatives. Ration is bounded, it's not enough to deal satisfactorily with the needs of millions of humans.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8394459697473834669?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8394459697473834669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/01/market-economy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8394459697473834669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8394459697473834669'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/01/market-economy.html' title='Market economy'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4516683641504350896</id><published>2009-01-23T19:00:00.008+01:00</published><updated>2009-01-23T19:29:18.326+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Converting MySQL to UTF-8 the easy way</title><content type='html'>This is just a quick note to myself.&lt;br /&gt;&lt;br /&gt;So you're running on the latest version of INSERT NAME OF FANCY WEB FRAMEWORK HERE? Think character set problems are a relic of the past? Not so with MySQL. The default configuration is using Latin-1. When you install MySQL, the first you should do is ensure MySQL is using UTF-8. This problem will go away at some point when distributions change the defaults, but until then.&lt;br /&gt;&lt;br /&gt;Meanwhile, if you're like me, you might have created some tables before discovering the problem. It is, after all, difficult to see before you create the tables. So here's a recipe for converting a whole database (&lt;a href="http://codex.wordpress.org/Converting_Database_Character_Sets"&gt;idea stolen from Wordpress&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;First type in these commands, replacing &lt;i&gt;mydb&lt;/i&gt; with the name of your database:&lt;br /&gt;&lt;code style="font-size: smaller"&gt;&lt;br /&gt;USE information_schema;  &lt;br /&gt;SELECT CONCAT('ALTER TABLE ', table_name, ' MODIFY ', column_name, ' ', column_type, ' CHARACTER SET utf8;') FROM columns WHERE table_schema = 'mydb' and data_type LIKE '%char%';  &lt;br /&gt;SELECT CONCAT('ALTER TABLE ', table_name, ' MODIFY ', column_name, ' ', column_type, ' CHARACTER SET utf8;') FROM columns WHERE table_schema = 'mydb' and data_type LIKE '%text%';&lt;br /&gt;SELECT CONCAT('ALTER TABLE ', table_name, ' CHARACTER SET utf8;') FROM tables WHERE table_schema = 'mydb';&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This should output the commands you need to feed into MySQL to do the change. If you start the MySQL shell with &lt;code&gt;-s&lt;/code&gt;, it's easier to copy-paste. Then type&lt;br /&gt;&lt;code style="font-size: smaller"&gt;&lt;br /&gt;USE mydb;&lt;br /&gt;ALTER DATABASE mydb CHARACTER SET utf8;&lt;br /&gt;[... pasted commands ...]&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The problem here is that the character set is stored on multiple levels. Both column, table and database level. The second line fixes the character set of the database, and the pasted in commands fixes the columns and tables.&lt;br /&gt;&lt;br /&gt;This works for Django. If you have used a framework that allows you to put UTF-8 characters into the Latin-1 columns, you need to do something else. The &lt;a href="http://codex.wordpress.org/Converting_Database_Character_Sets"&gt;Wordpress link&lt;/a&gt; has the details.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4516683641504350896?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4516683641504350896/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2009/01/converting-mysql-database-to-utf-8.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4516683641504350896'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4516683641504350896'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2009/01/converting-mysql-database-to-utf-8.html' title='Converting MySQL to UTF-8 the easy way'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8483385488643162560</id><published>2008-10-30T13:28:00.006+01:00</published><updated>2008-10-30T15:58:42.617+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Moving web servers without downtime</title><content type='html'>In IOLA, we've been moving some of the sites we're hosting, most notably iola.dk with associated services.&lt;br /&gt;&lt;br /&gt;The problem with moving web services is DNS. DNS is a silly protocol - it uses a hierarchy to scale, which perhaps makes sense admin-wise (don't forget that scaling means more than just performance, administration is usually at least as important given the cost of man hours). But a hierarchy is a disaster when it comes to performance since the root nodes quickly end up being swamped - think 100 million clients all querying .com domains simultanously. DNS uses caching extensively to alleviate this problem.&lt;br /&gt;&lt;br /&gt;And this is the problem starts. There is as far as I know no way to clear the cache. So with a typical DNS entry with maybe a time to live of 12 hours, you have a pretty long period in which some clients may see the old IP address and some clients are seeing the new.&lt;br /&gt;&lt;br /&gt;In some cases, it's not a problem to have both web servers running at the same time. But if a site is modifying the database, it's more difficult as you don't want to have two inconsistent databases.&lt;br /&gt;&lt;br /&gt;There are several ways to fix this, including staying up late at night until all your visitors have gone to bed, but I'll show a neat trick which is really easy to pull off and which shouldn't cause any sweat.&lt;br /&gt;&lt;br /&gt;Start by installing Varnish, a reverse proxy server, on the old server. If you are on Debian, it's &lt;code&gt;aptitude install varnish&lt;/code&gt; and you then get a &lt;code&gt;/etc/default/varnish&lt;/code&gt; file. In there are a couple of lines like:&lt;br /&gt;&lt;pre&gt;DAEMON_OPTS="-a :6081 \&lt;br /&gt;             -T localhost:6082 \&lt;br /&gt;             -b localhost:8080 \&lt;br /&gt;             -u varnish -g varnish \&lt;br /&gt;             -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G"&lt;/pre&gt;&lt;br /&gt;The first line means that Varnish is listening on port 6081 and the third line means that it is forwarding everything it sees to localhost port 8080. So change that third line to &lt;code&gt;-b 123.456.789.012:80&lt;/code&gt; or whatever the IP address of your shiny new server, execute &lt;code&gt;/etc/init.d/varnish restart&lt;/code&gt; and go test it by accessing your old host on port 6081, e.g. &lt;code&gt;http://www.example.com:6081&lt;/code&gt;. It should return the page from your new server - you can verify it by looking in the log on the new server.&lt;br /&gt;&lt;br /&gt;What happens here is that Varnish parses the request from your browser, forwards it to the backend (your web server on the new server) and caches and returns the web server response to the browser. It's of course a pretty dumb idea to fetch the content over the web twice, from the new server to the old server and then from the old server to the web browser, but we won't be doing it for long. And Varnish will cache as much as its default configuration allows for.&lt;br /&gt;&lt;br /&gt;Then when you think you're ready, change the first line above so that Varnish is listening on port 80 instead:&lt;br /&gt;&lt;pre&gt;DAEMON_OPTS="-a :80 \&lt;br /&gt;             -T localhost:6082 \&lt;br /&gt;             -b 123.456.789.012:80 \&lt;br /&gt;             -u varnish -g varnish \&lt;br /&gt;             -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G"&lt;/pre&gt;&lt;br /&gt;Then stop your web server, e.g. with &lt;code&gt;/etc/init.d/apache2 stop&lt;/code&gt; and restart varnish with &lt;code&gt;/etc/init.d/varnish restart&lt;/code&gt;. This puts Varnish in the place where the old web server was before. So from now on, there's only one web server seing the requests, the one on the new server. So you can switch over DNS from the old IP address to the new address, and when the last DNS entries have expired, you won't be getting hits anymore on Varnish on the old server.&lt;br /&gt;&lt;br /&gt;Varnish can process requests based on URL and host so if you're serving multiple hosts from the same server, it's possible to forward only some request to the new server. It's pretty easy to do, you just need to enable a configuration file and put the stuff in there. In Debian there's an example configuration in /etc/varnish/default.vcl. As long as Varnish is not listening on port 80, it's pretty easy to experiment with it.&lt;br /&gt;&lt;br /&gt;While you're at it you might want to start with putting Varnish on the new server in front of your ordinary web server. If there's something Varnish can cache, pages that look the same for several clients, it can save some serious load.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8483385488643162560?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8483385488643162560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/10/moving-web-servers-without-downtime.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8483385488643162560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8483385488643162560'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/10/moving-web-servers-without-downtime.html' title='Moving web servers without downtime'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7967288530714270963</id><published>2008-10-04T17:00:00.008+02:00</published><updated>2008-10-04T18:28:21.909+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Losing sight of the big picture</title><content type='html'>I just had the most wonderful session with my piano. I haven't been practicing much lately, if at all, so I was mostly playing through old faster pieces to get my fingers up to speed.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.freehandmusic.com/I/covers/asp/__MOZART.gif"&gt;&lt;img style="display:block; cursor:pointer; cursor:hand;width: 200px;" src="http://www.freehandmusic.com/I/covers/asp/__MOZART.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;i&gt;Herr Mozart&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;A funny thing happened. As I'm playing the first movement of a sonata by Mozart, I suddenly realize that I'm struggling with the phrasing, articulating the bold and the faint passages clearly. This piece has lots of &lt;i&gt;f&lt;/i&gt; and &lt;i&gt;p&lt;/i&gt; markings (forte and piano, strong and quiet). I'm not too happy with the sound either. So I start focusing on getting it more precise, only to discover that the extra precision makes the sound worse.&lt;br /&gt;&lt;br /&gt;The attention to detail draws attention away from the big picture, which in this case is the melody itself. Some pieces are simple, the music is in the tones. I think this is what most people relate to when they think of a melody. But this doesn't hold for most of the classical music I've heard and played. The tones are still important, but usually the overall dynamics are what will carry people away. Think of a symphony, an enticing crescendo towards the thundering finale, like &lt;a href="http://www.youtube.com/watch?v=ENl4JK6LJ0Y"&gt;the theme from Space Odyssey 2001&lt;/a&gt; (it's from "Also sprach Zarathustra"). If it can't erect the small hairs at the back of your head, you need a treatment with a five-pounder shovel.&lt;br /&gt;&lt;br /&gt;So by focusing on the details, which at most can make the music more interesting, although if you haven't played the piano you probably won't notice 90% of it, one can happily leave the most important of the piece to the coincidence.&lt;br /&gt;&lt;br /&gt;It didn't work for me. So I gave up the details and started listening for the bigger picture, and voila! Pure joy. For five minutes, I was completely carried away.&lt;br /&gt;&lt;br /&gt;The interesting thing is that the exercise turned out to be surprisingly harder than you'd think because all those little &lt;i&gt;f&lt;/i&gt;s and &lt;i&gt;p&lt;/i&gt;s kept breaking my concentration. I know this happens to professional players too, because I've experienced it at least once with a brilliant player, playing technically well and with lots of dynamics - but with no overall structure to push my emotions making the whole thing plain boring.&lt;br /&gt;&lt;br /&gt;There's a long and interesting discourse into the land of interpretation of music here. But let me instead draw a parallel to software engineering.&lt;br /&gt;&lt;br /&gt;For both piano playing and software engineering, often all we got is a clearly incomplete specification of how the thing is going to work. But if you (just) follow the spec, the result is guaranteed to be mediocre. And the more detailed the spec is, the more it'll distract you from the overall picture which is usually where the fate, success or failure, is determined.&lt;br /&gt;&lt;br /&gt;Now witness Wilhelm Kempff, a legend whose playing to me represents the opposite of the brilliant player mentioned above. He may make a couple of mistakes and he obviously bends the spec into what he sees fit. But I'm carried away.&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/oqSulR9Fymg&amp;hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/oqSulR9Fymg&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7967288530714270963?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7967288530714270963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/10/losing-sight-of-big-picture.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7967288530714270963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7967288530714270963'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/10/losing-sight-of-big-picture.html' title='Losing sight of the big picture'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3376289913990716768</id><published>2008-09-25T12:58:00.003+02:00</published><updated>2009-10-23T16:09:00.523+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flot 0.5 released!</title><content type='html'>New version of &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt; is out. See the &lt;a href="http://flot.googlecode.com/svn/trunk/NEWS.txt"&gt;changes&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_PIM6r0YLhk4/SNtwt33QAII/AAAAAAAAACo/4JE4XW_pTeE/s1600-h/screenshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_PIM6r0YLhk4/SNtwt33QAII/AAAAAAAAACo/4JE4XW_pTeE/s400/screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5249913724022554754" /&gt;&lt;/a&gt;&lt;br /&gt;The big news is support for mouse tracking and highlighting points. So you can make the plots much more interactive. Also included is support for dual axes and improved background markings, and a long list of bug fixes, the most noticeable probably being that timestamps are now interpreted according to UTC instead of the local time zone of the client.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3376289913990716768?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3376289913990716768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/flot-05-released.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3376289913990716768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3376289913990716768'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/flot-05-released.html' title='Flot 0.5 released!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_PIM6r0YLhk4/SNtwt33QAII/AAAAAAAAACo/4JE4XW_pTeE/s72-c/screenshot.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3211365646048565580</id><published>2008-09-20T20:35:00.002+02:00</published><updated>2008-09-20T20:40:28.889+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Consensus decision making</title><content type='html'>Is that crazy? &lt;a href="http://home.pacbell.net/ouster/decisions.html"&gt;Maybe not&lt;/a&gt;. Is a good manager the person who makes the decisions or the person who makes sure he doesn't?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3211365646048565580?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3211365646048565580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/consensus-decision-making.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3211365646048565580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3211365646048565580'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/consensus-decision-making.html' title='Consensus decision making'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2429899024352017882</id><published>2008-09-20T18:48:00.003+02:00</published><updated>2009-04-17T17:28:55.640+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The abysmal state of web layout technology - or not?</title><content type='html'>This post is about CSS and HTML.&lt;br /&gt;&lt;br /&gt;Coming from a desktop application programming background, the web platform is actually pretty flexible and concise. Customized layouts can be done with remarkably simple markup and a bit of separate style information.&lt;br /&gt;&lt;br /&gt;The big problem with the web is that HTML and CSS is designed for pages with a single stream of information, like a manual or a scientific article about an experiment in physics. Or a Wikipedia article. To layout another stream of information, like say something as exotic as a navigation bar, you basically have to use a hack which requires you to understand some pretty complicated concepts about HTML box layout or turn the markup into an unmanageable table hell.&lt;br /&gt;&lt;br /&gt;I've been integrating a design for an internal web application for a customer the last couple of days. The thing about CSS is that apart from the problem with multiple streams of information, it's usually quite straightforward to work with. As long as you stick to the HTML mindset of how the world looks like.&lt;br /&gt;&lt;br /&gt;The basic building block in this design is boxes (fine, HTML layout is about boxes) with shadows (urgh, no support for that) and rounded edges (double urgh, no support for that either) and shiny buttons (no built-in support for shininess). Clean markup out of the window, table soup and images to the rescue. Matters is made worse by unstable rendering by a certain browser which is a disaster for a box where the visual effect depends of getting the edges exactly right.&lt;br /&gt;&lt;br /&gt;The most annoying thing about this is perhaps that if just someone had put multiple background image support into CSS, then I could have my clean markup back and get over this in no time. I was thinking about this earlier today, and even got so far as to have a simple draft proposal for fixing this case. I was determined to submit it to whoever is in charge of CSS and push for its inclusion.&lt;br /&gt;&lt;br /&gt;But upon investigation, it turns out that a solution has already been proposed and written into the CSS draft specification years ago as &lt;code&gt;border-image&lt;/code&gt;. There's a nice overview on &lt;a href="http://www.css3.info/"&gt;css3.info&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Apparently, the situation with respect to adoption has been dead-locked for a long time. The spec can't be made final until at least two different renderings engines have implemented it. And the rendering engines can't implement them for real before the spec is final. So it's going to take some determination to get going with proprietary attributes to begin with. Luckily it appears that 2008 is the year that the push is finally happening.&lt;br /&gt;&lt;br /&gt;So there you are. It will change someday. I think this is typical of the web. There's some really good stuff in it, and a whole slew of annoyances. But they're going away, slowly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2429899024352017882?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2429899024352017882/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/abysmal-state-of-web-layout-technology.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2429899024352017882'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2429899024352017882'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/abysmal-state-of-web-layout-technology.html' title='The abysmal state of web layout technology - or not?'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-806460413081753143</id><published>2008-09-19T11:18:00.005+02:00</published><updated>2008-09-20T18:51:07.560+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Listening to music and last.fm</title><content type='html'>Some time ago &lt;a href="http://people.iola.dk/arj/"&gt;Anders&lt;/a&gt; created a &lt;a href="http://last.fm/"&gt;last.fm&lt;/a&gt; group for &lt;a href="http://www.yayart.com/"&gt;YayArt&lt;/a&gt;. Anders showed me how &lt;a href="http://www.gnome.org/projects/rhythmbox/"&gt;Rhythmbox&lt;/a&gt; can integrate with last.fm, reporting in what I'm listening to. So I ended up creating a profile.&lt;br /&gt;&lt;br /&gt;At first I find last.fm pretty confusing and leave it at that. A couple of days pass where I play music from my personal collection with Rhythmbox. Then I visit last.fm again. Behold, now it has lots of interesting suggestions. I just click "Play your recommendations" and I get a pretty varied assortment of music. You can control the stream to some degree by either hitting love or ban to what you're listening to.&lt;br /&gt;&lt;br /&gt;There are some minor glitches, e.g. I yesterday loved too many film music pieces so it was getting a bit stuck on that kind of music until I started banning some of the pieces, but overall it's working incredibly well.&lt;br /&gt;&lt;br /&gt;An interesting side effect is that you can listen to people's music taste. Now if I only knew someone on last.fm whose music taste I would like to listen to... My profile &lt;a href="http://www.last.fm/user/olelaursen"&gt;is here&lt;/a&gt; in case you want to listen to mine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-806460413081753143?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/806460413081753143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/listening-to-music-and-lastfm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/806460413081753143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/806460413081753143'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/listening-to-music-and-lastfm.html' title='Listening to music and last.fm'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7243144852127784613</id><published>2008-09-16T10:21:00.021+02:00</published><updated>2008-09-20T20:05:03.411+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The role of culture - and what's an object?</title><content type='html'>I had an interesting thought today. Why do you eat of plates? Our dining table is covered by a fine oilcloth, why don't we eat of that?&lt;br /&gt;&lt;br /&gt;The plain answer, I think, is that we've been taught when we were young to use plates, to the point that it even seems unhygienic not to do so. Imagine a dinner with meat, potatoes and a batch of sticky sauce on your dining table.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://farm2.static.flickr.com/1422/573135565_7d7f7f8d60.jpg?v=0"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://farm2.static.flickr.com/1422/573135565_7d7f7f8d60.jpg?v=0" border="0" alt="" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;font-size:smaller"&gt;Image by &lt;a href="http://flickr.com/photos/ayalan/573135565/"&gt;ayalan&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If you're looking for deeper reasons for why one eats of plates it's probably easy to find out. Just have a dinner directly on your table. I suspect the reasons have something to do with the difficulty of cleaning up afterwards and sharp knives  and scratches in the surface.&lt;br /&gt;&lt;br /&gt;Pottery is a pretty old technology. The point is that it's part of our culture, we never think about it, we just do it. But it's one of the million of things that make up the great technological advancements civilisation has made over the past centuries. If we weren't able to absorb them, if we had to think about all of them, we would never have gotten anywhere.&lt;br /&gt;&lt;br /&gt;A similar thing goes on in software. With a programming language you can build anything you can think of, provided you have the hardware to run it on. But still it takes time to invent new things. Old things have to be absorbed into our culture before we can move on, otherwise the leap is too great.&lt;br /&gt;&lt;br /&gt;Of course behaviours that once have been embedded might stop making sense as the environment changes, unnoticed. When you dig into it the reasons stop being reasonable. I think you'll find that traditions and rituals provide a rich source of examples of this phenomenon - it made sense to those who concocted it, but 50 years later it's really silly when you think about it. Of course, that might not stop you from enjoying it.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://farm1.static.flickr.com/44/119559865_cbeca5af9b.jpg?v=0"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://farm1.static.flickr.com/44/119559865_cbeca5af9b.jpg?v=0" border="0" alt="" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;font-size:smaller"&gt;Image by &lt;a href="http://flickr.com/photos/thomashawk/119559865/"&gt;Thomas Hawk&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In any case, I think it's sometimes interesting to turn things we take for granted upside-down. Today I've been thinking about the corner stone of object-oriented programming. Let's be pragmatic. What's an object?&lt;br /&gt;&lt;br /&gt;You might think of attributes and methods. My take on it is that it depends.&lt;br /&gt;&lt;br /&gt;In some types of code, it's a very useful modeling utility.&lt;br /&gt;&lt;br /&gt;For instance, just think of the string class in your favourite programming language. It encapsulates most of the stuff you'd want to do with a string in a neat little package you can easily instantiate as many of as you like. If you've ever tried the horror that is the built-in string handling in the C standard library (not to pick on it, it was probably fine for its time), you'll really appreciate that. You might miss the occasional method or two, but still, it's very useful and easy to understand.&lt;br /&gt;&lt;br /&gt;Library code is full of neat little packages like this. Dynamic arrays, linked lists, HTTP handling, database connections, widgets in a graphical toolkit.                                                                                                               &lt;br /&gt;&lt;br /&gt;As you go to application code, the answer is in many cases less clear cut. To a certain extent, this is perhaps because the concepts are less understood or perhaps just less well-defined.&lt;br /&gt;&lt;br /&gt;In my applications I find that I have objects that are more or less just data (plain old data, PODs as they call them in C++ lingo) and objects that aren't really objects that I instantiate, let do a well-defined job and then throw away, but more of the sort of puppeteers or glue. These are all objects that give me head-aches. I also find some objects that are like the library objects, neat little packages. These are usually a great relief.&lt;br /&gt;&lt;br /&gt;The first type of objects, typical application objects, are troublesome because they go against the culture of object-oriented programming. Which says that you shall divide your code into neat little interacting packages with methods and encapsulated attributes, instantiated and used dynamically.&lt;br /&gt;&lt;br /&gt;But if we step back, then what's the purpose of objects? As I see it, it's a way of organising code. Think of it as a hierarchy. At the lowest level, you have the individual lines of code. We organise them into functions which let us deal with perhaps 10-100 times the amount of code, still maintaining an overview, like the headlines in a newspaper that groups the thousands of lines of text into articles.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://farm1.static.flickr.com/33/36572011_3b1d69ab85.jpg?v=0"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://farm1.static.flickr.com/33/36572011_3b1d69ab85.jpg?v=0" border="0" alt="" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;font-size:smaller"&gt;Image by &lt;a href="http://flickr.com/photos/jurvetson/36572011/"&gt;jurvetson&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As a program grows, you need another level, one that will give you 10-100 more code without losing the grip of the program. This is plain hierarchy theory. Look at the folders on your computer for a prime example. An object is one such next level, one that is more flexible out of the box than traditional function library modules because it has state.&lt;br /&gt;&lt;br /&gt;However, just because you need another level, it doesn't mean that the code will fit well into the object-oriented thinking of a neat little package where the data is encapsulated inside. I personally think it's impossible in many applications; otherwise it's going to take a lot of hard thinking, and I mean really a lot, because I have often wasted hours trying to find a way out of my crippled semi-objects.&lt;br /&gt;&lt;br /&gt;It is true for the plain old data structures. In many cases, you just need to group a set of attributes under a name without the functionality making use of the attributes logically or even illogically belonging to the grouping. So just put them in, recognising this as a case where the object thinking is useful but not a 100% fit.&lt;br /&gt;&lt;br /&gt;If you're writing getters and setters for this kind of stuff, you're wasting your time serving the culture of object-oriented programming as it was taught to you.&lt;br /&gt;&lt;br /&gt;The same goes for glue code. It may organise the code better to put it inside an object. If so, go for it even if you're never going to do typical object stuff with it.&lt;br /&gt;&lt;br /&gt;In any case, as I see it the goal here is a well-organised program. A well-organised program is obviously easier to understand, and thus easier to work with, extend, fix bugs in, etc. Think about that and forget culture next time your wrestle with program design and nothing seems right.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7243144852127784613?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7243144852127784613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/role-of-culture-and-whats-object.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7243144852127784613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7243144852127784613'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/role-of-culture-and-whats-object.html' title='The role of culture - and what&apos;s an object?'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5699574958418704653</id><published>2008-09-14T17:59:00.009+02:00</published><updated>2008-09-14T18:36:02.683+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Weird web troubles</title><content type='html'>No, I'm not dead yet. I have even been doing some of the maintainer tasks for Flot I have neglected the past months.&lt;br /&gt;&lt;br /&gt;One of the new things in Flot is support for hovering/clicking with the mouse on the points. I was spending some time yesterday adding highlights so that Flot can show which point you're pointing the mouse at.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_PIM6r0YLhk4/SM09Oa0KeRI/AAAAAAAAACg/U-HyTKj-q9Y/s1600-h/screenshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_PIM6r0YLhk4/SM09Oa0KeRI/AAAAAAAAACg/U-HyTKj-q9Y/s400/screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5245916458882070802" /&gt;&lt;/a&gt;&lt;br /&gt;I got to the point that I was ready to performance test it, on my trusty old Pentium 3 950 MHz laptop. It felt a bit slow. Surprisingly, the profile from Firebug indicated that the slow function was the jQuery function &lt;code&gt;offset()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;I use it to compute where a mouse event is inside the plotting canvas - for some reason computing this offset separately is the only reliable way to know where the mouse is &lt;em&gt;inside&lt;/em&gt; the element where the event happened because you only get the global coordinates reliably in the event. You would have thought local coordinates was a natural thing to standardize and every browser support, but no.&lt;br /&gt;&lt;br /&gt;Anyway, &lt;code&gt;offset()&lt;/code&gt; walks up the DOM tree, computing offsets along the way. And it was taking 25 ms per invocation on average! That's just ridiculous.&lt;br /&gt;&lt;br /&gt;The funny thing is that I was meanwhile updating my system, apparently also updating Firefox from 2.x to 3.0.1. So when I boot up today with a couple of ideas for working around the slow offset function, it's now 4 ms on average. That's still far too much for querying a simple DOM tree, but it's not a show stopper anymore.&lt;br /&gt;&lt;br /&gt;I think the lesson here is that as soon as you do exotic things in the browser, things that are not really about browsing HTML pages, you get bitten by weird bugs. But the state of affairs is improving.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Pssst. We're having a sale on &lt;a href="http://www.yayart.com/"&gt;YayArt&lt;/a&gt;. I have it from a reliable source that it's going to end soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5699574958418704653?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5699574958418704653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/weird-web-troubles.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5699574958418704653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5699574958418704653'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/09/weird-web-troubles.html' title='Weird web troubles'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_PIM6r0YLhk4/SM09Oa0KeRI/AAAAAAAAACg/U-HyTKj-q9Y/s72-c/screenshot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5547177641972563961</id><published>2008-04-30T16:53:00.007+02:00</published><updated>2008-04-30T17:17:19.804+02:00</updated><title type='text'>The way of nature</title><content type='html'>I was helping my father put up new boards for the roof of my parent's house today. The old one was, well, old and rotten. It was the last remains covering the gutter on the north side of the house, we've already replaced the boards on the other sides of the house (got quite a thrill out of the top ones on a wobbly aluminium scaffold 8 meters above ground).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_PIM6r0YLhk4/SBiNL4h8vhI/AAAAAAAAACE/qljLrr9jp6c/s1600-h/Blackbird_2.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_PIM6r0YLhk4/SBiNL4h8vhI/AAAAAAAAACE/qljLrr9jp6c/s400/Blackbird_2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5195057405464722962" /&gt;&lt;/a&gt;&lt;br /&gt;Anyways, as we've done about half of the boards and reach the far end of the house, a female blackbird starts examining the wooden box around the gutter carefully at the other end, apparently interesting in using it for a new home. A few moments later the male blackbird, maybe its mate, does the same while we stand there, hammering boards in a few meters away.&lt;br /&gt;&lt;br /&gt;Leave things alone a few minutes and life around you starts occupying the good spots. Nature or perhaps rather evolution is wasteful with life, but certainly not with space.&lt;br /&gt;&lt;br /&gt;I sometimes wonder how fast a city would disappear if all humans were all suddenly extinct by a new plague-like disease.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5547177641972563961?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5547177641972563961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/04/way-of-nature.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5547177641972563961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5547177641972563961'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/04/way-of-nature.html' title='The way of nature'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_PIM6r0YLhk4/SBiNL4h8vhI/AAAAAAAAACE/qljLrr9jp6c/s72-c/Blackbird_2.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2658045271122691586</id><published>2008-04-17T17:29:00.002+02:00</published><updated>2008-04-17T17:34:31.081+02:00</updated><title type='text'>YayArt is up!</title><content type='html'>So it finally happened, we launched &lt;a href="http://www.yayart.net/"&gt;YayArt&lt;/a&gt;! It's a community-based approach to new digital art for the people (real prints).&lt;br /&gt;&lt;br /&gt;That's all. I'm wasted now. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2658045271122691586?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2658045271122691586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/04/yayart-is-up.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2658045271122691586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2658045271122691586'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/04/yayart-is-up.html' title='YayArt is up!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5242187367979388119</id><published>2008-03-07T16:11:00.003+01:00</published><updated>2009-10-23T16:09:15.974+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flot 0.4 released!</title><content type='html'>New version of &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt; is out. See the &lt;a href="http://flot.googlecode.com/svn/trunk/NEWS.txt"&gt;changes&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The date/time support has finalled arrived and there's support for coloring background areas. For instance, you might use this to &lt;a href="http://people.iola.dk/olau/flot/examples/visitors.html"&gt;plot the visitors to the Flot homepage&lt;/a&gt;. There's also support for segmented lines. Plus a long list of bug fixes and tweaks.&lt;br /&gt;&lt;br /&gt;For the next release, I'll probably be focusing on adding more interactive features. There are some patches floating on the mailing list I need to take a look at.&lt;br /&gt;&lt;br /&gt;I also think it would be a good idea with a hand-holding tutorial that for people who are not accustomed to Javascript plotting. I have something in the works, more about that later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5242187367979388119?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5242187367979388119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/03/flot-04-released.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5242187367979388119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5242187367979388119'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/03/flot-04-released.html' title='Flot 0.4 released!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8314821456880847393</id><published>2008-03-07T11:03:00.009+01:00</published><updated>2008-03-07T12:48:40.073+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Favourite Linux bug of all times</title><content type='html'>Here's the story about my favourite Linux bug: the thrashing hell syndrome.&lt;br /&gt;&lt;br /&gt;To reproduce it you need to have swap activated and a run-away process. Traditionally, for the latter Netscape Navigator was great, in these days it might happen with Firefox with Flash or Monodevelop. Or if you're developing something and accidentally make an eternal loop that allocates memory.&lt;br /&gt;&lt;br /&gt;What will happen is that the system quickly slows down. You'll have some seconds to react to kill the process. If you're too slow, the graphical interface locks up and you're toast while the machine enters thrashing hell. You can usually get to a terminal and even enter username and password, but it locks up again before the shell is running. Probably because you can't spawn a new process when in thrashing hell.&lt;br /&gt;&lt;br /&gt;The condition will last for anywhere between a couple of minutes and more than half an hour. Unless you power off the machine, there's nothing you can do. At all. Other than to stare at a frozen screen while the machine entertains itself.&lt;br /&gt;&lt;br /&gt;At &lt;a href="http://kerneltrap.org/node/142"&gt;some point&lt;/a&gt; an &lt;a href="http://linux-mm.org/OOM_Killer"&gt;out-of-memory killer&lt;/a&gt; was introduced in an attempt, I believe, to mitigate this problem. Its job was to kill a process to free up memory in memory-tight situations. Here's a &lt;a href="http://lwn.net/Articles/104179/"&gt;hilarious analogy&lt;/a&gt; by Andries Brouwer. I suspect Netscape was the main cause, although I might be wrong, I've never been close to kernel development anyway.&lt;br /&gt;&lt;br /&gt;Anyway, that was long time ago.&lt;br /&gt;&lt;br /&gt;Fast forward to the introduction of Ubuntu, the really easy-to-use GNU/Linux. I should warn you that I'm going to do some bashing of Ubuntu now, which is not really fair since Ubuntu is pretty cool.&lt;br /&gt;&lt;br /&gt;When I first got into thrashing hell on a Ubuntu system, I didn't believe it had happened. I thought the OOM killer should have saved me, but apparently not. Probably it'll only kick in when all of the swap is used. But hey, Ubuntu is for the masses, right? The system should have protected me. That's what I naively thought.&lt;br /&gt;&lt;br /&gt;So I filed &lt;a href="https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.15/+bug/27392"&gt;a Ubuntu bug report&lt;/a&gt; on the kernel. I hoped that a couple of the knowledgeable people who are working on Ubuntu would fix this old problem in a snap.&lt;br /&gt;&lt;br /&gt;Which didn't happen. The bug wasn't accepted. As far as I can tell because the guy in the other end thought the process limiting support in the kernel is advanced enough to take care of the problem. I wasn't sure whether he was right. But I filed a &lt;a href="https://bugs.launchpad.net/ubuntu/+bug/27441"&gt;new bug report&lt;/a&gt; with the hope that someone else would see the vision.&lt;br /&gt;&lt;br /&gt;That's over two years ago. So far no progress. Which is not what I'd hoped, because it really puts the system in a bad light. If this happened to an average user, that person would instantly conclude that Ubuntu/Linux just crashed.&lt;br /&gt;&lt;br /&gt;But it's sort of fine, because it's open source etc. etc. and the people in the other end are free to spend their time as they like.&lt;br /&gt;&lt;br /&gt;Then today there was an update. Cool! I thought. Until I found out that it's just a drone reply. Even a particularly annoying one, apparently it's not good enough to report a bug on the latest release since the problem might theoretically be fixed in the next alpha. The bug has already been auto-closed once because if noone's interested then the report must be invalid.&lt;br /&gt;&lt;br /&gt;So noone wants to listen. That makes me think this will continue to be my favourite Linux bug for many more years to come.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PS: My favourite recent Windows bug is probably the one where Windows interprets all mouse clicks as right-button clicks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8314821456880847393?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8314821456880847393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/03/favourite-linux-bug-of-all-times.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8314821456880847393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8314821456880847393'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/03/favourite-linux-bug-of-all-times.html' title='Favourite Linux bug of all times'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-9134312072476744190</id><published>2008-02-24T23:03:00.005+01:00</published><updated>2008-02-24T23:46:21.931+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plants'/><title type='text'>Quiescence</title><content type='html'>No update for a long time. I've been busy, mostly at work but also at home with the garden.&lt;br /&gt;&lt;br /&gt;I currently have our small garden full of bulbs. Many already have the first small leaves up, including some daffodils and tulips (in spite of it still being February!). I've buried so many bulbs that I have but the faintest idea of what is where. The last few batches are pretty much placed completely randomly.&lt;br /&gt;&lt;br /&gt;I've also invested in seeds from &lt;a href="http://www.plant-world-seeds.com/"&gt;Plant World Seeds&lt;/a&gt;. I chose them because they have several bags with different mixtures; our garden is not of a size where it's relevant with 50 flowers of the same kind. Janne also bought some seeds from a &lt;a href="http://www.barney.dk/"&gt;Danish merchant&lt;/a&gt;, mostly small flowering trees, so the house is now full of pots with moist soil and small white labels.&lt;br /&gt;&lt;br /&gt;I'm currently mostly excited about the 12 pots of &lt;a href="http://images.google.com/images?q=clematis+montana"&gt;clematis montana&lt;/a&gt; (four seeds in each pot, unfortunately it can take three months before we see any seedlings), and a bag of &lt;a href="http://images.google.com/images?q=dactylorhiza+maculata"&gt;dactylorhiza maculata&lt;/a&gt; (heath spotted orchid) that I haven't figured out how to sow yet. The funny part is that we haven't even sowed half of the seeds. The summer is still a bit too far away for most of them. I think we're going to have a problem by then.&lt;br /&gt;&lt;br /&gt;I read somewhere that the original meaning of "paradise" is a garden. Last year it was a little empty. Not surprising given that I dug all of it in April and May. But we'll see this year.&lt;br /&gt;&lt;br /&gt;In other news, I've spent some time today and yesterday working on time series support in &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt;. I just committed the code to the SVN repository. I need to clean up a couple of things, test it a bit more and write up some documentation before release. Next on the list is reviewing the patches on the bug tracker and on the mailing list.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-9134312072476744190?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/9134312072476744190/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2008/02/quiescence.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/9134312072476744190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/9134312072476744190'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2008/02/quiescence.html' title='Quiescence'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7784379036802872426</id><published>2007-12-14T19:04:00.000+01:00</published><updated>2009-10-23T16:09:15.974+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flot 0.3 released!</title><content type='html'>New version of &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt; is out. See the &lt;a href="http://flot.googlecode.com/svn/trunk/NEWS.txt"&gt;changes&lt;/a&gt;. This is mostly a bug fix release because the previous zip/tarball didn't contain the jquery.js file.&lt;br /&gt;&lt;br /&gt;I've also started a &lt;a href="http://groups.google.com/group/flot-graphs"&gt;mailing list&lt;/a&gt; which I really recommend using.&lt;br /&gt;&lt;br /&gt;I did sneak in a new feature - that's why it is 0.3 instead of 0.2.1. You can now enable people to click on the plot and get the (x,y) coordinates (in the unit of the plot, not pixels) out.&lt;br /&gt;&lt;br /&gt;This is a primitive form of interaction. Eventually I want to support automatic data coupling so you can know what has been clicked and in the same vein highlight the stuff underneath the cursor. But at the moment I'm pretty swamped with things to do, and this clicking was enough for our internal application. I suspect it will be useful for lots of other things too, at least until Flot is clever enough to map from points to data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7784379036802872426?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7784379036802872426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/12/flot-03-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7784379036802872426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7784379036802872426'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/12/flot-03-released.html' title='Flot 0.3 released!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3820466157762711858</id><published>2007-12-07T16:56:00.000+01:00</published><updated>2009-10-23T16:09:15.974+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flot 0.2 released!</title><content type='html'>I've released a new version of &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt;. The API is now documented and legends have a background. And you can get the whole package as a zip/tarball. See the &lt;a href="http://flot.googlecode.com/svn/trunk/NEWS.txt"&gt;changes&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Lots of people are asking for pie charts. And some people have difficulty seeing the charts in IE 6. Which is really unfortunate because I can't reproduce it myself. All the machines with IE 6 I have access to render them fine. It's probably something silly, but it's going to take a bit of debugging to find it.&lt;br /&gt;&lt;br /&gt;My plans for the near future mostly deal with getting proper support for time series and some of the other interactive stuff if I can squeeze it in. And get a forum or mailing list up running.&lt;br /&gt;&lt;br /&gt;If you have a public application of Flot online, I'd like to hear about it, I intend to compile a list of real-world examples.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3820466157762711858?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3820466157762711858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/12/flot-02-released.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3820466157762711858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3820466157762711858'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/12/flot-02-released.html' title='Flot 0.2 released!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4900940918759742431</id><published>2007-12-04T12:48:00.000+01:00</published><updated>2009-10-23T16:09:15.974+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flot 0.1 released!</title><content type='html'>We've released the first version of &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt;, a new Javascript plot library for &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;It draws on inspiration from &lt;a href="http://solutoire.com/plotr/"&gt;Plotr&lt;/a&gt; and &lt;a href="http://www.liquidx.net/plotkit/"&gt;PlotKit&lt;/a&gt;, and several other commercial packages. But also from venerable old &lt;a href="http://www.gnuplot.info/"&gt;gnuplot&lt;/a&gt; - there's nothing more boring than hand-picking axis scales so I wanted the default algorithm to be smart.&lt;br /&gt;&lt;br /&gt;The project actually got started because we were growing dissatisfied with Plotr which looks nice but is pretty dumb when it comes to auto-detecting stuff and didn't have any interactive features which we needed for a custom web-based business intelligence application.&lt;br /&gt;&lt;br /&gt;I find it interesting that I initially thought plotting to be a hard problem - but this project got started a couple of months ago and I've only been hacking on it sporadically. Right now, the library is still missing support for time series, but is otherwise pretty much feature-complete for what we need it to do. I'm looking forward to hearing other people's opinion.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4900940918759742431?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4900940918759742431/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/12/flot-01-released.html#comment-form' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4900940918759742431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4900940918759742431'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/12/flot-01-released.html' title='Flot 0.1 released!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5890096382428161166</id><published>2007-11-29T10:57:00.001+01:00</published><updated>2007-11-29T11:24:19.946+01:00</updated><title type='text'>Codename Nemo 0.1 alpha released</title><content type='html'>We've finally released an &lt;a href="http://www.iola.dk/nemo/"&gt;alpha version&lt;/a&gt; of our file manager project!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.iola.dk/nemo/blog/wp-content/uploads/2007/11/screenshot-nemo-01-alpha-1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://www.iola.dk/nemo/blog/wp-content/uploads/2007/11/screenshot-nemo-01-alpha-1.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This has been a long way. The idea has been the same throughout the project, to focus on the aspects of documents that people actually remember instead of forcing a hierarchical tree down their throat.&lt;br /&gt;&lt;br /&gt;Believe it or not, a lot of novices find it difficult to understand the hierarchical folder structure, and it's not particularly easy to navigate or keep in order for most computer literates either. People have trouble remembering what folder they put things in - and they would rather not put things into folders if they can avoid it.&lt;br /&gt;&lt;br /&gt;This has been known for a very long time. We're building on research from the 80s here.&lt;br /&gt;&lt;br /&gt;The big question is how you design a GUI that makes it possible to find the files again without a hierarchy. There's been a lot of attempts on this. Recently, people have been using pure text search - but while it can help, it's not a panacea. It's not always easy to remember something precise enough to put into the seach bar.&lt;br /&gt;&lt;br /&gt;There's also been a lot of experiments with 3d spatial systems that mimic the physical world, usually with a high bling-bling factor. They look funny, but in my opinion they're never going to work because a) cleaning up in the physical world is really painful and something most people tend to avoid and b) the physical world scales really bad. Sure it's easy to find something on your desk if you only have 10 documents on it. But what if you have 1000?&lt;br /&gt;&lt;br /&gt;So what do you do? We started from something that looked completely different from what this alpha release looks like and went through a couple of iterations before we arrived at what we have now.&lt;br /&gt;&lt;br /&gt;There are lots of problems in this alpha release, but I personally think the basics are about right now. Is it usable? I think so. You can't select files or delete them, or even rename them because we haven't coded a proper custom widget for it yet (like e.g. in Nautilus). Must of the automatic stuff that's supposed to be smart is really pretty dumb because it hasn't been refined yet.&lt;br /&gt;&lt;br /&gt;But you can get an overview of your files, organize them and find them again. And that's what it is all about.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5890096382428161166?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5890096382428161166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/codename-nemo-01-alpha-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5890096382428161166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5890096382428161166'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/codename-nemo-01-alpha-released.html' title='Codename Nemo 0.1 alpha released'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5231194657610420850</id><published>2007-11-27T17:24:00.000+01:00</published><updated>2007-11-27T17:56:33.224+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Adding quote marks with Django</title><content type='html'>I've posted my first Django snippet on djangosnippets.org. It's a &lt;a href="http://www.djangosnippets.org/snippets/486/"&gt;template filter for adding quotes&lt;/a&gt; around a piece of text. It's clever enough to take paragraph tags into account.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5231194657610420850?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5231194657610420850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/adding-quote-marks-with-django.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5231194657610420850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5231194657610420850'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/adding-quote-marks-with-django.html' title='Adding quote marks with Django'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3554691625995020919</id><published>2007-11-23T10:00:00.000+01:00</published><updated>2007-12-19T18:59:45.719+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Debugging</title><content type='html'>I've been thinking about debugging lately. In my humble opinion, there are four phases in the art of debugging.&lt;br /&gt;&lt;br /&gt;The first phase, which I think of as the &lt;b&gt;debug prevention&lt;/b&gt; phase is where you proactively do something to prevent silly bugs from happening.&lt;br /&gt;&lt;br /&gt;The thing is that debugging is time-consuming. Not only because you have to find the bugs, but also because it disturbs the flow. Silly bugs are like faulty brakes on a bicycle. It's neither fun or productive to push and push to get up to speed just to find yourself hanging over the handlebars at a sudden halt.&lt;br /&gt;&lt;br /&gt;So it's worthwhile to do something to avoid obvious sources of bugs.&lt;br /&gt;&lt;br /&gt;Here's an example of an obvious source that I recently encountered (apologies to the source): you have a model class with a certain state which can change on its own while also being changed in the user interface by the user. One way to approach this is to code the model and user interface separately, each with its own state, then add a bit of glue code to synchronize the two.&lt;br /&gt;&lt;br /&gt;The (obvious) problem is that the two copies of the states can get out of sync, in which case you're toasted. It's also pretty confusing - when you ask for the real state, the master state so to speak, where do you ask? The model or the UI? So avoid the copy in the UI, even if it's less convenient to have to go ask the model all the time.&lt;br /&gt;&lt;br /&gt;Of course, obvious sources of bugs are probably not obvious unless you spend some time reflecting on what you're doing. Here's a simple rule of thumb: If something is so confusing to think about that you can't immediately make head or tails of it, then it's probably worthwhile spending some time refactoring it because otherwise it's likely to become a source of bugs. Sometimes it's just a matter of renaming variables and functions.&lt;br /&gt;&lt;br /&gt;The second phase is &lt;b&gt;testing&lt;/b&gt;. This is where you find the bugs. The relation to debugging is that you &lt;em&gt;should&lt;/em&gt; test and never assume that things are working just because the compiler/interpreter/whatever ate it. Untested code is an obvious source of bugs and it's always better to fix the bugs before you've embarassed yourself by shipping them. Debugging in the open is not fun. It's also much easier to find the bugs if you test early.&lt;br /&gt;&lt;br /&gt;The third phase is the &lt;b&gt;trial-and-error&lt;/b&gt; phase. This is the initial reaction most people have when something stops working: What, it worked before, let me just try this, then it'll probably work again. Repeat.&lt;br /&gt;&lt;br /&gt;What's important in this phase is to leave it early if the first couple of tries don't work out. Trial and error is time-consuming and mentally draining because it puts you through lots of frustrating failures with no clear reward. Don't get stuck.&lt;br /&gt;&lt;br /&gt;Instead consider proceeding to phase four which is the &lt;b&gt;observation-thinking&lt;/b&gt; cycle. Here the goal is not to fix the problem, but to understand what's going on. Think of yourself as the analyzing surgeon who's on a mission to make the smallest possible cut, not the rampant safari hunter with the over-size elephant gun.&lt;br /&gt;&lt;br /&gt;How do you understand your program? By running it, either in a debugger or with print statements sprinkled so you can see why the code is failing. You start at the innermost level, then gradually trace your way through the call stack.&lt;br /&gt;&lt;br /&gt;The idea is &lt;em&gt;not to trust anything you see&lt;/em&gt;. It's always a bad idea to assume something is working before you have printed the result and verified it in the actual faulty run. For instance, it might be a good idea to really start at the innermost level even though there's nothing exciting going on there. I've spent countless hours trying to understand why something obviously correct wasn't working just to find out that the code I was looking at wasn't actually the code running. Maybe you've forgotten to save the file. Or upload it. Or recompile. Or maybe it's running another, similar function you've forgotten about. Or maybe it suddenly crashed because of something seemingly unrelated. A print statement can tell you the truth.&lt;br /&gt;&lt;br /&gt;You observe (real output, not source code) and you think, add more debug code, and observe and think. When you're done, you &lt;em&gt;know&lt;/em&gt; what the program, your little creation, is doing. That's a nice, rewarding feeling. You're in control. And it makes the kind of story you can tell your (geeky) friends: I had this really annoying bug, and I traced it through ..., and finally I had it nailed!&lt;br /&gt;&lt;br /&gt;Sometimes it's difficult to proceed to phase four because the debugging environment, frankly, is not up to the task. In which case you'd better start working on the debugging environment rather than building up frustration when the bugs begin to manifest themselves. So little is needed - if you can get printf-like output and short compile-run cycles, then you're good to go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3554691625995020919?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3554691625995020919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/debugging.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3554691625995020919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3554691625995020919'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/debugging.html' title='Debugging'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-955287460079650404</id><published>2007-11-21T10:24:00.000+01:00</published><updated>2007-12-19T19:02:42.654+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Peopleware</title><content type='html'>I finished reading &lt;i&gt;&lt;a href="http://www.dorsethouse.com/books/pw.html"&gt;Peopleware: Productive Projects and Teams&lt;/a&gt;&lt;/i&gt;. It's about the human factor of conducting projects. Which according to the book completely overshadows any other aspects of the project, even though it's often ignored completely.&lt;br /&gt;&lt;br /&gt;The way the book is written it's addressing project managers. But don't let that fool you. If you're somehow involved in a software project, you're also involved in the management of it. If you're not, there's something wrong - read the book to find out why.&lt;br /&gt;&lt;br /&gt;I think the main message of the book is that you should focus on keeping the people involved in a project happy to make the productive.&lt;br /&gt;&lt;br /&gt;Make their work fun, go to great lengths to ensure they can work together well as a team, stay out of decision making that others are in a better position to do in spite of what hierarchy suggests and avoid interrupting everyone all the time.&lt;br /&gt;&lt;br /&gt;In doing so, you're likely to have increased their productivity by an order of magnitude more than any Methodology (as they put it) can give you. The reason is that people are not machines. If you expect them to be and make them feel that way, and this is easier to do than you might think if you come from a software background where it's all about the logics, then it shifts from high gears to low gears, the magical stuff that makes a design or a program uncover itself from nothing just by the help of &lt;em&gt;human&lt;/em&gt; mind.&lt;br /&gt;&lt;br /&gt;There a lot of interesting tidbits in the book too. For example, it's often postulated that you need a deadline to make people work efficiently. So okay, people are not machines, they're much worse, they need a bit of whipping to make them work hard. The book cites a study which found that people are almost twice as productive on projects without a deadline compared to projects where the project manager sets one.&lt;br /&gt;&lt;br /&gt;Another example is that people tend to react to change in unpredictable ways. For instance, if you do something different from what you use to do, you might be more productive, not necessarily because the new way is better than the old, but simply because you expect it to be better (I guess this is the Placebo effect) or because it's new and exciting and you're generally more productive when you're excited. The authors call the phenomenon the &lt;a href="http://en.wikipedia.org/wiki/Hawthorne_effect"&gt;Hawthorne effect&lt;/a&gt; after an experiment with illumination in a factory. Increase the lighting and people are more productive, decrease the lighting and people are also more productive.&lt;br /&gt;&lt;br /&gt;Anyway, if you're somehow involved in software projects, or any other kind of mind-worker projects for that matter, I recommend you read this book. You won't regret it. It taught me more about my work than any other book I've ever read.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-955287460079650404?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/955287460079650404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/peopleware.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/955287460079650404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/955287460079650404'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/peopleware.html' title='Peopleware'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5594425841280486575</id><published>2007-11-05T10:11:00.000+01:00</published><updated>2007-11-05T10:25:03.393+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>Torturing prisoners</title><content type='html'>I &lt;a href="http://www.unsubscribe-me.org"&gt;unsubscribed&lt;/a&gt; last Friday.&lt;br /&gt;&lt;br /&gt;I can't believe some of the stories you hear. What happened to the &lt;a href="http://en.wikipedia.org/wiki/Third_Geneva_Convention"&gt;Geneva Conventions&lt;/a&gt;? What happened to treating other people, including your enemies, with respect? What happened to being decent people?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5594425841280486575?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5594425841280486575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/torturing-prisoners.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5594425841280486575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5594425841280486575'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/11/torturing-prisoners.html' title='Torturing prisoners'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8997591510093769896</id><published>2007-10-21T22:34:00.000+02:00</published><updated>2007-11-21T11:27:49.786+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Making foreach useful in the corner cases</title><content type='html'>One of the nice things about recent developments in mainstream programming languages is improved support for looping.&lt;br /&gt;&lt;br /&gt;In a functional language with proper support for lambda functions, it might not be a problem but if you're programming in something a bit more mainstream, chances are that you're writing loops over data structures all the time. In my experience, it turns out to be a real improvement if the language offers a bit of syntactic sugar and allows you to write something like:&lt;br /&gt;&lt;pre&gt;for element in datastructure &lt;br /&gt;    do something with element&lt;/pre&gt;&lt;br /&gt;Python's got it, C# and Java have it, last I heard there were plans to introduce it in C++ too.&lt;br /&gt;&lt;br /&gt;Until recently I thought this particular piece of syntactic sugar was of the kind which is really useful in 90% of the cases and completely useless in the remaining 10%. For example, what if you want to extract two elements and not just one? Or loop over two datastructures? Or only loop over certain elements?&lt;br /&gt;&lt;br /&gt;In fact, there are elegant solutions to all these problems. I've been pondering over them this week. Here's how you do in Python, but all this requires of the language is the concept of iterators and some kind of convenient support for tuples.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Extracting only part of the sequence:&lt;/b&gt; Slice the datastructure with a function that skips the parts you don't want.&lt;br /&gt;&lt;br /&gt;Python has the [start:end] operator which is as succinct as it gets for extracting an interval. There's also a more generic &lt;code&gt;islice&lt;/code&gt; function in the &lt;code&gt;itertools&lt;/code&gt; module which works with iterators:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: #4169e1;"&gt;from&lt;/span&gt; itertools &lt;span style="color: #4169e1;"&gt;import&lt;/span&gt; islice&lt;br /&gt;&lt;span style="color: #b8860b;"&gt;mylist&lt;/span&gt; = [1, 2, 3, 4, 5]&lt;br /&gt;&lt;span style="color: #4169e1;"&gt;for&lt;/span&gt; x &lt;span style="color: #4169e1;"&gt;in&lt;/span&gt; islice(mylist, 1, 4):&lt;br /&gt;    &lt;span style="color: #4169e1;"&gt;print&lt;/span&gt; x&lt;br /&gt;&lt;i&gt;(... prints 2, 3, 4)&lt;/i&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This idea can be extended in many ways, e.g. if you need every second element, it's easy to come up with a function that only iterates over them. If you need to exclude elements based on something related to the elements themselves and not their position in the datastructure, there's always the &lt;code&gt;filter&lt;/code&gt; function.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Extracting elements from several datastructures:&lt;/b&gt; Construct an iterator that iterates over the sequences in parallel, returning a tuple with one element from each.&lt;br /&gt;&lt;br /&gt;Python has the built-in &lt;code&gt;zip&lt;/code&gt; function which returns the complete sequences zipped up as a list of tuples. Until Python 3 is out, &lt;code&gt;izip&lt;/code&gt; in &lt;code&gt;itertools&lt;/code&gt; is closer to what we want:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: #4169e1;"&gt;from&lt;/span&gt; itertools &lt;span style="color: #4169e1;"&gt;import&lt;/span&gt; izip&lt;br /&gt;&lt;span style="color: #b8860b;"&gt;xlist&lt;/span&gt; = [1, 2, 3]&lt;br /&gt;&lt;span style="color: #b8860b;"&gt;ylist&lt;/span&gt; = [&lt;span style="color: #bc8f8f;"&gt;"a"&lt;/span&gt;, &lt;span style="color: #bc8f8f;"&gt;"b"&lt;/span&gt;, &lt;span style="color: #bc8f8f;"&gt;"c"&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: #4169e1;"&gt;for&lt;/span&gt; x, y &lt;span style="color: #4169e1;"&gt;in&lt;/span&gt; izip(xlist, ylist):&lt;br /&gt;    &lt;span style="color: #4169e1;"&gt;print&lt;/span&gt; x, y&lt;br /&gt;&lt;i&gt;(... prints 1 a, 2 b, 3 c)&lt;/i&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So it's no problem. We can still maintain our nice, succinct &lt;code&gt;for&lt;/code&gt; loop, there's no need to manually iterate over each sequence in turn. Note that &lt;code&gt;zip&lt;/code&gt; and &lt;code&gt;izip&lt;/code&gt; will automatically stop when one of sequences runs out of data.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Extracting several elements from the same datastructure:&lt;/b&gt; Construct an iterator that returns a sliding window of the sequence as a tuple.&lt;br /&gt;&lt;br /&gt;You could code this manually, but it's even easier to do once you know &lt;code&gt;zip&lt;/code&gt;: just &lt;code&gt;zip&lt;/code&gt; the datastructure with itself, with a little offset. In Python, that's &lt;code&gt;zip(seq, seq[1:])&lt;/code&gt;, or more generally:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: #4169e1;"&gt;from&lt;/span&gt; itertools &lt;span style="color: #4169e1;"&gt;import&lt;/span&gt; izip, islice&lt;br /&gt;&lt;span style="color: #4169e1;"&gt;def&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;slidingwindow&lt;/span&gt;(seq, windowsize):&lt;br /&gt;    t = ()&lt;br /&gt;    &lt;span style="color: #4169e1;"&gt;for&lt;/span&gt; i &lt;span style="color: #4169e1;"&gt;in&lt;/span&gt; range(windowsize):&lt;br /&gt;        t += (islice(seq, i, &lt;span style="color: #4169e1;"&gt;None&lt;/span&gt;),)&lt;br /&gt;    &lt;span style="color: #4169e1;"&gt;return&lt;/span&gt; izip(*t)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all you need to get two or more elements out at the same time:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: #b8860b;"&gt;mylist&lt;/span&gt; = [1, 2, 4, 8]&lt;br /&gt;&lt;span style="color: #4169e1;"&gt;for&lt;/span&gt; prev, val &lt;span style="color: #4169e1;"&gt;in&lt;/span&gt; slidingwindow(mylist, 2):&lt;br /&gt;    &lt;span style="color: #4169e1;"&gt;print&lt;/span&gt; val - prev&lt;br /&gt;&lt;i&gt;(... prints 1, 2, 4)&lt;/i&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;No need to manually set &lt;code&gt;prev&lt;/code&gt; to the predecessor in each iteration. You could easily add padding or let the window advance in larger steps if needed. The latter is probably easiest to do by combining the function with an appropriate slicing operation.&lt;br /&gt;&lt;br /&gt;Which brings me to the final point. An important part of the beauty of these operations is that they can be combined in a multitude of ways.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Note: if you're going to use &lt;code&gt;slidingwindow&lt;/code&gt; in production and want something to paste in, here's an even more general version that works with arbitrary iterator input:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: #4169e1;"&gt;from&lt;/span&gt; itertools &lt;span style="color: #4169e1;"&gt;import&lt;/span&gt; izip, tee&lt;br /&gt;&lt;span style="color: #4169e1;"&gt;def&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;slidingwindow&lt;/span&gt;(iterable, windowsize):&lt;br /&gt;    t = tee(iterable, windowsize)&lt;br /&gt;    &lt;span style="color: #4169e1;"&gt;for&lt;/span&gt; j &lt;span style="color: #4169e1;"&gt;in&lt;/span&gt; range(1, windowsize):&lt;br /&gt;        &lt;span style="color: #4169e1;"&gt;for&lt;/span&gt; i &lt;span style="color: #4169e1;"&gt;in&lt;/span&gt; range(j, windowsize):&lt;br /&gt;            &lt;span style="color: #4169e1;"&gt;try&lt;/span&gt;:&lt;br /&gt;                t[i].next()&lt;br /&gt;            &lt;span style="color: #4169e1;"&gt;except&lt;/span&gt; StopIteration:&lt;br /&gt;                &lt;span style="color: #4169e1;"&gt;pass&lt;/span&gt;&lt;br /&gt;            &lt;br /&gt;    &lt;span style="color: #4169e1;"&gt;return&lt;/span&gt; izip(*t)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;code&gt;tee&lt;/code&gt; function duplicates an iterator by storing the data returned by it until all its duplicates have passed it too. The code above advances each of the returned iterators by the correct amount. It seems it's a bit faster to use &lt;code&gt;tee&lt;/code&gt; than doing the equivalent stuff by hand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8997591510093769896?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8997591510093769896/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/10/making-foreach-useful-in-corner-cases.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8997591510093769896'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8997591510093769896'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/10/making-foreach-useful-in-corner-cases.html' title='Making foreach useful in the corner cases'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7886141470518538665</id><published>2007-10-16T22:45:00.000+02:00</published><updated>2007-10-16T22:59:32.580+02:00</updated><title type='text'>Landmark</title><content type='html'>Today I have lived for a quarter of a century. I was born in 1982. According to &lt;a href="http://en.wikipedia.org/wiki/1982_in_science"&gt;Wikipedia&lt;/a&gt;, it was the year the first computer virus escaped into the wild.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7886141470518538665?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7886141470518538665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/10/landmark.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7886141470518538665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7886141470518538665'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/10/landmark.html' title='Landmark'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2578535752474196835</id><published>2007-10-05T10:17:00.000+02:00</published><updated>2007-10-05T12:48:13.172+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Django-rendertext 0.2 and a new jquery plot plugin</title><content type='html'>A quick update. I've released &lt;a href="http://code.google.com/p/django-rendertext/"&gt;django-rendertext 0.2&lt;/a&gt;. The most notable change is support for GIF output, mostly intended for the browser that didn't support PNGs properly until very recently.&lt;br /&gt;&lt;br /&gt;I deliberately built rendertext with PNGs first because it's the format of the future. IE7 usage is picking up (see for instance &lt;a href="http://www.upsdell.com/BrowserNews/stat.htm"&gt;here&lt;/a&gt; or &lt;a href="http://www.w3schools.com/browsers/browsers_stats.asp"&gt;here&lt;/a&gt;), within a year or two it's probably completely dominant among IE users. Now that I whipped up the GIF support, I'm glad I didn't start with it because it was a real PITA to do with PIL (Python Imaging Library). PIL doesn't have proper documentation of the corner cases that turn out to be the most important and only sketchy support for palettes.&lt;br /&gt;&lt;br /&gt;I've also been working on a plotting plugin for &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;. We've not been entirely satisfied with &lt;a href="http://solutoire.com/plotr/"&gt;Plotr&lt;/a&gt; which otherwise appeared to be the prettiest and most advanced Javascript-only plotting library out there. It's for &lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt; and not for jQuery but most importantly I've found it confusing to hack on. And it's completely missing the interactive aspects. &lt;br /&gt;&lt;br /&gt;So I have started my own project. So far I think I've discovered that the hardest part of a library for plotting is not actually drawing the stuff, but layouting and auto-adjusting the axes. In any case, in two days I've gotten pretty far. Here's a screenshot (click to get the full version):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_PIM6r0YLhk4/RwX3Eh36tHI/AAAAAAAAABk/fKgVpZFy2TE/s1600-h/jquery-plot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_PIM6r0YLhk4/RwX3Eh36tHI/AAAAAAAAABk/fKgVpZFy2TE/s400/jquery-plot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5117768208760878194" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;What's missing right now is support for plotting points and columns, options for controlling the plot, legends, support for time and the interactive stuff such as zooming, highlighting graphs, tooltips for points on mouse hover. Also, the auto-adjusting algorithm is still a bit rough but I think I managed to crack it on paper last night so I just need to implement and test it.&lt;br /&gt;&lt;br /&gt;In other news, from Werner Vogels' blog here's &lt;a href="http://www.allthingsdistributed.com/2007/10/amazons_dynamo.html"&gt;how Amazon manages storage&lt;/a&gt; (warning! scientific paper ahead). Now that's as far as you can get from N=1 thinking. As an aside, I'm fond of that guy's name, Werner Vogels (note for English native speakers: in German the surname is pronounced like [fogels] not [wogels]). It's just like &lt;i&gt;der Vogelfänger bin ich ja&lt;/i&gt; I guess, nice rhythm.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2578535752474196835?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2578535752474196835/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/10/quick-update.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2578535752474196835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2578535752474196835'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/10/quick-update.html' title='Django-rendertext 0.2 and a new jquery plot plugin'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_PIM6r0YLhk4/RwX3Eh36tHI/AAAAAAAAABk/fKgVpZFy2TE/s72-c/jquery-plot.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-9076990553383443225</id><published>2007-09-21T22:11:00.000+02:00</published><updated>2007-09-22T00:43:36.146+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Object-oriented database systems</title><content type='html'>I had a discussion with &lt;a href="http://people.iola.dk/anders/"&gt;Anders&lt;/a&gt; today about databases.&lt;br /&gt;&lt;br /&gt;I'm not very fond of relational databases, or perhaps more precisely SQL. Having to convert back and forth between a database model and SQL and a programming language with all it entails of proper quoting to prevent SQL injection attacks and joining tables all over the place is really a great source of frustration. And a good waste of time.&lt;br /&gt;&lt;br /&gt;On the other hand, I've seen what a record-based file system with no built-in join can do to people. And the built-in indexing stuff that lets you search on different attributes without a linear scan is just neat.&lt;br /&gt;&lt;br /&gt;I think the great success of the relational database systems has a few simple explanations. Many business-related applications are really about the data. They spend their time collecting and displaying lots of simple, boring data - unlike an entertainment program or a standard desktop application that spends a lot of time dealing with small amounts of data, maybe less than say 1000 objects.&lt;br /&gt;&lt;br /&gt;This data may be larger than what can be kept in memory (the possibility of this happening is enough to make it a problem). Furthermore the data must be kept when the machine is turned off. So you have to deal with secondary storage, a topic largely ignored by most programming languages that otherwise go to great lengths to make dynamic use of primary storage as transparent as possible.&lt;br /&gt;&lt;br /&gt;Finally, you need to be able to access all that boring data in a many different ways. Say you have a situation that you naturally think of as a hierarchy, e.g. a company with multiple departments with each department having a number of employees and each employee having far to many tasks to do.&lt;br /&gt;&lt;br /&gt;In object-oriented thinking, you would model this as a list of departments, each with some attributes and a list of employees, each again with a list of tasks. Want to show the org chart in the company? Easy, just loop through the departments and print the list of employees one by one. Super efficient. Assign a task to an employee? Easy, just add it to his list. Super efficient. But what if you want to know who's task it is to empty the bin? Then you have to wade through two outer loops plus lots of unrelated stuff, just to get to the actual data you're interested in.&lt;br /&gt;&lt;br /&gt;Relational database systems, while solving the secondary storage problem, have an answer to this problem, and it's simple and elegant.&lt;br /&gt;&lt;br /&gt;Then never mind that getting data in and out of them is hugely painful, especially hierarchical data, even if you're not trying to force everything leaving the database into a class. &lt;br /&gt;&lt;br /&gt;I've recently been working a lot with Django's object-relational mapper, the ORM (which BTW means "worm" in Danish), and it really takes the pain out of the situation since it obviates the need for writing SQL strings in favour of native, albeit somewhat weird, Python code.&lt;br /&gt;&lt;br /&gt;However, I still wonder why true object-oriented databases haven't overtaken the world ages ago. One that I hand over my object to and later can get it back from, in one piece with no fuss.&lt;br /&gt;&lt;br /&gt;It turns out that there's a pretty large open-source one available, &lt;a href="http://www.db4o.com/"&gt;db4o&lt;/a&gt;. I think it solves the problem of retrieving objects based on arbitrary attribute values by incorporating a special query system where it's possible to define indexes like for a relational database, but I'll have to do some more reading.&lt;br /&gt;&lt;br /&gt;There's a smaller one, &lt;a href="http://www.garret.ru/~knizhnik/dybase.html"&gt;DyBASE&lt;/a&gt;, for Python (and Ruby and PHP). It could be interesting to compare the performance against Django's ORM + MySQL.&lt;br /&gt;&lt;br /&gt;The interesting question is of course whether you need a relational database if you can have the simpler object-oriented one. Interoperability issues aside, some people apparently seem to believe that the relational model is crucial for performance with huge amounts of data.&lt;br /&gt;&lt;br /&gt;It would seem to me, though, that the natural partitions which the object-oriented paradigm splits the data into would tend to make things faster since there's less data to search through. For instance, if you're only concerning yourself with data from one department in the company, then it's rather wasteful to work with the maybe ten times larger table containing data for all departments. Perhaps it has something to do with joins and restrictions being difficult to express simultaneously in one query with the object-oriented approach. There are some benchmarks on &lt;a href="http://www.polepos.org/"&gt;www.polepos.org&lt;/a&gt; although they are not exactly easy to interpret at a glance.&lt;br /&gt;&lt;br /&gt;Other people, like &lt;a href="http://developers.slashdot.org/comments.pl?sid=150578&amp;cid=12627032"&gt;this guy on Slashdot&lt;/a&gt;, seem to think that the abstract relational model makes for better &lt;span style="font-style:italic;"&gt;databases&lt;/span&gt; as opposed to &lt;span style="font-style:italic;"&gt;application-specific persistent storages&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;This makes me wonder whether many, perhaps most, uses of a relational database today wouldn't be better served by a simple object-oriented application-specific data store. I think most run-of-the-mill web sites need easy object persistence far more than terabyte scalability or an enterprise-wide database. Leave the heavy artillery for when it's really required.&lt;br /&gt;&lt;br /&gt;Then, on the other hand, if you can get easy object persistence with Django's ORM, maybe this is a moot point.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-9076990553383443225?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/9076990553383443225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/09/object-oriented-database-systems.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/9076990553383443225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/9076990553383443225'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/09/object-oriented-database-systems.html' title='Object-oriented database systems'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8637445446099069498</id><published>2007-09-06T09:52:00.000+02:00</published><updated>2007-09-06T11:17:49.133+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Django-rendertext 0.1 is out</title><content type='html'>We're releasing the first version of rendertext, a &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; app for making use of custom fonts on a web page!&lt;br /&gt;&lt;br /&gt;It's designed to work with the built-in Django template system by providing a simple template filter you can use to render images dynamically.&lt;br /&gt;&lt;br /&gt;The first showcase is &lt;a href="http://www.talentnord.dk/"&gt;Talent Nord&lt;/a&gt;. I hope to get a longer list as more people start using it. The design of Talent Nord is by &lt;a href="http://www.fa-mo.com"&gt;Fata Morgana&lt;/a&gt;, house of two intelligent designers.&lt;br /&gt;&lt;br /&gt;I think you need a pretty recent Django for the app to work. Here's the code that does the hard work:&lt;br /&gt;&lt;!-- Generator: GNU source-highlight 2.4&lt;br /&gt;by Lorenzo Bettini&lt;br /&gt;http://www.lorenzobettini.it&lt;br /&gt;http://www.gnu.org/software/src-highlite --&gt;&lt;br /&gt;&lt;pre style="font-size:xx-small;"&gt;&lt;b&gt;&lt;font color="#000080"&gt;import&lt;/font&gt;&lt;/b&gt; md5&lt;font color="#990000"&gt;,&lt;/font&gt; os&lt;br /&gt;&lt;b&gt;&lt;font color="#000080"&gt;import&lt;/font&gt;&lt;/b&gt; &lt;font color="#009900"&gt;Image&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#009900"&gt;ImageFont&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#009900"&gt;ImageDraw&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#009900"&gt;ImageColor&lt;/font&gt;&lt;br /&gt;&lt;b&gt;&lt;font color="#000080"&gt;from&lt;/font&gt;&lt;/b&gt; django&lt;font color="#990000"&gt;.&lt;/font&gt;conf &lt;b&gt;&lt;font color="#000080"&gt;import&lt;/font&gt;&lt;/b&gt; settings&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;font color="#0000FF"&gt;def&lt;/font&gt;&lt;/b&gt; &lt;b&gt;&lt;font color="#000000"&gt;render&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;text&lt;font color="#990000"&gt;,&lt;/font&gt; fontalias&lt;font color="#990000"&gt;,&lt;/font&gt; size &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#993399"&gt;12&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; color &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#FF0000"&gt;"#000"&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; rotation &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#993399"&gt;0&lt;/font&gt;&lt;font color="#990000"&gt;):&lt;/font&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    """Construct image from text.&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    Returns a tuple with the image file path, width and height. Takes&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    the parameters 'fontalias', 'size' (default 12), 'color' (default&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    black) as CSS hex string, and 'rotation' (default 0) which is the&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    degrees to rotate the text counter-clockwise. The constructed&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    image is stored in a cache under the media root in the&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    RENDERTEXT_DIR set in settings.py (default is 'rendertext/').&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    The font file to use is determined by mapping the fontalias&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    through the RENDERTEXT_FONTMAP setting, a dictionary from&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    fontalias strings to font file paths, e.g. {'verdana':&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    '/home/django/fonts/verdana.ttf'}."""&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;    &lt;i&gt;&lt;font color="#9A1900"&gt;# get settings&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;    render&lt;font color="#009900"&gt;_&lt;/font&gt;dir &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#FF0000"&gt;"rendertext/"&lt;/font&gt;&lt;br /&gt;    &lt;b&gt;&lt;font color="#0000FF"&gt;if&lt;/font&gt;&lt;/b&gt; &lt;b&gt;&lt;font color="#000000"&gt;hasattr&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;settings&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#FF0000"&gt;"RENDERTEXT_DIR"&lt;/font&gt;&lt;font color="#990000"&gt;):&lt;/font&gt;&lt;br /&gt;        render&lt;font color="#009900"&gt;_&lt;/font&gt;dir &lt;font color="#990000"&gt;=&lt;/font&gt; settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;RENDERTEXT_DIR&lt;/font&gt;&lt;br /&gt;    &lt;br /&gt;    fontmap &lt;font color="#990000"&gt;=&lt;/font&gt; settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;RENDERTEXT_FONTMAP&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;    fontfile &lt;font color="#990000"&gt;=&lt;/font&gt; fontmap&lt;font color="#990000"&gt;[&lt;/font&gt;fontalias&lt;font color="#990000"&gt;]&lt;/font&gt;&lt;br /&gt;    info &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#FF0000"&gt;"|"&lt;/font&gt;&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;join&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;([&lt;/font&gt;text&lt;font color="#990000"&gt;,&lt;/font&gt; fontalias&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;b&gt;&lt;font color="#000000"&gt;str&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;size&lt;font color="#990000"&gt;),&lt;/font&gt; &lt;b&gt;&lt;font color="#000000"&gt;str&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;color&lt;font color="#990000"&gt;),&lt;/font&gt; &lt;b&gt;&lt;font color="#000000"&gt;str&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;rotation&lt;font color="#990000"&gt;)])&lt;/font&gt;&lt;br /&gt;    name &lt;font color="#990000"&gt;=&lt;/font&gt; md5&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;new&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;info&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;encode&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;&lt;font color="#FF0000"&gt;'utf-8'&lt;/font&gt;&lt;font color="#990000"&gt;)).&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;hexdigest&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;()&lt;/font&gt;&lt;br /&gt;    filepath &lt;font color="#990000"&gt;=&lt;/font&gt; render&lt;font color="#009900"&gt;_&lt;/font&gt;dir &lt;font color="#990000"&gt;+&lt;/font&gt; name &lt;font color="#990000"&gt;+&lt;/font&gt; &lt;font color="#FF0000"&gt;".png"&lt;/font&gt;&lt;br /&gt;    &lt;br /&gt;    dim &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#990000"&gt;(-&lt;/font&gt;&lt;font color="#993399"&gt;1&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#990000"&gt;-&lt;/font&gt;&lt;font color="#993399"&gt;1&lt;/font&gt;&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;    &lt;b&gt;&lt;font color="#0000FF"&gt;if&lt;/font&gt;&lt;/b&gt; &lt;b&gt;&lt;font color="#0000FF"&gt;not&lt;/font&gt;&lt;/b&gt; os&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;access&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;MEDIA_ROOT&lt;/font&gt; &lt;font color="#990000"&gt;+&lt;/font&gt; filepath&lt;font color="#990000"&gt;,&lt;/font&gt; os&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;F_OK&lt;/font&gt;&lt;font color="#990000"&gt;):&lt;/font&gt;&lt;br /&gt;        &lt;i&gt;&lt;font color="#9A1900"&gt;# construct the image&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;        imf &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#009900"&gt;ImageFont&lt;/font&gt;&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;truetype&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;fontfile&lt;font color="#990000"&gt;,&lt;/font&gt; size&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;        dim &lt;font color="#990000"&gt;=&lt;/font&gt; imf&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;getsize&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;text&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;        im &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#009900"&gt;Image&lt;/font&gt;&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;new&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;&lt;font color="#FF0000"&gt;"RGBA"&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; dim&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#009900"&gt;ImageColor&lt;/font&gt;&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;getrgb&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;color&lt;font color="#990000"&gt;)&lt;/font&gt; &lt;font color="#990000"&gt;+&lt;/font&gt; &lt;font color="#990000"&gt;(&lt;/font&gt;&lt;font color="#993399"&gt;0&lt;/font&gt;&lt;font color="#990000"&gt;,))&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;        draw &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#009900"&gt;ImageDraw&lt;/font&gt;&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;Draw&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;im&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;        weird&lt;font color="#009900"&gt;_&lt;/font&gt;font&lt;font color="#009900"&gt;_&lt;/font&gt;renderer&lt;font color="#009900"&gt;_&lt;/font&gt;fix &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#FF0000"&gt;" "&lt;/font&gt;&lt;br /&gt;        draw&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;text&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;((&lt;/font&gt;&lt;font color="#993399"&gt;0&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#993399"&gt;0&lt;/font&gt;&lt;font color="#990000"&gt;),&lt;/font&gt; text &lt;font color="#990000"&gt;+&lt;/font&gt; weird&lt;font color="#009900"&gt;_&lt;/font&gt;font&lt;font color="#009900"&gt;_&lt;/font&gt;renderer&lt;font color="#009900"&gt;_&lt;/font&gt;fix&lt;font color="#990000"&gt;,&lt;/font&gt; font&lt;font color="#990000"&gt;=&lt;/font&gt;imf&lt;font color="#990000"&gt;,&lt;/font&gt; fill&lt;font color="#990000"&gt;=&lt;/font&gt;color&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;        &lt;b&gt;&lt;font color="#0000FF"&gt;if&lt;/font&gt;&lt;/b&gt; rotation &lt;font color="#990000"&gt;!=&lt;/font&gt; &lt;font color="#993399"&gt;0&lt;/font&gt;&lt;font color="#990000"&gt;:&lt;/font&gt;&lt;br /&gt;            im &lt;font color="#990000"&gt;=&lt;/font&gt; im&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;rotate&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;rotation&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#009900"&gt;Image&lt;/font&gt;&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;BICUBIC&lt;/font&gt;&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#009900"&gt;True&lt;/font&gt;&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;            dim &lt;font color="#990000"&gt;=&lt;/font&gt; im&lt;font color="#990000"&gt;.&lt;/font&gt;size&lt;br /&gt;&lt;br /&gt;        &lt;b&gt;&lt;font color="#0000FF"&gt;if&lt;/font&gt;&lt;/b&gt; &lt;b&gt;&lt;font color="#0000FF"&gt;not&lt;/font&gt;&lt;/b&gt; os&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;access&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;MEDIA_ROOT&lt;/font&gt; &lt;font color="#990000"&gt;+&lt;/font&gt; render&lt;font color="#009900"&gt;_&lt;/font&gt;dir&lt;font color="#990000"&gt;,&lt;/font&gt; os&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;F_OK&lt;/font&gt;&lt;font color="#990000"&gt;):&lt;/font&gt;&lt;br /&gt;            os&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;makedirs&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;MEDIA_ROOT&lt;/font&gt; &lt;font color="#990000"&gt;+&lt;/font&gt; render&lt;font color="#009900"&gt;_&lt;/font&gt;dir&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;        &lt;br /&gt;        im&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;save&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;MEDIA_ROOT&lt;/font&gt; &lt;font color="#990000"&gt;+&lt;/font&gt; filepath&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#FF0000"&gt;"PNG"&lt;/font&gt;&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;    &lt;b&gt;&lt;font color="#0000FF"&gt;else&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;:&lt;/font&gt;&lt;br /&gt;        &lt;i&gt;&lt;font color="#9A1900"&gt;# read width and height&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;        im &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#009900"&gt;Image&lt;/font&gt;&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;b&gt;&lt;font color="#000000"&gt;open&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;MEDIA_ROOT&lt;/font&gt; &lt;font color="#990000"&gt;+&lt;/font&gt; filepath&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;        dim &lt;font color="#990000"&gt;=&lt;/font&gt; im&lt;font color="#990000"&gt;.&lt;/font&gt;size&lt;br /&gt;&lt;br /&gt;    &lt;b&gt;&lt;font color="#0000FF"&gt;return&lt;/font&gt;&lt;/b&gt; &lt;font color="#990000"&gt;(&lt;/font&gt;settings&lt;font color="#990000"&gt;.&lt;/font&gt;&lt;font color="#009900"&gt;MEDIA_URL&lt;/font&gt; &lt;font color="#990000"&gt;+&lt;/font&gt; filepath&lt;font color="#990000"&gt;,&lt;/font&gt; dim&lt;font color="#990000"&gt;[&lt;/font&gt;&lt;font color="#993399"&gt;0&lt;/font&gt;&lt;font color="#990000"&gt;],&lt;/font&gt; dim&lt;font color="#990000"&gt;[&lt;/font&gt;&lt;font color="#993399"&gt;1&lt;/font&gt;&lt;font color="#990000"&gt;])&lt;/font&gt;&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;font color="#0000FF"&gt;def&lt;/font&gt;&lt;/b&gt; &lt;b&gt;&lt;font color="#000000"&gt;render_tag&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;text&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#990000"&gt;*&lt;/font&gt;args&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#990000"&gt;**&lt;/font&gt;kwargs&lt;font color="#990000"&gt;):&lt;/font&gt;&lt;br /&gt;&lt;i&gt;&lt;font color="#9A1900"&gt;    """Construct image tag from text."""&lt;/font&gt;&lt;/i&gt;&lt;br /&gt;    &lt;font color="#990000"&gt;(&lt;/font&gt;path&lt;font color="#990000"&gt;,&lt;/font&gt; width&lt;font color="#990000"&gt;,&lt;/font&gt; height&lt;font color="#990000"&gt;)&lt;/font&gt; &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;b&gt;&lt;font color="#000000"&gt;render&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;(&lt;/font&gt;text&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#990000"&gt;*&lt;/font&gt;args&lt;font color="#990000"&gt;,&lt;/font&gt; &lt;font color="#990000"&gt;**&lt;/font&gt;kwargs&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;    &lt;b&gt;&lt;font color="#0000FF"&gt;if&lt;/font&gt;&lt;/b&gt; &lt;b&gt;&lt;font color="#0000FF"&gt;not&lt;/font&gt;&lt;/b&gt; path&lt;font color="#990000"&gt;:&lt;/font&gt;&lt;br /&gt;        &lt;b&gt;&lt;font color="#0000FF"&gt;return&lt;/font&gt;&lt;/b&gt; &lt;font color="#FF0000"&gt;""&lt;/font&gt;&lt;br /&gt;    &lt;b&gt;&lt;font color="#0000FF"&gt;else&lt;/font&gt;&lt;/b&gt;&lt;font color="#990000"&gt;:&lt;/font&gt;&lt;br /&gt;        dim &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#FF0000"&gt;""&lt;/font&gt;&lt;br /&gt;        &lt;b&gt;&lt;font color="#0000FF"&gt;if&lt;/font&gt;&lt;/b&gt; width &lt;font color="#990000"&gt;&amp;gt;&lt;/font&gt; &lt;font color="#993399"&gt;0&lt;/font&gt; &lt;b&gt;&lt;font color="#0000FF"&gt;and&lt;/font&gt;&lt;/b&gt; height &lt;font color="#990000"&gt;&amp;gt;&lt;/font&gt; &lt;font color="#993399"&gt;0&lt;/font&gt;&lt;font color="#990000"&gt;:&lt;/font&gt;&lt;br /&gt;            dim &lt;font color="#990000"&gt;=&lt;/font&gt; &lt;font color="#FF0000"&gt;'width="%s" height="%s" '&lt;/font&gt; &lt;font color="#990000"&gt;%&lt;/font&gt; &lt;font color="#990000"&gt;(&lt;/font&gt;width&lt;font color="#990000"&gt;,&lt;/font&gt; height&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;            &lt;br /&gt;        &lt;b&gt;&lt;font color="#0000FF"&gt;return&lt;/font&gt;&lt;/b&gt; &lt;font color="#FF0000"&gt;'&amp;lt;img src="%s" alt="%s" %s/&amp;gt;'&lt;/font&gt; &lt;font color="#990000"&gt;%&lt;/font&gt; &lt;font color="#990000"&gt;(&lt;/font&gt;path&lt;font color="#990000"&gt;,&lt;/font&gt; text&lt;font color="#990000"&gt;,&lt;/font&gt; dim&lt;font color="#990000"&gt;)&lt;/font&gt;&lt;br /&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8637445446099069498?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8637445446099069498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/09/django-rendertext-01-is-out.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8637445446099069498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8637445446099069498'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/09/django-rendertext-01-is-out.html' title='Django-rendertext 0.1 is out'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-1052532800166960619</id><published>2007-07-27T15:50:00.000+02:00</published><updated>2007-09-23T18:57:30.111+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Groups and social software</title><content type='html'>From a link from Joel on Software is this essay on &lt;a href="http://www.shirky.com/writings/group_enemy.html"&gt;groups and social software&lt;/a&gt;. Lots of interesting points, e.g. that the group needs rules to prevent it from ruining itself, that social and technological issues are inseparable and that a core of members arise which should be nurtured.&lt;br /&gt;&lt;br /&gt;No posts for long time, I have been on vacation, away from computers. I've gotten through a good deal of books, all fictional. Not having read much fiction for the last year or so, I find it surprising that a book can still keep me occupied in bed in the morning so that breakfast turns into lunch and I spend most of the day on the couch reading.&lt;br /&gt;&lt;br /&gt;I've also been thinking. Mostly about life and what I like to spend my time on. Nothing major came out of it, though. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-1052532800166960619?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/1052532800166960619/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/07/groups-and-social-software.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1052532800166960619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1052532800166960619'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/07/groups-and-social-software.html' title='Groups and social software'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3417179956646394765</id><published>2007-06-22T14:39:00.000+02:00</published><updated>2007-07-25T11:23:26.986+02:00</updated><title type='text'>When you're smiling...</title><content type='html'>Greetings! They come in various flavours, depending on how well you know the person you're greeting and the time since the last greeting. I came across a funny one today.&lt;br /&gt;&lt;br /&gt;In descending order of warmness, there's the hug, the informal "hi!" or "hey!", the slight nod of the head, and then there's the funny smile. It's not a real smile. The real smile is reserved for people you can't say hi to because they're on the phone or for people you're flirting with.&lt;br /&gt;&lt;br /&gt;The funny smile is the kind of smile where you consciously try to move the corners of your mouth a little bit upwards while keeping the lips pressed together. The meaning is something like, "I see and acknowledge you but a real greeting is not warranted".&lt;br /&gt;&lt;br /&gt;The funny thing about it is the way it looks. I haven't thought about it before, but someone sent me such a funny smile while I was on my bicycle to work. And you surely appear to be sucking your lips into your mouth, like the archetypal toothless old witch.&lt;br /&gt;&lt;br /&gt;Think about that next time you funny-smile someone.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3417179956646394765?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3417179956646394765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/06/when-youre-smiling.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3417179956646394765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3417179956646394765'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/06/when-youre-smiling.html' title='When you&apos;re smiling...'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3494328675330126447</id><published>2007-06-02T15:04:00.000+02:00</published><updated>2007-06-02T18:25:40.014+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Software design, part I</title><content type='html'>What makes a good software design?&lt;br /&gt;&lt;br /&gt;I have been thinking about this for some time. The more I think about it, the simpler I come to think it is. Forget all the -tion, -ability, -ance words that are usually used to describe properties of good designs. In my mind a good design is one that is &lt;i&gt;easy to understand&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;As developers we think that ease of use/understanding is something that first and foremost applies to our users. We are used to horribly complicated IDEs, command lines that requires memorizing half a dictionary of cryptic commands, debuggers that speak assembler, etc. I argue that as a program grows from 50-100 lines of code to several thousands lines, the only thing that really matters is that you're not lost when you read and navigate it. And not whether you could squeeze a couple of lines out of it by some obscure technique you recently read about in a blog post.&lt;br /&gt;&lt;br /&gt;Why? Because you need to understand the code in order to extend it, to fix bugs, to avoid introducing errors, to be able to estimate effects of changes, and in a sort of self-referential way, to still keep the design ease to understand.&lt;br /&gt;&lt;br /&gt;In my mind, there are three goals with programming. In order of importance: to get the thing running the way it is intended to as fast as possible, to have as few bugs as possible, and to have as much fun as you can in the process. These are complementary - the faster you proceed, the more fun it usually is, at least if you are attracted to the creative dimension of programming. And the fewer bugs you have, the faster you can proceed. A non-existing defect that you don't have to try to reproduce, debug and then release a fix for is a real time-saver. Likewise, the more fun it is, the better you can concentrate and the faster you will go along.&lt;br /&gt;&lt;br /&gt;Easy-to-understand code generally supports all three of these goals. If the code is difficult to understand, it will slow down the process immensely. And it will increase the risk of subtle bugs of the kind where the logic is faulty in corner cases. Even a clever compiler can't help with you out of those. Finally, it is simply more fun to work with code you understand.&lt;br /&gt;&lt;br /&gt;How do you make a design easy to understand? I believe at the code level itself it is very much about using clear language and avoiding cryptic, non-descriptive names. Especially over time. If an identifier changes it meaning, even in a subtle way, so that its name is no longer descriptive, it's important to rename even if you have to change code all over the place.&lt;br /&gt;&lt;br /&gt;At the design level it's on one hand about keeping things together that belong together and on the other hand about splitting up the code into smaller chunks so that the reader is not overwhelmed. Two opposing forces.&lt;br /&gt;&lt;br /&gt;It's not that complicated really. To be able to understand something, it helps that the lines of code are sitting right next to each other. If you have to look them up in different places, you've lost your overview - and how do you know you found all the places unless the issue is easily greppable? But also, the piece of code needs to be small enough to fit in your brain, else you have to break it down yourself through analysis which is time-consuming and takes away the focus on what you really are trying to accomplish.&lt;br /&gt;&lt;br /&gt;Keep things together that belong together. If you are designing a database application, don't put the logic that decides how the screen widgets should look like in the code that computes stuff from data received from the database. Likewise, don't put code that performs complicated computations in GUI code which is already busy with presenting an intuitive interface that prevents people from doing unnecessary damage to their data.&lt;br /&gt;&lt;br /&gt;When you program, violations of this principle might be easy to see if you look at the dependencies. So the database code had to explicitly import a GUI module? Maybe it has more concerns than the database itself then. But sometimes violations are more difficult to spot. I think a main culprit here is the decision making.&lt;br /&gt;&lt;br /&gt;Code is all about making decisions. In fact a computer program is at heart just a long series of decisions - first I'll do this, then I'll do that, then if this happens, I'll do this again. Most of these decisions are not important decisions, however, more like minor tweaks. It's not easy to see this in the code itself. One statement looks as good as the other, even though the first might decide what the user is going to look at and the other just adds a 2 pixel space around the border.&lt;br /&gt;&lt;br /&gt;But what you usually want to do is to keep decisions about the same kind of things together. Then it's easy to understand. What fundamental things does the user see on screen? The answer should be along the lines of, take a look at this piece of code and you can see all of it. Not, well, grep for XYZ in these 40 source files. That's why lowly GUI code which is often preoccupied with tweaking spacing and interactive behaviour shouldn't be allowed to do much on its own, but immediately transfer control to somewhere else when anything less than trivial is to be done.&lt;br /&gt;&lt;br /&gt;Since the programming language does not directly help with arranging the decision making in a sensible way, you should explicitly take it into account in the design, e.g. by forbidding certain parts of the code to do certain things or by designing modules that are explicitly made responsible for nothing more than high-level decision making. Or by arranging a calling structure that makes it easier to delegate decisions to upper management, e.g. by means of signals/callbacks/hooks/events/delegates.&lt;br /&gt;&lt;br /&gt;Sometimes it means more hassle to follow this principle. But no pain, no gain. In most situations the long-term consequences of understandability are much more important than whether it takes an extra minute to code.&lt;br /&gt;&lt;br /&gt;I have much more to say about this, but for now I'll conclude with this remark: When you start working on a new aspect of program, one that you haven't worked on before in another project so that you have a design already, it's easy to become irresolute, to get stuck on how to proceed in the best possible way. But if you simply design to keep things together that belong together while splitting the code into chunks that are small enough to be understood, it's probably going to be a healthy design and the details are less important. So just go ahead and see what happens.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3494328675330126447?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3494328675330126447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/06/software-design-part-i.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3494328675330126447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3494328675330126447'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/06/software-design-part-i.html' title='Software design, part I'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-1148978622176015706</id><published>2007-05-22T19:52:00.000+02:00</published><updated>2007-05-22T20:05:45.888+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>How not to do management</title><content type='html'>Joel on &lt;a href="http://www.joelonsoftware.com/items/2006/08/09.html"&gt;why simple economics is not a good way to manage other people&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-1148978622176015706?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/1148978622176015706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/how-not-to-do-management.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1148978622176015706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1148978622176015706'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/how-not-to-do-management.html' title='How not to do management'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6960038208535213029</id><published>2007-05-22T19:06:00.001+02:00</published><updated>2007-05-22T19:08:51.222+02:00</updated><title type='text'>Labeling</title><content type='html'>Labeling is &lt;a href="http://xkcd.com/c262.html"&gt;the new, Web 2.0-enabled way&lt;/a&gt; of organising information...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6960038208535213029?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6960038208535213029/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/labeling.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6960038208535213029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6960038208535213029'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/labeling.html' title='Labeling'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5076997333976702498</id><published>2007-05-20T11:51:00.000+02:00</published><updated>2007-05-20T13:00:16.014+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>Human rights</title><content type='html'>I received the latest issue of &lt;a href="http://amnesty.dk/"&gt;Amnesty&lt;/a&gt;'s Danish member magazine. A horrible and moving reading as usual. Hit by a cluestick: hey, there's a world around you!&lt;br /&gt;&lt;br /&gt;The theme this time was Iraqi refugees. It didn't even go into much detail about the two million people who have left the country (&lt;i&gt;two million people!&lt;/i&gt;), but instead explained how the Danish authorities are detaining the 519 who have come to Denmark in 2006. 47 of them have been let through and can restart their life.&lt;br /&gt;&lt;br /&gt;The remaining are left in limbo. They have nothing to do, they are not allowed to get a job and do some meaningful work, their food is prepared to them so they can't choose what to eat, clothes and other personal stuff is only available at a local shop that operates by giving each refugee a number of points they are allowed to spend. According to one of them who has been living in the camp through her teenage years it's like living on a train station. Imagine growing up on a train station with nothing to do.&lt;br /&gt;&lt;br /&gt;Meanwhile Sweden is letting 80% pass through. And Sweden didn't start the war.&lt;br /&gt;&lt;br /&gt;The problem in Denmark appears to be the rule set. Imagine trying to codify compassion. It's a human trait, and an important one as well, maybe the one that makes us so successful compared to other species. But what is compassion anyway? How do you explain it in terms that a bureaucrat can understand and manage?&lt;br /&gt;&lt;br /&gt;Apparently, the rule set the Danish authorities apply for accepting refugees requires that there is a personal element of prosecution. If your house has just collapsed from an American bomb and there are fightings in the street, is that personal? Maybe not. Do you have to move? Yes. And if you have the responsibility of taking care of others, if you have children, then you have a damn obligation to get away as fast as possible.&lt;br /&gt;&lt;br /&gt;So the system is rotten. There are some hints for fixes in the magazine. The Danish association of doctors wants to examine the people living the in the camps. If they are getting provably ill in a medical sense, then they can declare the camps medically irresponsible.&lt;br /&gt;&lt;br /&gt;Fixing the system itself is unfortunately not going to happen soon. That requires going to the politicians in charge - which in turns with the current aliens-generally-smell-rob-our-homes-and-rape-our-children political climate requires that the people starts changing it's collective mind. Appealing to the politicians themselves is not going to work.&lt;br /&gt;&lt;br /&gt;So how do you change the mind of the people? I think the standard news stuff like "bomb in Iraq, 40 people killed" and then you see a smashed ruin covered with dust is part of the problem rather than of the solution because it gets people used to the sufferings. They start thinking, 40 persons killed, that's not so bad, it happened yesterday too.&lt;br /&gt;&lt;br /&gt;I think we need more documentaries and even fictional movies about how it's like to having your house bombed and then come to Denmark and live at a train station with no freedom and lots of spare time for 5 years.&lt;br /&gt;&lt;br /&gt;Oh, I almost forgot. The magazine also had an interview with Hans Blix, the former chief of the UN's weapons inspectors in Iraq. The US federal government wanted the war so badly that they even fabricated evidence (or let evidence be fabricated) about the famous non-existing weapons of mass destruction to convince people... Now two million Iraqi people have fled their homes and country. &lt;br /&gt;&lt;br /&gt;Imagine being responsible for that. Of course the issue is more complex than that, but still. &lt;i&gt;Two million people!&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5076997333976702498?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5076997333976702498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/human-rights.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5076997333976702498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5076997333976702498'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/human-rights.html' title='Human rights'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8281970199195335047</id><published>2007-05-07T22:34:00.000+02:00</published><updated>2007-05-07T22:45:03.475+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Algorithms</title><content type='html'>Just today I implemented &lt;a href="http://en.wikipedia.org/wiki/Bubble_sort"&gt;bubble sort&lt;/a&gt; in Javascript to speed up the inner loop of a search algorithm. I think that's the first time I've actually implemented a sorting algorithm for real in spite of having read pages and pages about them. It's just one of the things that all real programming environments solved long before my time.&lt;br /&gt;&lt;br /&gt;Of course, right after having implemented the bubble sort thing, I read on Wikipedia that it's actually considered inferior to &lt;a href="http://en.wikipedia.org/wiki/Insertion_sort"&gt;insertion sort&lt;/a&gt;. So I then switched to insertion sort (I'm only sorting short sequences so asymptotic performance is irrelevant). It's my lucky day, two classic algorithms in such a short span of time...&lt;br /&gt;&lt;br /&gt;The simple optimisation work did improve the running time by more than 400%, so that's cool.&lt;br /&gt;&lt;br /&gt;When I think of algorithms I invariably come to think of the &lt;a href="http://en.wikipedia.org/wiki/Knuth_Morris_Pratt_algorithm"&gt;Knuth–Morris–Pratt algorithm&lt;/a&gt;, an algorithm for searching for occurrences of a search phrase in a larger text. A very famous string searching algorithm because it seems clever in theory and with toy examples. Your standard text book on algorithms probably does not mention this, but in practice it is not really an improvement over the simplest possible algorithm, because the issue it is trying to optimise, repeated examinations of the same character, almost never occurs when searching in natural language texts. &lt;br /&gt;&lt;br /&gt;I found this out as an undergraduate when we were curious how KMP and the Boyer-Moore algorithm would stack up in practice. We discovered that KMP was only a 1% improvement in the number of characters compared, compared to the simple algorithm.&lt;br /&gt;&lt;br /&gt;It's probably a great algorithm for searching DNA strings, though.&lt;br /&gt;&lt;br /&gt;I added a note to the &lt;a href="http://en.wikipedia.org/wiki/Knuth_Morris_Pratt_algorithm"&gt;Wikipedia page&lt;/a&gt;. If I should die tomorrow, please consider it my contribution to the field of computer science.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8281970199195335047?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8281970199195335047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/just-today-i-implemented-bubble-sort-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8281970199195335047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8281970199195335047'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/just-today-i-implemented-bubble-sort-in.html' title='Algorithms'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-252338707458766470</id><published>2007-05-07T21:02:00.000+02:00</published><updated>2007-05-07T22:33:52.613+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Update</title><content type='html'>Long time, no post. I finished reading &lt;i&gt;Naked Economics&lt;/i&gt;, it didn't take me more than a week. I still highly recommend it. It did have lots of interesting points on globalisation.&lt;br /&gt;&lt;br /&gt;One is the notion of human capital. From a macro-economic point of view, one of the best thing a country can do is to invest in its people - education, health-care and the like. The return of investment as measured in cold cash is worth it.&lt;br /&gt;&lt;br /&gt;One example of this is the fact that countries that are rich in natural resources don't fare particularly well in the long run - because they haven't had to invest in human capital. Another example is a research facility of well-educated engineers and scientists that has to close down. A local disaster in the short run, but the engineers and scientists are highly motivated individuals that soon find something else to do. If the facility is not simply bought by a large corporation that has no other interest in the facility than its human capital.&lt;br /&gt;&lt;br /&gt;Another point concerns one of the dark sides of capitalism. Creative destruction - those who cannot compete cannot survive. Instead of fighting this built-in ground rule, the author suggests that the transition period be made less severe by retraining the involved workers and support from the authorities. Focusing on the future instead of the past.&lt;br /&gt;&lt;br /&gt;From the point of view of the Far East sweatshops, the author notes that the people working in these shops would not be there if it weren't for the fact that they are better than the alternatives. I think that's simplifying things (*ahem*, did anyone mention the word "exploitation"?), but nevertheless an interesting point to ponder.&lt;br /&gt;&lt;br /&gt;In other news, I upgraded to the new &lt;a href="http://www.ubuntulinux.com"&gt;Ubuntu&lt;/a&gt; version at work. More incremental improvements, as always.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-252338707458766470?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/252338707458766470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/long-time-no-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/252338707458766470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/252338707458766470'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/05/long-time-no-post.html' title='Update'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-488972561088665285</id><published>2007-04-13T10:34:00.000+02:00</published><updated>2007-05-07T21:08:18.287+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Naked Economics</title><content type='html'>I started on &lt;i&gt;Naked Economics&lt;/i&gt; by Charles Wheelan which is about how basic economic stuff shape our society and make the wheels go around. We had some basic economic theory in high school in the history classes, so I didn't hold my hopes too high but I've already learned some profound insights after having read the first chapter. It's a great book.&lt;br /&gt;&lt;br /&gt;One of the first thing he writes about is the market and pricing system as a decentralised system for distributing goods. Think about it.&lt;br /&gt;&lt;br /&gt;Having worked a couple of years on decentralised large-scale software systems for the internet, it makes a lot of sense. Centralised software systems have many scaling issues. They easily break down or are very inefficient, or both. But it's not exactly easy to construct a large-scale decentralised software system that is sustainable in the long run either. And software, as complex as it is, is nowhere near the complexity of a human society.&lt;br /&gt;&lt;br /&gt;The book also points out flaws of capitalism and how government tries (or should try) to amend those, e.g. through taxes on goods that harm the environment and thus incurs hidden costs to all of us. I'm looking forward to some well-founded thoughts on globalisation.&lt;br /&gt;&lt;br /&gt;So far the worst thing about the book has been the foreword by some other guy (I've never heard of him, but he's probably famous). The book would actually have been better off without it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-488972561088665285?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/488972561088665285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/04/naked-economics.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/488972561088665285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/488972561088665285'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/04/naked-economics.html' title='Naked Economics'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-969220310277771348</id><published>2007-04-11T20:28:00.000+02:00</published><updated>2007-04-13T10:55:00.295+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>Battle scene revisited</title><content type='html'>I had a look at the &lt;a href="http://ole-laursen.blogspot.com/2007/04/duel.html"&gt;battle scene of yesterday&lt;/a&gt;. I can see why the youngster was cycling in the middle of the bicycle lane, with the downhill corner and a thorny rose plant leaning over the lane. &lt;br /&gt;&lt;br /&gt;But he made two mistakes. He didn't compensate for the fact that he had a sidecar mounted on his bike, leaving too little space for me to get past him unless I drove over the edge of the lane. And with such a dangerous path he failed to pay enough attention to whether someone was coming round the corner. I hope for him that he didn't bang his head so hard in the lane that he suffered a concussion. My little finger is surely still hurting.&lt;br /&gt;&lt;br /&gt;In other news I watched Al Gore speak in &lt;i&gt;An Inconvenient Truth&lt;/i&gt; today at NOVI. Disturbing non-news. I am still determined to buy a windmill if I ever get rich. A local upstart called &lt;a href="http://www.climaid.dk"&gt;ClimAid&lt;/a&gt; handed out adverts at the event, they appear to be selling CO2 reductions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-969220310277771348?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/969220310277771348/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/04/battle-scene-revisited.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/969220310277771348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/969220310277771348'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/04/battle-scene-revisited.html' title='Battle scene revisited'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2869374981945076733</id><published>2007-04-10T20:18:00.000+02:00</published><updated>2007-04-10T22:35:36.701+02:00</updated><title type='text'>The duel</title><content type='html'>What do a young computer scientist and a medieval knight have in common?&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.the-toy-soldier.com/images/knights2.jpg"&gt;&lt;img style="display:block; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px;" src="http://www.the-toy-soldier.com/images/knights2.jpg" border="0" alt="" /&gt;&lt;/a&gt;They both have to duel to get to work.&lt;br /&gt;&lt;br /&gt;I dueled today on my way to work at lunch time. A malefactor rounded the corner downhill just before the tunnel I was parting through. I initially thought we would be able to pass each other without dispute, but no, he went for it with his sidecar. A big clang, my steed was forced to its knees and we were both down in the mud. My opponent hit the trodden road behind me. I landed on my hands, gauntlets taking most of the shock but little finger crushed, though not broken, then got on my feet to witness his fate.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.tourcart.net/tourmate/img/tours/7166-1.jpg"&gt;&lt;img style="display:block; margin:0 10px 10px 0;cursor:pointer; cursor:hand; width: 200px;" src="http://www.tourcart.net/tourmate/img/tours/7166-1.jpg" border="0" alt="" /&gt;&lt;/a&gt;He did not came back for another round, instead he simply glared at me, still seated on his charger on the ground, blood trickling from his face. A fair lady ran to his help; yet another tender champion ready for the hospital. Pity his ill fate. I turned my attention to my steed, its neck turned around, mud in the ear, but otherwise virtually unharmed. Checking up on my opponent again, I eventually left the scene of battle.&lt;br /&gt;&lt;br /&gt;No young upstart shall bring a seasoned warrior down for good. A duel was fought, blood was spilled, but in the end we both survived, without permanent injury I believe.&lt;br /&gt;&lt;br /&gt;But I don't think I'll be able to dig the rest of the garden the next couple of days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2869374981945076733?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2869374981945076733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/04/duel.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2869374981945076733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2869374981945076733'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/04/duel.html' title='The duel'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-322076833735973440</id><published>2007-03-25T23:59:00.001+02:00</published><updated>2009-03-08T13:05:53.061+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='photo'/><title type='text'>Camera batteries</title><content type='html'>I've been looking for new batteries for Janne's digital camera. We're getting tired of buying expensive Duracells. The cheap batteries go dead almost immediately.&lt;br /&gt;&lt;br /&gt;After having read the Wikipedia articles on batteries and discovering that lithium-ion AA-compatible batteries do not appear to exist (update: I later found some on &lt;a href="http://www.batteribyen.dk"&gt;batteribyen.dk&lt;/a&gt;, but unfortunately not rechargeable), I was going to buy Sanyo 2700 mAh rechargeable batteries from batteribyen.dk. The shop owner recommended Sanyo Eneloop, however. They are supposed to be able to keep their energy for longer. But they are only rated for 2000 mAh...&lt;br /&gt;&lt;br /&gt;But then I found &lt;a href="http://www.epinions.com/content_309637189252"&gt;this page&lt;/a&gt; (which in turn references &lt;a href="http://www.users.on.net/~mhains/Reviews.html"&gt;this test&lt;/a&gt;, I also found &lt;a href="http://www.imaging-resource.com/ACCS/BATTS/BATTS.HTM"&gt;another big test&lt;/a&gt;). Apparently some cameras are very picky with the voltage they get from the battery, turning off at the point most batteries deliver the bulk of their energy at.&lt;br /&gt;&lt;br /&gt;But not the Eneloops. So I'm going to be buying a set of those with a charger.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-322076833735973440?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/322076833735973440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/camera-batteries.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/322076833735973440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/322076833735973440'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/camera-batteries.html' title='Camera batteries'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5659937698014848056</id><published>2007-03-23T21:33:00.000+01:00</published><updated>2007-03-23T22:14:19.454+01:00</updated><title type='text'>The small things</title><content type='html'>Still settling in at our new home. It's funny how small things can make a big difference.&lt;br /&gt;&lt;br /&gt;Like the lighting. We've bought and put up a couple of lamps. Transforming the apartment from a grave or a scene in a detective story in the evenings. Actually I have already formed a habit not to press the light switches since they didn't work. Will have to unlearn that.&lt;br /&gt;&lt;br /&gt;Or the stove and cooker hood. The stove is quick to warm up and easy to clean. And the hood doesn't make much noise while actually sucking pretty hard. The one at the old place sucked a lot too, but not in the sense you want it to.&lt;br /&gt;&lt;br /&gt;Or the fridge, kindly borrowed from Janne's sister, which has one large instead of two small vegetable drawers, meaning that it &lt;span style="font-style:italic;"&gt;can&lt;/span&gt; store cucumbers, probably the second most common vegetable after tomatoes in Denmark.&lt;br /&gt;&lt;br /&gt;Or the wall-mounted things that I haven't had the time to put up before now. Changed the setting from construction site to something closer to a home.&lt;br /&gt;&lt;br /&gt;Or the fact that the two phalaenopsis orchids which have bloomed the last few months are still carrying their flowers while the two others decided to bloom almost simultaneously. So we now have four blooming phalaenopsis in a row at the counter between the kitchen and the living room. Pictures soon, I promise.&lt;br /&gt;&lt;br /&gt;Still on the todo list is digging the garden. Yesterday I put felt feet under the piano, in attempt to reduce floor vibrations, hopefully transmitting less sound to the neighbours. Tilting the piano was like lifting an elephant.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5659937698014848056?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5659937698014848056/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/small-things.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5659937698014848056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5659937698014848056'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/small-things.html' title='The small things'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-187676337710525262</id><published>2007-03-16T12:06:00.000+01:00</published><updated>2007-06-02T18:31:48.402+02:00</updated><title type='text'>Efficient work force</title><content type='html'>Janne mentioned yesterday that the head of a recycling company was eager to hire former farmers. Over the last couple of decades a lot of farmers in Denmark have had to stop because small farms cannot produce enough surplus to give a decent income. They used to large or medium farms, now they're simply too small. So many farmers have had to find a job.&lt;br /&gt;&lt;br /&gt;A similar wave hit the country when industrialisation started kicking in and women in large numbers began joining the work force. I remember reading about egg-packers in a factory in literature classes in high school. The point is that those women were also valued because they were used to work for themselves and thus act responsibly and conscientious.&lt;br /&gt;&lt;br /&gt;I think there's a parallel here to open source developers who have run and nurtured their own projects. It shows that they care.&lt;br /&gt;&lt;br /&gt;Whether there really is a difference, I don't know. I haven't met that many non-open source developers and in any case I'd think that discipline is much more important in non-creative factory work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-187676337710525262?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/187676337710525262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/efficient-work-force.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/187676337710525262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/187676337710525262'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/efficient-work-force.html' title='Efficient work force'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3737332229636720580</id><published>2007-03-12T12:44:00.000+01:00</published><updated>2007-03-12T12:50:42.530+01:00</updated><title type='text'>Finally moved</title><content type='html'>So we moved the remaining things Saturday and are now officially living at the new place. We were pretty bombed Sunday but managed to buy some flower bulbs and a small raspberry bush to put in the garden. I'll post pictures soon. It's nice with the extra space, although the bed got stuck in the staircase a couple of times - we barely managed to squeeze it through.&lt;br /&gt;&lt;br /&gt;We still need to do some cleaning and painting in the old place.&lt;br /&gt;&lt;br /&gt;Also the internet connection arrived today (5Mbit/256 kbit ADSL2) right on schedule. So far it's working fine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3737332229636720580?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3737332229636720580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/finally-moved.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3737332229636720580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3737332229636720580'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/finally-moved.html' title='Finally moved'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-102917216640149809</id><published>2007-03-08T10:25:00.000+01:00</published><updated>2007-03-28T18:00:03.489+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>AJAX</title><content type='html'>I started looking into &lt;a href="http://en.wikipedia.org/wiki/AJAX"&gt;AJAX&lt;/a&gt;. We want to use AJAX for doing searches for client.&lt;br /&gt;&lt;br /&gt;Turns out it is much simpler than I thought, at least if you have worked with asynchronous programming, callbacks and closures before. First thing to note is that it's a good idea to use a library to do most of the Javascript stuff because of browser incompatibilities. I digged up &lt;a href="http://jquery.com"&gt;jQuery&lt;/a&gt; which seems sensible. I suggest starting with &lt;a href="http://docs.jquery.com/Tutorials:Getting_Started_with_jQuery"&gt;this tutorial&lt;/a&gt; from the &lt;a href="http://docs.jquery.com/Main_Page"&gt;documentation page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There's really not much more in it than a few lines of simple Javascript code (because of the library) on the client side. You register a callback and can inject things into the page when you get data. On the server-side is a standard PHP/Django/ASP/whatever script that parses GET or POST parameters and returns an XML string. It's even simpler than normal form processing because you just return the data you need on the client side instead of a whole page.&lt;br /&gt;&lt;br /&gt;There are also a couple of links to tutorials on the Wikipedia page on AJAX to understand what jQuery is doing under the hood.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-102917216640149809?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/102917216640149809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/ajax.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/102917216640149809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/102917216640149809'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/ajax.html' title='AJAX'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6072160128154617642</id><published>2007-03-08T10:12:00.000+01:00</published><updated>2007-03-08T10:25:38.147+01:00</updated><title type='text'>Moving</title><content type='html'>We started moving our stuff last weekend to &lt;a href="http://maps.google.com/?ie=UTF8&amp;z=15&amp;ll=57.027746,9.992323&amp;spn=0.014831,0.034761&amp;t=h&amp;om=1"&gt;Vilsundvej&lt;/a&gt;. Yesterday the piano was moved. I'm relieved it's done, but it was a sad moment. The acustics in the empty living room with its hard walls and wooden floor was just perfect for playing Bach.&lt;br /&gt;&lt;br /&gt;This weekend we'll move the things we use daily, completing the journey - we still live in the old apartment. I took some pictures, will post them later.&lt;br /&gt;&lt;br /&gt;Then it's just a question of getting the old apartment cleaned up and repainted.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6072160128154617642?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6072160128154617642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/moving.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6072160128154617642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6072160128154617642'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/03/moving.html' title='Moving'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6961083003446183660</id><published>2007-02-28T09:55:00.000+01:00</published><updated>2007-02-28T10:31:17.637+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Fantasy/science fiction books</title><content type='html'>I just finished reading the Fionavar Tapestry by Guy Gavriel Kay. Borrowed the three books in one cover at the local library, in a Danish translation. I enjoyed the read, although the first book started slow. It shows that it was his first. The writing in the later books is much, much better.&lt;br /&gt;&lt;br /&gt;Other great books I would recommend are the Hyperion series by Dan Simmons, and in similar vein the Riverworld series by Philip José Farmer. And the Foundation series by Isaac Asimov. And how could I not mention Ender's Game by Orson Scott Card.&lt;br /&gt;&lt;br /&gt;I recently read the Earthsea series by Ursula K. Le Guin series. A bit boring at times, but otherwise good. They feel different than most fantasy/science fiction books I've read. Perhaps the gender of the author shows.&lt;br /&gt;&lt;br /&gt;I have also read the series by David Eddings, they are not exactly great works of art. But they do their job, exciting.&lt;br /&gt;&lt;br /&gt;I recently reread Midworld by Alan Dean Foster (it's called Grøn = Green in Danish, I didn't even know it was part of a series). I remembered it as a favourite, but was disappointed when I read it again. Too naive. I think I've had too much scientific training. It's like the Dragonlance books I used to read and really enjoy when I was young, at about the age of 15 they were simply unbearable.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6961083003446183660?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6961083003446183660/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/fantasyscience-fiction-books.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6961083003446183660'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6961083003446183660'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/fantasyscience-fiction-books.html' title='Fantasy/science fiction books'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6484035784092675142</id><published>2007-02-25T23:19:00.000+01:00</published><updated>2007-02-25T23:29:22.998+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>How to dress</title><content type='html'>Some &lt;a href="http://www.fashion4nerds.com"&gt;tips for dressing&lt;/a&gt;, if you're in the trade.&lt;br /&gt;&lt;br /&gt;I already knew most of them, of course. Janne told me.&lt;br /&gt;&lt;br /&gt;Reminds of &lt;a href="http://thingsmygirlfriendandihavearguedabout.com/"&gt;thingsmygirlfriendandihavearguedabout.com&lt;/a&gt;, probably the longest domain name ever registered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6484035784092675142?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6484035784092675142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/how-to-dress.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6484035784092675142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6484035784092675142'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/how-to-dress.html' title='How to dress'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-9201276058067088427</id><published>2007-02-22T10:18:00.000+01:00</published><updated>2007-06-02T18:34:49.604+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>The secret of success in business</title><content type='html'>&lt;a href="http://headrush.typepad.com/photos/uncategorized/loveocracy.jpg"&gt;This illustration&lt;/a&gt; is just hilarious.&lt;br /&gt;&lt;br /&gt;While I'm at it, I stumbled upon a &lt;a href="http://www.euroorchid.com/"&gt;webshop with orchids&lt;/a&gt;. It's short, pretty and to the point. Great example of how to construct a webshop.&lt;br /&gt;&lt;br /&gt;And here's &lt;a href="http://www.glasshaus.com/samplechapters/1159/default.asp"&gt;how to choose a colour scheme&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-9201276058067088427?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/9201276058067088427/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/secret-of-success-in-business.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/9201276058067088427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/9201276058067088427'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/secret-of-success-in-business.html' title='The secret of success in business'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4037165275073494844</id><published>2007-02-19T21:03:00.000+01:00</published><updated>2007-02-21T19:40:27.803+01:00</updated><title type='text'>Success!</title><content type='html'>The &lt;a href="http://ole-laursen.blogspot.com/2007/01/gambling.html"&gt;local newspaper I've mentioned before&lt;/a&gt; called us 8 o'clock this morning. We won an iPod Nano, amongst 34 correct solutions!&lt;br /&gt;&lt;br /&gt;This was the fourth week we participated, with the number of correct solutions so far being 17, 27, 46 (that week it was particularly easy) and 34. Thus the probability of winning one iPod has been 1/17 + 16/17 * 1/27 + 16/17 * 26/27 * 1/46 + 16/17 * 26/27 * 45/46 * 1/34 = 14%. I guess we were lucky.&lt;br /&gt;&lt;br /&gt;There's one problem, though. All my ripped music is encoded in Vorbis. I definitely don't want to store my data in a proprietary format that Apple is controlling. I'll have to take a look at &lt;a href="http://www.rockbox.org/"&gt;Rockbox&lt;/a&gt;, although it seems the 2nd generation iPod Nano is not supported. Sigh.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4037165275073494844?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4037165275073494844/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/success.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4037165275073494844'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4037165275073494844'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/success.html' title='Success!'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4975059131253310871</id><published>2007-02-06T14:32:00.000+01:00</published><updated>2007-02-06T15:45:05.405+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Frustrated users</title><content type='html'>Spent some time with &lt;a href="http://cgis.cs.umd.edu/localphp/hcil/tech-reports-search.php?number=2004-12"&gt;a link&lt;/a&gt; I got from the blog of Federico Mena-Quintero. Apparently, people get frustrated often with the programs they use most (word processing comes out top) and waste on average about 40% of their time.&lt;br /&gt;&lt;br /&gt;Some of the suggestions in the paper are simply to rephrase unclear wording and work on the error messages.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://cgis.cs.umd.edu/localphp/hcil/tech-reports-search.php?number=2004-05"&gt;Another paper&lt;/a&gt; suggests an adaptable file browser. Their concept is pretty simple - replace seldomly used folders with an ellipsis. Easily generalised.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4975059131253310871?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4975059131253310871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/frustrated-users.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4975059131253310871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4975059131253310871'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/frustrated-users.html' title='Frustrated users'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-1233332615241325809</id><published>2007-02-04T17:50:00.000+01:00</published><updated>2007-02-06T15:44:52.339+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>REST, web speak, architectures</title><content type='html'>Why are new technologies on the web so slang infested? I found a reference to REST which I thought from earlier impressions was an architectural style for building web applications. Apparently, it is more complicated than that. Researching the matter is difficult, the usual ACRONYM gabble-gabble, future gobble-gobble.&lt;br /&gt;&lt;br /&gt;So I &lt;a href="http://en.wikipedia.org/wiki/REST"&gt;looked it up on Wikipedia&lt;/a&gt; and found this little gem, out of the blue:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-style: italic;"&gt;Systems that follow Fielding's REST principles are often referred to as RESTful; REST's most zealous advocates call themselves RESTafarian.&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;Rereading Wikipedia's summary the ideas in REST begin to make sense: build systems with URLs and send-command/return-contents protocols that are stateless and cacheable, instead of the usual remote procedure call/method invocation paradigm.&lt;br /&gt;&lt;br /&gt;An interesting aspect of XML-RPC and SOAP is that you take a flexible format like XML that can easily support forwards and backwards compatibility and force a strict RPC interpretation down on top of it (if I understand things correctly, admittedly I'm no expert on web services). It may be easier than parsing the XML itself, but it is a poor man's version of a protocol. And why use XML when you're not really exploiting it anyway? Certainly not of performance reasons...&lt;br /&gt;&lt;br /&gt;I have a feeling the requirement of a stateless protocol is a problem in many circumstances, though, having spent some time working on distributed file systems. Sometimes it's just better if the server can send a notification back to a client instead of the client having to poll. I believe NFS has left its much touted stateless approach with version 4.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-1233332615241325809?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/1233332615241325809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/rest-web-speak-architectures.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1233332615241325809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1233332615241325809'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/rest-web-speak-architectures.html' title='REST, web speak, architectures'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2526515623512906934</id><published>2007-02-03T18:09:00.001+01:00</published><updated>2010-11-16T11:49:52.231+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Harpsichord</title><content type='html'>My mother has bought the scores for the chromatic fantasia and fuga by J. S. Bach (BWV 903) for me, apparently mostly because she heard it in the radio, liked it and then the scores suddenly were there in the book shop.&lt;br /&gt;&lt;br /&gt;So I have been trying to find a free recording of the piece on the web. I don't have time to study it right now, but I need to know where to put it in the ever-growing stack of pieces I'm planning to work on in the future. No real luck yet, although I managed to find a very nice &lt;a href="http://www.baroquemusiclibrary.com/"&gt;sample on harpsichord&lt;/a&gt; by George Malcolm (a famous harpsichord player). Still looking for a piano recording, although I suspect I will have to visit the library or spend some money on it.&lt;br /&gt;&lt;br /&gt;I ended up on &lt;a href="http://www.magnatune.com"&gt;Magnatune&lt;/a&gt;. Their collection of classical music has grown quite a lot. Apparently about 30% of their sales are for classical music, no wonder, they have some real gems in there. I'm currently listening to the &lt;a href="http://magnatune.com/artists/albums/kperl-french/hifi_play"&gt;French Suites&lt;/a&gt; after having enjoyed &lt;a href="http://magnatune.com/artists/albums/mcarlin-rameau/hifi_play"&gt;Rameau&lt;/a&gt; for a moment. There's nothing like harpsichord solos to bring forth the baroque mood. Tiring after while, unfortunately, I suspect because of the lack of dynamics. That's one point a Yamaha keyboard has going for it. You can play harpsichord with graduated touches.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2526515623512906934?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2526515623512906934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/harpsichord.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2526515623512906934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2526515623512906934'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/harpsichord.html' title='Harpsichord'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-454845854998560483</id><published>2007-02-02T22:39:00.000+01:00</published><updated>2007-02-02T23:11:22.313+01:00</updated><title type='text'>Feed reader</title><content type='html'>I finally succumbed and started using an RSS feed reader. I'm trying &lt;a href="http://www.google.com/reader/"&gt;Google Reader&lt;/a&gt; right now, we'll see how it goes.&lt;br /&gt;&lt;br /&gt;It's quite fun to add feeds. It's like creating your own newspaper.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-454845854998560483?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/454845854998560483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/feed-reader.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/454845854998560483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/454845854998560483'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/02/feed-reader.html' title='Feed reader'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5551693627166726982</id><published>2007-01-27T15:34:00.001+01:00</published><updated>2010-12-10T12:07:02.070+01:00</updated><title type='text'>Gambling</title><content type='html'>One of the local newspapers has a puzzle with the prize of a 2 GB iPod Nano. You send an SMS with the solution to a number, pay 5 DKK (app. $1, a little less than 1€) and take part in the draw if your solution is correct.&lt;br /&gt;&lt;br /&gt;I recently noticed that they are kind enough to print the number of correct solutions sent to them the previous week. It turns out it is very few. This week it was 28. That probably says something about the quality of the newspaper...&lt;br /&gt;&lt;br /&gt;Anyway, what's interesting here is that you pay 5 kroner to get about 1/28 chance of winning a prize worth 1600 kroner. Assuming a probability of 1/28 every week, the outcome is &lt;a href="http://en.wikipedia.org/wiki/Binomial_distribution"&gt;binomially distributed&lt;/a&gt;. The expected number of wins is thus &lt;span style="font-style: italic;"&gt;np&lt;/span&gt;, the number of draws times the probability. Consequently, after 28 weeks you can expect to have won the prize once, paying 140 kroner to get an iPod!&lt;br /&gt;&lt;br /&gt;So we've decided to start sending in the solutions from now on.&lt;br /&gt;&lt;br /&gt;The thing about gambling is that the variance is usually high. For the binomial distribution the variance is &lt;span style="font-style: italic;"&gt;np(1-p)&lt;/span&gt;. Since the probability is 1/28, the extra factor compared to the expected value is 1-1/28, or almost 1. In other words, the variance is almost the size of the expected value. We take the square root to get the standard deviation, but this does not change the value much.&lt;br /&gt;&lt;br /&gt;For the normal distribution, which the binomial distribution approximates, one should not be surprised to get a value within 2 standard deviations of the mean (this is about 95% of the probability mass). Here are two plot that shows the expected value (red line) with one and two standard deviations added as a function of the number of weeks. The first one is zoomed in on 0-28 weeks.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_PIM6r0YLhk4/RbtuWaMc5YI/AAAAAAAAAAM/hiEVXr6Wq-A/s1600-h/up-to-28-draws.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer;" src="http://bp2.blogger.com/_PIM6r0YLhk4/RbtuWaMc5YI/AAAAAAAAAAM/hiEVXr6Wq-A/s400/up-to-28-draws.png" alt="" id="BLOGGER_PHOTO_ID_5024731140529644930" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_PIM6r0YLhk4/RbtuWqMc5ZI/AAAAAAAAAAU/_xMFBDFEfEU/s1600-h/up-to-200-draws.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer;" src="http://bp3.blogger.com/_PIM6r0YLhk4/RbtuWqMc5ZI/AAAAAAAAAAU/_xMFBDFEfEU/s400/up-to-200-draws.png" alt="" id="BLOGGER_PHOTO_ID_5024731144824612242" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The good news is that after 28 weeks, I wouldn't be surprised to have won two or even three iPods. The bad news is that I might as well not have won one at all. As you can see on the second plot, it takes more than 150 weeks to make the expected value minus two standard deviations greater than one.&lt;br /&gt;&lt;br /&gt;I am crossing my fingers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5551693627166726982?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5551693627166726982/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/gambling.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5551693627166726982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5551693627166726982'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/gambling.html' title='Gambling'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_PIM6r0YLhk4/RbtuWaMc5YI/AAAAAAAAAAM/hiEVXr6Wq-A/s72-c/up-to-28-draws.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-281666707095196349</id><published>2007-01-22T09:43:00.000+01:00</published><updated>2007-01-22T11:47:59.220+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Open source community building</title><content type='html'>A guy describes &lt;a href="http://community.linux.com/article.pl?sid=07/01/12/2127230"&gt;his experiences&lt;/a&gt; with building an open source project. The most interesting part is the check list to the right.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-281666707095196349?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/281666707095196349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/open-source-community-building.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/281666707095196349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/281666707095196349'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/open-source-community-building.html' title='Open source community building'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5034709119795558271</id><published>2007-01-21T21:38:00.000+01:00</published><updated>2007-01-21T22:20:01.364+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>A model of eating</title><content type='html'>I have known for some time now that Janne and I don't eat a lot of meat. It's not that we are vegetarians, most of our meals include meat in some form or another, it's just that the amounts are small.&lt;br /&gt;&lt;br /&gt;On the other hand, we eat a lot of vegetables. Since moving in with Janne I'm beginning to think that a standard Danish dinner consisting of some kind of meat with boiled potatoes and a sauce is extremely boring. It needs to be augmented with vegetables.&lt;br /&gt;&lt;br /&gt;So today I was wondering whether there is a correlation between these two facts, less meat and more garniture. I think I've found a model that can explain it.&lt;br /&gt;&lt;br /&gt;When I eat, I usually alternate between putting the various stuff on the plate in my mouth, e.g. with potatoes and a beef it is one mouthful of potato with sauce, one mouthful of beef, one mouthful of potato, etc. Let's pretend it takes 100 mouthfuls to get full, ignoring differences in the saturation effect of different types of food. Then roughly 1/2 of these, 50 mouthfuls, will be of meat.&lt;br /&gt;&lt;br /&gt;If instead the plate contains of two kinds of vegetable garniture plus the potatoes (Janne and I almost always cook them in the oven and forgo the sauce) and the beef, then I'll usually alternate in a round-robin fashion between the vegetables, the potatoes and the meat. So only roughly 1/4 of the mouthfuls will be of meat, i.e. 25 mouthfuls.&lt;br /&gt;&lt;br /&gt;If the model is correct, and I'll think about validating it, then I eat about twice as much meat at a traditional dinner at my parents' compared to a typical dinner cooked by Janne or me, without really noticing it.&lt;br /&gt;&lt;br /&gt;So now I'm just left wondering exactly how many mouthfuls it takes for me to get full. I think I'll try counting them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5034709119795558271?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5034709119795558271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/model-of-eating.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5034709119795558271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5034709119795558271'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/model-of-eating.html' title='A model of eating'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-147346505869960155</id><published>2007-01-16T16:45:00.000+01:00</published><updated>2007-01-16T16:49:17.019+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Starving</title><content type='html'>Some guy tried living on &lt;a href="http://hungryforamonth.blogspot.com/"&gt;$30 for a month&lt;/a&gt;. Incidentally, he appears to have found an effective means of losing weight.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-147346505869960155?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/147346505869960155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/starving.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/147346505869960155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/147346505869960155'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/starving.html' title='Starving'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-1170610032694518196</id><published>2007-01-15T09:56:00.000+01:00</published><updated>2007-01-15T10:04:46.578+01:00</updated><title type='text'>Talk by Claus Elgaard</title><content type='html'>A couple of months back I attended a talk by Claus Elgaard with my iola comrades. Coming from the world of sports and athletes, he talked about motivation, preparation, excitation and hybris in the extreme. Fascinating. I found my notes while cleaning up the office in preparation for our move.&lt;br /&gt;&lt;br /&gt;Unfortunately, almost noone showed up so financially it was a disaster. A picture of life, perhaps.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-1170610032694518196?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/1170610032694518196/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/talk-by-claus-elgaard.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1170610032694518196'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/1170610032694518196'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/talk-by-claus-elgaard.html' title='Talk by Claus Elgaard'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8941129773991535549</id><published>2007-01-13T17:30:00.000+01:00</published><updated>2007-02-25T23:13:36.832+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Small games</title><content type='html'>I recently discovered &lt;a href="http://apocalypse.rulez.org/pangzero/Pang_Zero"&gt;Pang Zero&lt;/a&gt;, a small game in which you try to avoid getting hit by balls and at the same time shooting them away. I have been playing it a couple of times lately. It reminds me of another game, AckAck!, that I played back when I was using &lt;a href="http://www.talula.demon.co.uk/allegro/"&gt;Allegro&lt;/a&gt;. Unfortunately, I can't find it anymore.&lt;br /&gt;&lt;br /&gt;Could be interesting to try speed programming small games like these with something like &lt;a href="http://www.pygame.org"&gt;Pygame&lt;/a&gt;. I've always spent too much time mulling over the details with my game projects. Perhaps that's why I only got &lt;a href="http://people.iola.dk/olau/monster-masher/"&gt;Monster Masher&lt;/a&gt; finished enough to actually release. I have the source code for a Spacewar game, a Scorched Earth-like game (we called it Gnome Tanks) and a clone of Helter Skelter stored somewhere, none of them complete enough to be really playable.&lt;br /&gt;&lt;br /&gt;Still, I'm secretly proud of Gnome Tanks. The AI can aim perfectly in all terrains, even when it is windy. Wind transforms the equations into something that cannot be solved analytically so I had to research and implement a numerical solver. When I wrote the code, it was the only Scorched Earth-like game that could do this. I wonder if others have figured out how to do it by now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8941129773991535549?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8941129773991535549/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/small-games.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8941129773991535549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8941129773991535549'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/small-games.html' title='Small games'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5205513820710615969</id><published>2007-01-10T23:11:00.001+01:00</published><updated>2008-02-24T23:53:56.519+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Usability testing</title><content type='html'>I just discovered that &lt;a href="http://www.betterdesktop.org"&gt;betterdesktop.org&lt;/a&gt; has gotten quite some content now. Cool!&lt;br /&gt;&lt;br /&gt;I think they could do with a usability test of the site itself, though. It's a bit confusing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5205513820710615969?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5205513820710615969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/usability-testing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5205513820710615969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5205513820710615969'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2007/01/usability-testing.html' title='Usability testing'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-2903048001530395208</id><published>2006-12-30T23:05:00.000+01:00</published><updated>2007-01-10T23:20:22.034+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>About burn-out</title><content type='html'>Found an &lt;a href="http://nymag.com/news/features/24757/index1.html"&gt;article on burn-out&lt;/a&gt;. Interesting, albeit with more talk than thought. Quote from the article: &lt;span style="font-style: italic;"&gt;"... happiness equals reality divided by expectations."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I found the link via the &lt;a href="http://evan.livejournal.com/"&gt;blog of some guy&lt;/a&gt; working at Google. The blog is an entertaining read. Check out the &lt;a href="http://evan.livejournal.com/tag/famous+people"&gt;famous people&lt;/a&gt; he's seen.&lt;br /&gt;&lt;br /&gt;Update: I subsequently read that Danes are generally more content than people in southern Europe because they on average expect less of their life.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-2903048001530395208?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/2903048001530395208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/about-burn-out.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2903048001530395208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/2903048001530395208'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/about-burn-out.html' title='About burn-out'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4302912680080162460</id><published>2006-12-27T12:52:00.000+01:00</published><updated>2006-12-30T23:12:28.138+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plants'/><title type='text'>Age of plants</title><content type='html'>I found a fascinating article in the Danish Wikipedia about &lt;a href="http://da.wikipedia.org/wiki/Tr%C3%A6biologi"&gt;the biology of trees&lt;/a&gt;. Written in a intriguing language, like this quote about defense mechanisms (my translation):&lt;br /&gt;&lt;blockquote&gt;Within seconds the tree knows that it has been hurt. As soon as the bark is breached, the tree needs to defend itself. If fungus and bacteria spores find an uncovered piece of wood - and they will - they will launch an attack on the wound and into the wood.&lt;/blockquote&gt;I wonder whether it'll survive the editing process in the long run.&lt;br /&gt;&lt;br /&gt;I was looking for information about how old plants can grow. Indoor perennial plants and flowers are commonly multiplied by division. But that doesn't reset their age. If they have one. I still don't know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4302912680080162460?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4302912680080162460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/age-of-plants.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4302912680080162460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4302912680080162460'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/age-of-plants.html' title='Age of plants'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-5467000174064302277</id><published>2006-12-26T16:38:00.000+01:00</published><updated>2006-12-26T16:56:43.350+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='photo'/><title type='text'>Imagery</title><content type='html'>I was showing my sister how to do some basic things with &lt;a href="http://www.gimp.org/"&gt;The Gimp&lt;/a&gt;. Just yesterday I was experimenting with the gradient tool in The Gimp to create some graphics for &lt;a href="http://people.iola.dk/olau/"&gt;my website&lt;/a&gt; with footage from photos we've taken. I'm a complete beginner when it comes to photography, but I managed to find a decent photo.&lt;br /&gt;&lt;br /&gt;Reminds me about &lt;a href="http://www.thefairest.info/"&gt;thefairest.info&lt;/a&gt;. And &lt;a href="http://www.thecutest.info/"&gt;thecutest.info&lt;/a&gt;. Excellent stuff!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-5467000174064302277?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/5467000174064302277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/imagery.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5467000174064302277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/5467000174064302277'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/imagery.html' title='Imagery'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-8216219131067153069</id><published>2006-12-24T10:56:00.000+01:00</published><updated>2007-02-25T23:15:49.596+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Christmas</title><content type='html'>&lt;span style="color: rgb(255, 0, 0);"&gt;&lt;/span&gt;That &lt;span style="font-weight: bold; color: rgb(204, 0, 0);"&gt;time&lt;/span&gt; of the year again.&lt;br /&gt;&lt;br /&gt;I heard a small passage from Bach's Weinachtsoratorium on P2 (Danish national radio channel). Reminded me of &lt;a href="http://en.wikipedia.org/wiki/Messiah_%28Handel%29"&gt;Messiah&lt;/a&gt; by Händel. One of the verses, song by the alto, is about "the lamb of God":&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;He was despisèd and rejected of men: a man of sorrows, and acquainted with grief.&lt;/blockquote&gt;A quote from the bible I believe. I'm not a Believer, but that quote is ingenious. Take that, happy Christmas&lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;&lt;/span&gt;! :-) You have to hear it in Händel's interpretation to really appreciate it. But that's easy, just scroll down on the &lt;a href="http://en.wikipedia.org/wiki/Messiah_%28Handel%29"&gt;Wikipedia page&lt;/a&gt; and there should be a link.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-8216219131067153069?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/8216219131067153069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/christmas.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8216219131067153069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/8216219131067153069'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/christmas.html' title='Christmas'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-7742121329606561571</id><published>2006-12-23T18:33:00.000+01:00</published><updated>2006-12-23T22:38:14.293+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>Charity</title><content type='html'>I'm in the midst of cleaning up my home page in preparation for moving it from the university domain to  our company server. So I stumbled upon the link I put in to &lt;a href="http://www.amnesty.org"&gt;Amnesty International&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I have an ambivalent attitude towards charity organisations in general. I think we ("we" as in people like me who can listen to Wilhelm Kempff and Alfred Brendel all day long) have a responsibility to help people, even if they are half way round the globe. The mere fact that we know about them and easily have the resources to help is enough to institute the responsibility.&lt;br /&gt;&lt;br /&gt;It's like if you encounter someone lying on the highway with a broken leg in the middle of the night. As soon as you see that person, you have a responsibility to help. It's a criminal offense not to do so.&lt;br /&gt;&lt;br /&gt;That said I'm not particularly fond of private organisations spending money and resources on collecting monetary donations. They do so by promoting guilt, which is a horrible thing to do. It also seems wasteful. The tax system is already implemented and works. And I think it favours short-sighted fire extinguishing instead of long-term pulls. People donate a little to nurture their conscience when a major disaster has happened, and then half a year later go vote for a party that cuts the foreign support in half.&lt;br /&gt;&lt;br /&gt;Reminds me about &lt;a href="http://www.garretthardinsociety.org/articles/art_tragedy_of_the_commons.html"&gt;the tragedy of the commons&lt;/a&gt;, an interesting essay which among other things touches upon why a completely free market is not always a good idea and why taxes are sometimes preferable to the initiative of individuals, given the nature of human kind.&lt;br /&gt;&lt;br /&gt;But back to Amnesty. I'm a member of Amnesty (and paying to be so) because I believe Amnesty is actually working on a long-term project, securing the rights and the freedom of people. Basic human rights cannot be taken for granted, not even today.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-7742121329606561571?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/7742121329606561571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/charity.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7742121329606561571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/7742121329606561571'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/charity.html' title='Charity'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-3197919615895896329</id><published>2006-12-23T14:20:00.000+01:00</published><updated>2006-12-23T16:29:36.842+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Magnatune, Händel and playing Beethoven</title><content type='html'>I'm listening to music on &lt;a href="http://magnatune.com/"&gt;Magnatune&lt;/a&gt;, an online music store that lets you listen to the music (all of it) before you buy and offers non-&lt;a href="http://en.wikipedia.org/wiki/CRAP"&gt;CRAP&lt;/a&gt; formats.&lt;br /&gt;&lt;br /&gt;They've added opera. I'm not a big fan of opera, but it turns out to be composed by Händel. Who I'm also not a big fan of, but supposedly he was good at the opera stuff. So I'm giving it a try. Frankly it sounds great!&lt;br /&gt;&lt;br /&gt;On a related note, I'm practising a sonata by Beethoven for the time being. A small and delightful one, opus 78 according to the scores. It's not the first one. I've previously played and enjoyed the Moonlight and the Waldstein sonatas.&lt;br /&gt;&lt;br /&gt;The third and probably less known movement of the Moonlight sonata is an aggressive contrast to the tranquility of the first movement (the one with the moonlight) and the joyous mood of the second movement. Fun albeit not easy to play. Plus it's a bit loud; a problem with three neighbours in close vicinity.&lt;br /&gt;&lt;br /&gt;The Waldstein sonata is very long. I don't think I would have the stamina to play it in its entirety in front of an audience. It's great, though. I only started rehearsing it because I fell in love with a recording of the piece by &lt;a href="http://en.wikipedia.org/wiki/Wilhelm_Kempff"&gt;Wilhelm Kempff&lt;/a&gt; (from Deutche Grammophon if I recall corretly). Mmmm. The sound quality is obviously not great, but the interpretation and the excellence of control...&lt;br /&gt;&lt;br /&gt;What I like about Beethoven's piano music and symphonies is the tremendous &lt;span style="font-style: italic;"&gt;passion&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Chopin's music has the same quality in his preludes and nocturnes, but it's mostly about melancholy. Less anger, less joy. I think my next piece to study will be either the last or second to last sonata by Beethoven. I have great recordings of these by &lt;a href="http://en.wikipedia.org/wiki/Alfred_Brendel"&gt;Alfred Brendel&lt;/a&gt;. I feel fortunate to be able to listen to such an artist, by a mere poke of my finger, as often as I like. Great times we live in.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-3197919615895896329?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/3197919615895896329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/im-listening-to-music-on-magnatune.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3197919615895896329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/3197919615895896329'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/im-listening-to-music-on-magnatune.html' title='Magnatune, Händel and playing Beethoven'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-4154243369567511377</id><published>2006-12-22T17:46:00.000+01:00</published><updated>2006-12-22T17:50:20.137+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='writing'/><title type='text'>Fiction editing</title><content type='html'>Sometimes interesting stuff shows up at &lt;a href="http://slashdot.org"&gt;Slashdot&lt;/a&gt;. A post about &lt;a href="http://slashdot.org/comments.pl?sid=213092&amp;amp;cid=17336576"&gt;editing&lt;/a&gt; of fiction novels (in the context of celebrity writers).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-4154243369567511377?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/4154243369567511377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/fiction-editing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4154243369567511377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/4154243369567511377'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/fiction-editing.html' title='Fiction editing'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8236785121426515359.post-6788834459216192749</id><published>2006-12-13T22:06:00.000+01:00</published><updated>2006-12-13T22:10:59.756+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>CSS fun</title><content type='html'>I'm going to redo my home page. Still thinking about how to do it. Meanwhile I found a nice little page about &lt;a href="http://www.barelyfitz.com/screencast/html-training/css/positioning/"&gt;CSS positioning&lt;/a&gt;. And here's a longer tutorial on &lt;a href="http://css.maxdesign.com.au/floatutorial/index.htm"&gt;floating stuff&lt;/a&gt; with CSS.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8236785121426515359-6788834459216192749?l=ole-laursen.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ole-laursen.blogspot.com/feeds/6788834459216192749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/css-fun.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6788834459216192749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8236785121426515359/posts/default/6788834459216192749'/><link rel='alternate' type='text/html' href='http://ole-laursen.blogspot.com/2006/12/css-fun.html' title='CSS fun'/><author><name>Ole Laursen</name><uri>http://www.blogger.com/profile/11202062681718096078</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PIM6r0YLhk4/SXoFvXXOY5I/AAAAAAAAADM/bDTUF5imeMA/S220/klaverfingre.jpg'/></author><thr:total>0</thr:total></entry></feed>
