<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="http://feeds.feedburner.com/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"><id>tag:blogger.com,1999:blog-4089129643157405806</id><updated>2008-11-19T15:17:46.529+01:00</updated><title type="text">public interface</title><subtitle type="html">news from conan's brain</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.conandalton.net/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>61</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://feeds.feedburner.com/PublicInterface" type="application/atom+xml" /><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-5358153608301095183</id><published>2008-11-17T23:23:00.004+01:00</published><updated>2008-11-17T23:45:36.825+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-17T23:45:36.825+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">risible-db: database migrations in java: release 0.2</title><content type="html">&lt;p style="height:1pt;"&gt;&amp;nbsp;&lt;/p&gt;It is with joy and delight that I announce &lt;a href="https://sourceforge.net/project/showfiles.php?group_id=193899"&gt;risible-db-0.2&lt;/a&gt;, our beloved &lt;a href="http://www.conandalton.net/2008/10/risible-db-database-migrations-in-java.html"&gt;risible migrations project&lt;/a&gt;, now with support for Oracle!&lt;br /&gt;&lt;br /&gt;Risible-db is an ultra-simple database schema migration utility, not as fancy as Rails, you just write plain SQL (really, how many times do you change DB vendor mid-project and think "wow, it's lucky we used a platform-independent DB abstraction layer!" ?). By convention, it looks for SQL scripts in the &lt;code&gt;migrations&lt;/code&gt; subdirectory of your &lt;code&gt;WEB-INF&lt;/code&gt; directory, and will execute any that have not previously been executed.&lt;br /&gt;&lt;br /&gt;So the only reason it needs Oracle support is to create the initial migrations table.&lt;br /&gt;&lt;br /&gt;Usage has changed little, this is how to configure it with Spring (you can imagine the equivalent java code) -&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;applicationContext.xml&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;bean id="migrations" class="risible.db.WebInfMigrations" init-method="start"&amp;gt;&lt;br /&gt;  &amp;lt;property name="dataSource" ref="mysqlDataSource"/&amp;gt;&lt;br /&gt;  &amp;lt;property name="migrationTableName" value="migrations"/&amp;gt;&lt;br /&gt;  &amp;lt;property name="migrationTable"&amp;gt;&lt;br /&gt;    &amp;lt;bean class="risible.db.MySqlMigrationTable"/&amp;gt;&lt;br /&gt;  &amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;The &lt;code&gt;migrationTable&lt;/code&gt; property specifies the db-specific table creation strategy. You can use &lt;code&gt;risible.db.MySqlMigrationTable&lt;/code&gt;, &lt;code&gt;risible.db.OracleMigrationTable&lt;/code&gt;, or roll your own implementation of &lt;code&gt;risible.db.MigrationTable&lt;/code&gt; - a one-method interface whose implementation creates a table if necessary.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://sourceforge.net/project/showfiles.php?group_id=193899"&gt;download here!&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Enjoy, and let us know how it works for you ...&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/456506925" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/5358153608301095183/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=5358153608301095183" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5358153608301095183?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5358153608301095183?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/11/risible-db-database-migrations-in-java.html" title="risible-db: database migrations in java: release 0.2" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-5976376106899509796</id><published>2008-11-11T00:18:00.003+01:00</published><updated>2008-11-11T01:26:19.753+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-11T01:26:19.753+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">little javascript trap</title><content type="html">Sometimes the Spartan simplicity of programming with java interfaces has a lot of appeal, especially when faced with the way Rails treats each class as an enormous hold-all for a gaggle of methods. Pros and cons yadda yadda yadda. &lt;br /&gt;&lt;br /&gt;Having got that out of the way: Rails' &lt;code&gt;to_json&lt;/code&gt; rocks - instant serialisation of your Ruby objects into Javascript objects. We need a JSR to do this to java.lang.Object (I mean, how many times have you needed &lt;code&gt;wait()&lt;/code&gt; and &lt;code&gt;notify()&lt;/code&gt;?), so Java may become Web-2.0 enabled/compliant.&lt;br /&gt;&lt;br /&gt;Anyway, there I was merrily scripting freemarker to do what Rails gives us for free, and everything's honkey-dorey on Firefox and Safari, but Opera is choking. After the requisite banging-head-on-wall, I spot a trailing comma.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;  &amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;      var x = {a:1, b:2, c:3, d:4,}; // trailing comma here. This isn't the original code, btw, in case you're thinking, "duh!"&lt;br /&gt;      for (var i in x) {&lt;br /&gt;        document.write("&amp;lt;div&amp;gt;" + i + " is " + x[i] + "&amp;lt;/div&amp;gt;");&lt;br /&gt;      }&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Warning: Opera don't like trailing commaz. Solution: switch to a language that provides &lt;code&gt;to_json&lt;/code&gt;, or keep suffering. Or drop support for Opera. Speaking of not supporting stuff, I've no idea what IE does. How dangerous is that? Here are my browser stats for the last 30 days:&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;FF&lt;/td&gt;&lt;td&gt;64%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IE&lt;/td&gt;&lt;td&gt;24%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chrome&lt;/td&gt;&lt;td&gt;4%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari&lt;/td&gt;&lt;td&gt;3.9%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Opera&lt;/td&gt;&lt;td&gt;2.7%&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Chrome? Google so pwns you dudes lol&lt;br /&gt;&lt;br /&gt;cheers, and good luck with the code generation ...&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/448981988" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/5976376106899509796/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=5976376106899509796" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5976376106899509796?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5976376106899509796?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/11/little-javascript-trap.html" title="little javascript trap" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-5699367948928968525</id><published>2008-11-07T12:54:00.005+01:00</published><updated>2008-11-07T21:41:14.185+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T21:41:14.185+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="parenting" /><title type="text">Acknowledge Feelings</title><content type="html">&lt;hr width=0/&gt;&lt;style&gt;.drama i {font-size:small;} .drama td, .drama th {vertical-align:top; padding: 0 0.4em; } .drama th { font-family: monospace; }&lt;/style&gt;&lt;br /&gt;Acknowledging the feelings of another person is a powerful technique we learned from Faber-Mazlish.&lt;br /&gt;&lt;br /&gt;One day Sapphire, our delightful daughter, about a year old at the time, was climbing over Bingo the Dog (a noisy plastic toy), fell flat on her face, and started screaming. I picked her up to comfort her, but she struggled and screamed even more. I thought, hopeless father, why did I ever have children, I'm not cut out for this, etc ... and all of a sudden realised she wasn't crying out of pain, she wanted to get back to her toy and I was preventing her! Her tears dryed up pretty fast once I put her back on the floor. But my understanding of crying was forever changed. It wasn't simply "crying =&gt; pain" anymore, crying is now an ambiguous message calling for emotionally-sensitive context-dependent interpretation. This wasn't going to be easy for my INTP personality.&lt;br /&gt;&lt;br /&gt;Fast forward 18 months. Sometimes she trips over a toy and bashes her face on the floor - actually hurting herself. As usual, my (hopelessly wrong) intuition would suggest I say it's nothing, don't worry about it, you're not really hurt, it'll get better, stop whining, hey, get over it, it's only a bruise, it's not the end of the world, for god's sake! Here's the updated alternative:&lt;br /&gt;&lt;br /&gt;&lt;table class="drama"&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;&lt;i&gt;(picks Sapphire up, cradles her in arms, adopts a concerned, caring-parent expression)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(screaming in agony)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Sapphire, are you hurt?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(screams some more)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Is it the end of the world?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(screams continue, but she appears to be nodding in agreement)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Did you bash your face on the floor?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(screaming)&lt;/i&gt; Yes!!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;That must hurt a lot!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(screaming less)&lt;/i&gt; Yes!!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Did you crack your skull open?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(a little calmer)&lt;/i&gt; Yes!!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Is your nose all squashed?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(crying, still)&lt;/i&gt; Yes!!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;ouch!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(sobs gently)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;&lt;i&gt;(points to the floor where she fell)&lt;/i&gt; Is this where you hit the floor?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(sobbing gently)&lt;/i&gt; Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Oh dear, it must have hurt you a lot.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(moaning)&lt;/i&gt; Yes. Urting.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;&lt;i&gt;(pointing to the totally wrong place, her shoulder for example)&lt;/i&gt; Does it hurt here?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(points to her head)&lt;/i&gt; No, here. Ed ache.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Oh, your head hurts?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;Yes, ed ache. My ed ache.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Sapphire, tell me, what do the wheels on the bus do?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;&lt;i&gt;(sniff)&lt;/i&gt; Go wound and wound.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;And the horn on the bus?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;Go beep-beep-beep.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Indeed ... do you want to play some more?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sapphire&lt;/th&gt;&lt;td&gt;Yes &lt;i&gt;(Gets down. Surveys the situation. Wipes her nose in her sleeve. Resumes activity. All is well)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;NLP teaches us that 90% of our communication is non-verbal, which is why you can get away with "did you crack your skull open?". Empathise, let your tone of voice and facial expression reflect how you feel, and the words don't really matter so much. Once you establish empathy, you can change the tone of the conversation, and her mood will follow. The wheels on the bus, or whatever her favourite daytime song is, makes a great distraction, and then she's ready to get back to clambering over Bingo.&lt;br /&gt;&lt;br /&gt;It doesn't seem to work so well among adults, at least not if you're unsubtle or the victim knows what you're up to. &lt;a href="http://www.quotedb.com/quotes/1773"&gt;Sincerity&lt;/a&gt; is really the key here.&lt;br /&gt;&lt;br /&gt;&lt;table class="drama"&gt;&lt;tr&gt;&lt;th&gt;&amp;nbsp;&lt;/th&gt;&lt;td&gt;&lt;i&gt;(late one evening)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;&lt;i&gt;(despairingly)&lt;/i&gt; This PayPal developer API is really complicated, I don't know how I'll ever make money :(&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Sabrina&lt;/th&gt;&lt;td&gt;&lt;i&gt;(tongue firmly in cheek)&lt;/i&gt; Oh, Conan, you must be really frustrated, it sounds so difficult ...&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Conan&lt;/th&gt;&lt;td&gt;Don't try your Faber-Mazlish stuff on me, dammit!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;&amp;nbsp;&lt;/th&gt;&lt;td&gt;&lt;i&gt;(all burst out laughing)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/445412463" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/5699367948928968525/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=5699367948928968525" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5699367948928968525?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5699367948928968525?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/11/acknowledge-feelings.html" title="Acknowledge Feelings" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-8197182072649537753</id><published>2008-10-30T17:57:00.005+01:00</published><updated>2008-11-07T13:15:47.665+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:15:47.665+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">When to use Interfaces</title><content type="html">&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;The simplest answer: never. Use Ruby or any other dynamic language instead! But if you're still stuck in javaland, well, I promised this article in &lt;a href="http://www.conandalton.net/2008/05/programming-for-stone-age.html"&gt;Programming for the Stone Age&lt;/a&gt; after witnessing some serious interface-itis. My university Software Engineering courses all talked about "always code to an interface" and articles abound on this insidious meme. So you get interface-implementation couples, where every method signature on the class is duplicated on the interface, pointlessly. I remember with horror a proprietary ORM I was obliged to use that actually required this pairing for every persistent class. It was unfortunately the backbone of our application, and hurt more than raw SQL. But that was in the days before Hibernate and the whole POJO movement, so let's call it a learning experience. Let us end the tedium of obligatory signature copying for every little property we add to our applications!&lt;br /&gt;&lt;br /&gt;Right. So you're building your application, you decide your domain objects will include &lt;code&gt;Customer&lt;/code&gt;, &lt;code&gt;Supplier&lt;/code&gt;, &lt;code&gt;Product&lt;/code&gt;(*). You already have a concept: a &lt;i&gt;Domain Object&lt;/i&gt;, a kind of a thing that is unlike a &lt;i&gt;Controller&lt;/i&gt;, or &lt;i&gt;Service&lt;/i&gt;, or a &lt;i&gt;Repository&lt;/i&gt;. A &lt;i&gt;Domain Object&lt;/i&gt; has nothing in common with a &lt;code&gt;HashMap&lt;/code&gt; or &lt;code&gt;String&lt;/code&gt; or &lt;code&gt;JdbcConnection&lt;/code&gt;. Major distinguishing features of &lt;i&gt;Domain Objects&lt;/i&gt; are that each instance can be uniquely identified from among all Domain Object instances; each Domain Object is subject to CRUD operations and may be in an unsaved or deleted state. You might have decided for your application that objects are never deleted from your database, but rather they are tagged as deleted. You might decide that all domain objects should have a human-readable name. If any of this is the case, it is possible, depending on the nature of your application, that an interface resembling the following may be useful:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;DomainObject.java&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;interface DomainObject {&lt;br /&gt;  UniqueIdentifier getUID(); // this isn't necessarily the database id of the object&lt;br /&gt;  String getName();&lt;br /&gt;  boolean isSaved();&lt;br /&gt;  boolean isDeleted();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;With this interface in place, you can write some methods whose behaviour will be consistent for all of your domain objects:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  public void update(DomainObject o) {&lt;br /&gt;    if (o.isDeleted()) {&lt;br /&gt;      throw new CantUpdate("object " + o.getUID() + " is already deleted");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public String renderLink(DomainObject o) {&lt;br /&gt;    return "&amp;lt;a href='/object/" + o.getUID() + "'&amp;gt;" + o.getName() + "&amp;lt;/a&amp;gt;";&lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Customer&lt;/code&gt; and &lt;code&gt;Supplier&lt;/code&gt; have a feature in common: they are contactable. That is, they have addresses, phone numbers, emails and so on. It would be nice to handle contact information in a uniform way: to do so, the &lt;code&gt;Contactable&lt;/code&gt; interface might be handy:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;Contactable.java&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;interface Contactable {&lt;br /&gt;  String getAddress();&lt;br /&gt;  void setAddress(String address);&lt;br /&gt;  String getEmail();&lt;br /&gt;  void setEmail(String email);&lt;br /&gt;&lt;br /&gt;  ... etc ...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;Armed with &lt;code&gt;Contactable&lt;/code&gt;, you can manage contact information in a uniform way:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  public void verify(Contactable c) {&lt;br /&gt;    ...&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public String renderContactInfo(Contactable c) {&lt;br /&gt;    ...&lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Last example: &lt;code&gt;Auditable&lt;/code&gt; : for those Domain Objects for which all changes need to be audited. If your application requires &lt;i&gt;all&lt;/i&gt; objects are audited, you might as well merge this with &lt;code&gt;DomainObject&lt;/code&gt; (and you might make it a superclass rather than an interface).&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;Auditable.java&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;interface Auditable {&lt;br /&gt;  User modifiedBy();&lt;br /&gt;  Date modifiedOn();&lt;br /&gt;  void updated(User by, Date when);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Auditable&lt;/code&gt; works well in combination with a Hibernate interceptor or event handler so that every object that is persisted and requires auditing, is guaranteed to be audited with no more effort than adding "implements Auditable" to your class definition. This way, together with the magic of hibernate, your business-logic code can deal with modifying your object in a clear and readable fashion, without a heap of audit-code clutter. For example:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;MyHibernateInterceptor.java&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;class MyHibernateInterceptor {&lt;br /&gt;  boolean onSaveDirty(Object o, Stuff... otherStuff) {&lt;br /&gt;    if (o instanceof Auditable) {&lt;br /&gt;      ((Auditable) o).updated(getUser(), new Date());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ... etc ...&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  ... etc ...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;So you end up with domain classes looking a little like this:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;Customer.java&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;class Customer implements DomainObject, Auditable, Contactable {&lt;br /&gt;&lt;br /&gt;  ... interface methods ...&lt;br /&gt;&lt;br /&gt;  ... other methods unique to customer ...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;Java interfaces allow you slice up a big class into smaller, managable chunks so that you can write well-targeted pieces of code that guarantee consistent behaviour over a range of different objects.&lt;br /&gt;&lt;br /&gt;Lastly and most importantly: don't create the interface until you really need it. If you have only one implementation, and there is only one possible implementation (at least for the moment), then you don't need an interface. The day you have a second implementation of the same concept, hit Refactor -&gt; Extract Interface in Intellij (**) to introduce your interface quickly and painlessly. And I really mean painlessly. Intellij will hunt down all references to the old class in your code, and convert them where possible into references to the interface. And you get to decide whether the class keeps its name and the interface gets a new name, or the other way around. &lt;br /&gt;&lt;br /&gt;There is a whole universe of refactorings out there: "extract interface" is so trivial and easy to understand that it can be automated. So use it. When you have the freedom to evolve your code in any direction at the last minute, you implicitly have the freedom to keep your codebase much simpler by &lt;i&gt;not&lt;/i&gt; evolving it &lt;i&gt;until&lt;/i&gt; the last minute.&lt;br /&gt;&lt;br /&gt;Good luck, and let me know how it goes.&lt;br /&gt;&lt;br /&gt;&lt;small&gt;(*) It looks like e-commerce is the "hello world" of web-app bloggers&lt;br /&gt;(**) I'm sure Eclipse and Netbeans have this refactoring too.&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/437210093" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/8197182072649537753/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=8197182072649537753" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/8197182072649537753?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/8197182072649537753?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/when-to-use-interfaces.html" title="When to use Interfaces" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-5641515061711213074</id><published>2008-10-25T12:36:00.009+02:00</published><updated>2008-10-26T00:13:34.259+02:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-10-26T00:13:34.259+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="recruiting" /><title type="text">Tales of the Interview</title><content type="html">&lt;p style='height:1px;'&gt;&amp;nbsp;&lt;/p&gt;Recruiting is not only for colleagues: we recruit everybody. It is the world's biggest problem: getting the right people into the right jobs, eliminating the waste and drag of unproductivity engendered by poorly matched people and positions. We recruit babysitters, bankers, plumbers, spouses, presidents, even if  the recruitment process is completely different in each case. The only people we can't recruit are our blood relations.&lt;br /&gt;&lt;br /&gt;This post was triggered by two recruitment-horror-story posts from &lt;a href="http://thedailywtf.com"&gt;the world's best blog&lt;/a&gt;: &lt;a href="http://thedailywtf.com/Articles/A-Problem-at-the-Personal-Level--More.aspx"&gt;A Problem at the Personal Level&lt;/a&gt;, and  &lt;a href="http://thedailywtf.com/Articles/The-Receptionist-Test.aspx"&gt;The Receptionist Test&lt;/a&gt;. They're worth a read.&lt;br /&gt;&lt;br /&gt;It scares me how some software organisations hire coders on the basis of nothing more than their CV, a handshake, and a smile. If you do nothing else in a programming interview, please do this: sit in front of a computer with your candidate, and ask her, or him, to write some code. This alone is the best filtering tool you have, it's so powerful you won't want to look at CVs ever again.&lt;br /&gt;&lt;br /&gt;Tips for programming interviews:&lt;ul&gt;&lt;li&gt;Take time at the beginning of the interview to help your candidate relax so they can put on their best performance. If you've done this, and they're still nervous and stressed, you need to think about having a nervous and stressed person on your team. If you haven't done this, and they perform poorly due to stress, you might have missed your best candidate.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The more developers from the team are involved, the better (perhaps a rotating three-person interviewing team for example).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Over time, build up a collection of programming exercises. You will need a string of performances in order to be able to judge comparatively, but you will also need a variety of exercises so that you can choose, for a given candidate, the exercise that allows their strengths to shine.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Don't waste time on candidates that you haven't pre-screened with some basic technology questions first. (Really basic. Eg, What's the difference between a Set and a List?)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There are many kinds of programming exercise.&lt;ul&gt;&lt;li&gt;Pattern-oriented: because you (probably) need these in your project: you can find or design an exercise that requires Command, or Strategy or example&lt;/li&gt;&lt;li&gt;For someone claiming to be a java "expert", design an exercise that shows a mastery of exception handling and inner-class use.&lt;/li&gt;&lt;li&gt;Find (or write) some particularly nasty code that your candidate can refactor and write tests for.&lt;/li&gt;&lt;li&gt;Best of all: don't interrupt your work, turn your current project task into an exercise. No interview exercise can be more relevant than this!&lt;/li&gt;&lt;li&gt;Avoid algorithm-oriented problems, (sorting, searching, packing, etc) - because libraries exist for this kind of thing so these exercises don't test real-world experience so well.&lt;/li&gt;&lt;li&gt;Avoid framework-specific problems (eg Swing, Hibernate): an exercise requires too much setup and context, and ordinary spoken Q&amp;A are best for evaluating technology-specific knowledge.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Incorporate test-automation. A solid candidate will grasp testing concepts even if they are not familiar with the frameworks you use.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It's not just about coding and technical skills. This is your chance to sit next to your candidates, to work with them, as if they were already on your team. Do they have a sense of humour? The right kind of humour for your team? Are they comfortable, relaxed, confident? Do they listen, take the time to understand you, and verify that they understood you, before launching into the problem? Do you really want to work with this person?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You're not a born interviewer. You will make mistakes. I did. Get over it. Interview more.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Gladwell argues in &lt;a href="http://www.gladwell.com/blink/index.html"&gt;Blink&lt;/a&gt; that although we make snap judgements all the time, whether we can trust these judgements is a matter of experience. With a few years of interview practice, your snap judgements will gradually become more and more accurate. And maybe, at that point, a firm handshake and a quick, friendly, confident smile will be enough?&lt;br /&gt;&lt;br /&gt;Now if there was a way to do this for presidents, and plumbers ... the world might be a different place ...&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/432082436" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/5641515061711213074/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=5641515061711213074" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5641515061711213074?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5641515061711213074?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/tales-of-interview.html" title="Tales of the Interview" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-5373628190965814745</id><published>2008-10-24T16:14:00.003+02:00</published><updated>2008-11-07T13:16:09.586+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:16:09.586+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">CSS can replace many lines of Javascript</title><content type="html">&lt;p style='height:1px'&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;Here's a trick that has saved me dozens of lines of javascript. Say you need to toggle your page between two modes - for example beginner/expert, verbose/concise, update/read-only, screen/printer. You could set a cookie or request parameter and reload the page, but that may not be efficient, it calls for a lot of code server-side. More practically, you could write some javascript that iterates over your DOM, identifies target elements, and toggles some aspect of their state in conformity with the desired mode. But again, it's a lot of code, only this time on the client-side.&lt;br /&gt;&lt;br /&gt;Or you could do the following. Use a CSS classname to identify elements that should be visible in one mode or the other. For example, &lt;code&gt;ifVerbose&lt;/code&gt; and &lt;code&gt;ifConcise&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;any-page.html&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;  &amp;lt;body class='&lt;b&gt;verboseMode&lt;/b&gt;'&amp;gt;&lt;br /&gt;    &amp;lt;a href='/blah/blah' class='&lt;b&gt;ifVerbose&lt;/b&gt;'&amp;gt;&lt;br /&gt;      This is a link to /blah/blah where you &lt;br /&gt;      will discover a lot of blah.&lt;br /&gt;    &amp;lt;/a&amp;gt;&lt;br /&gt;    &amp;lt;a href='/blah/blah' class='&lt;b&gt;ifConcise&lt;/b&gt;'&amp;gt;/blah/blah&amp;lt;/a&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;The CSS is trivial: any &lt;code&gt;ifVerbose&lt;/code&gt; element on a &lt;code&gt;conciseMode&lt;/code&gt; page is invisible, and vice-versa.&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;application.css&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;  .verboseMode .ifConcise {&lt;br /&gt;    display: none;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  .conciseMode .ifVerbose {&lt;br /&gt;    display: none;&lt;br /&gt;  }&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;All you need now is a tiny bit of javascript to toggle the classname of your root element between &lt;code&gt;conciseMode&lt;/code&gt; and &lt;code&gt;verboseMode&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;application.js&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  var verbose = function(v) {&lt;br /&gt;    if (v) {&lt;br /&gt;      $(document.body).addClassName('verboseMode');&lt;br /&gt;      $(document.body).removeClassName('conciseMode');&lt;br /&gt;    } else {&lt;br /&gt;      $(document.body).addClassName('conciseMode');&lt;br /&gt;      $(document.body).removeClassName('verboseMode');&lt;br /&gt;    }&lt;br /&gt;  }&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;Much less work than toggling with javascript in a big loop, no? Let the browser's CSS engine do the work!&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/430780972" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/5373628190965814745/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=5373628190965814745" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5373628190965814745?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5373628190965814745?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/css-can-replace-many-lines-of.html" title="CSS can replace many lines of Javascript" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-6833121430501861275</id><published>2008-10-21T12:22:00.006+02:00</published><updated>2008-11-07T13:16:24.843+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:16:24.843+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">Sending binary data from Freemarker</title><content type="html">&lt;p font-size="1pt;"&gt;&amp;nbsp;&lt;/p&gt;There are probably many better alternatives to writing binary content with Freemarker: for example write your data to the filesystem and let apache serve it. But sometimes you have no choice and you need to serve your png or jpg or doc or pdf from a freemarker template.&lt;br /&gt;&lt;br /&gt;The solution involves setting the content type, like when &lt;a href="http://www.conandalton.net/2008/10/make-freemarker-work-with-prototypes.html"&gt;sending javascript from freemarker&lt;/a&gt;. Then we need an object having access to the data with a method to write it for us.&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;send-png-image.ftl&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;${res.setContentType("image/png")}${action.writeData(res.getOutputStream())}&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;The framework you are using should have injected the HttpResponse object into the Freemarker context. For example, Webwork/Struts2 creates a "res" variable which you can access from your Freemarker templates. The &lt;code&gt;writeData&lt;/code&gt; implementation need not differ much from this:&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;SendImage.java&lt;/legend&gt;&lt;pre&gt;&lt;code&gt; ...&lt;br /&gt;&lt;br /&gt;public void writeData(OutputStream os) throws IOException {&lt;br /&gt;   os.write(this.getByteArray(this.image));&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;It would be nice to simply write &lt;code&gt;${res.outputStream.write(byteArray)}&lt;/code&gt; in Freemarker, and dispense with the Java method - but Freemarker wraps the &lt;code&gt;byte[] byteArray&lt;/code&gt; in a SequenceAdapter (ooh, a Design Pattern), and doesn't unwrap it again when we just want the byte array. So we need a &lt;code&gt;writeData&lt;/code&gt; method or equivalent in java.&lt;br /&gt;&lt;br /&gt;The same principle works for other kinds of binary content - photos, movies, documents, pdfs, etc - set the right content-type. If you want the browser to open a "Save As ..." dialog instead of displaying the content inline, add this after the content-type declaration:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;${res.setHeader("Content-Disposition", "attachment; filename=${fileName}" )}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Special Note for writing Excel&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Set your content-type as follows: &lt;code&gt;${res.setContentType("application/vnd.ms-excel; charset=UTF-8")}&lt;/code&gt;, and then write your data as a HTML table. That's all! Trivial! No POI, no hacks, no bridging to VB. The only thing you need worry about is escaping some of the data so Excel doesn't misinterpret it.&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/427373238" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/6833121430501861275/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=6833121430501861275" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/6833121430501861275?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/6833121430501861275?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/sending-binary-data-from-freemarker.html" title="Sending binary data from Freemarker" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-761885595481957316</id><published>2008-10-21T11:25:00.007+02:00</published><updated>2008-11-07T13:36:55.304+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:36:55.304+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="photos" /><category scheme="http://www.blogger.com/atom/ns#" term="business" /><title type="text">His Serene Highness</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_7sjE3SRdZhk/SP2gTAse4oI/AAAAAAAAAGQ/ozHBWUxIttg/s1600-h/full.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_7sjE3SRdZhk/SP2gTAse4oI/AAAAAAAAAGQ/ozHBWUxIttg/s400/full.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5259536188303139458" /&gt;&lt;/a&gt;&lt;br /&gt;This is &lt;a href="http://en.wikipedia.org/wiki/Ferdinand_Philippe,_Duke_of_Orl%C3%A9ans"&gt;Ferdinand-Philippe, the Duke of Orléans&lt;/a&gt;, who died in a car accident in 1842. &lt;span style="font-size:small"&gt;(*)&lt;/span&gt;. &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7sjE3SRdZhk/SP2gS1vsKwI/AAAAAAAAAGI/RFcGErjZ0Qc/s1600-h/front.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_7sjE3SRdZhk/SP2gS1vsKwI/AAAAAAAAAGI/RFcGErjZ0Qc/s400/front.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5259536185363802882" /&gt;&lt;/a&gt;&lt;br /&gt;His statue reminds me of someone I worked with briefly a few years ago. Pompous, arrogant, disdainful, and overdressed. If you're a duke, and the king's son, you can probably get away with it. &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7sjE3SRdZhk/SP2gTXBikrI/AAAAAAAAAGY/5HCwEe0Gq2s/s1600-h/side.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_7sjE3SRdZhk/SP2gTXBikrI/AAAAAAAAAGY/5HCwEe0Gq2s/s400/side.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5259536194297041586" /&gt;&lt;/a&gt;&lt;br /&gt;If you're a low-ranking manager, try another strategy.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:small"&gt;* It was actually a "voiture à cheval", a horse and carriage. Modern vehicles kill people much more effectively, but it is interesting that the culture of death on the roads was already in place so long ago.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/427373239" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/761885595481957316/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=761885595481957316" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/761885595481957316?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/761885595481957316?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/his-serene-highness.html" title="His Serene Highness" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_7sjE3SRdZhk/SP2gTAse4oI/AAAAAAAAAGQ/ozHBWUxIttg/s72-c/full.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-2759570257192924254</id><published>2008-10-16T23:39:00.003+02:00</published><updated>2008-11-07T13:17:03.890+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:17:03.890+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">risible-db: database migrations in java</title><content type="html">Jean-Philippe Hallot, and I, are pleased to offer the world &lt;a href="https://sourceforge.net/project/showfiles.php?group_id=193899"&gt;risible-db&lt;/a&gt;, a new, free, really simple, 100% java open-source utility library for managing database schema migrations in a manner not wholly unlike rails migrations.&lt;br /&gt;&lt;br /&gt;The class of interest is (somewhat unimaginatively) called &lt;code&gt;Migrations&lt;/code&gt;, with a convenience subclass called &lt;code&gt;WebInfMigrations&lt;/code&gt; that does a lot of the convention-over-configuration thing for you. Use as follows:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;in your Spring applicationContext.xml config&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;bean id="migrations" class="risible.db.WebInfMigrations" init-method="start"&amp;gt;&lt;br /&gt;  &amp;lt;property name="dataSource" ref="myDataSource"/&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;You can of course auto-wire the datasource. There is no other property to set. When Spring calls the init-method, &lt;code&gt;start()&lt;/code&gt;, risible-db will look in a directory called &lt;code&gt;migrations&lt;/code&gt; directly under your application's &lt;code&gt;WEB-INF&lt;/code&gt; directory, for &lt;code&gt;*.sql&lt;/code&gt; files, order them lexicographically, and execute any that have not already been executed against the current datasource. Here's an example:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;/WEB-INF/migrations&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;001-add-widgets-table.sql&lt;br /&gt;002-add-users-table.sql&lt;br /&gt;003-add-permissions.sql&lt;br /&gt;003-add-widget-tags.sql&lt;br /&gt;004-rename-widgets-to-woggles.sql&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;select * from migrations;&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;+---------------------------+&lt;br /&gt;| name                      |&lt;br /&gt;+---------------------------+&lt;br /&gt;| 001-add-widgets-table.sql |&lt;br /&gt;| 002-add-users-table.sql   |&lt;br /&gt;| 003-add-widget-tags.sql   |&lt;br /&gt;+---------------------------+&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;In this situation, risible-db migrations will execute &lt;code&gt;003-add-permissions.sql&lt;/code&gt;, and &lt;code&gt;004-rename-widgets-to-woggles.sql&lt;/code&gt;, because they are not already installed in the migrations table. Unlike Rails, risible-db considers the whole name of the migration, not just the number. So you can have multiple migrations with the same number; in fact risible doesn't care about the numbers at all, but it's a convenient way for you to keep your migrations in order. If you prefer &lt;code&gt;A-add-widgets.sql, B-add-users.sql, etc&lt;/code&gt;, that's ok too.&lt;br /&gt;&lt;br /&gt;We found that the strict numbering approach called for a lot of number-management overhead when working in a distributed team - when the half of the team on this continent wanted to add a migration at the same time as the other half on another continent, we needed a way to ensure we wouldn't both use the same number, while simultaneously guaranteeing that the order of migrations respected schema dependencies.&lt;br /&gt;&lt;br /&gt;Each &lt;code&gt;.sql&lt;/code&gt; file is a plain text file containing one or more sql commands, separated by semicolons. No comments, please.&lt;br /&gt;&lt;br /&gt;Notes: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Support for Oracle is coming in the next few days - right now it works only for MySQL&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Respect and acknowledgements to &lt;a href="http://extremepill.blogspot.com/"&gt;Pascal Pratmarty&lt;/a&gt; who was the first to say, "well we could do that in java, too!"&lt;/li&gt;&lt;br /&gt;&lt;li&gt;future enhancements include &lt;code&gt;down&lt;/code&gt; migrations like rails, conditional migrations (eg data migrations only to run in a test environment), other ways to express migrations (java code, for example), and SQL comments. Please comment if there's something on this list (or not) that you can't live without.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="https://sourceforge.net/project/showfiles.php?group_id=193899"&gt;download here&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Don't search for documentation. This is it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It's LGPL licensed&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Please let us know how it works for you, we'll be happy to hear your feedback.&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/423109235" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/2759570257192924254/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=2759570257192924254" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/2759570257192924254?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/2759570257192924254?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/risible-db-database-migrations-in-java.html" title="risible-db: database migrations in java" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-3422234363736059477</id><published>2008-10-08T22:43:00.003+02:00</published><updated>2008-10-08T23:01:52.862+02:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-10-08T23:01:52.862+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="nonsense" /><title type="text">π is for porn</title><content type="html">Today's favourite from reddit: &lt;a href="http://www.everything2.net/index.pl?node_id=1302963"&gt;π stands for porn&lt;/a&gt; ... and a host of other nasty things you never expected to see in maths classes ... watch out!&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/415148765" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/3422234363736059477/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=3422234363736059477" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/3422234363736059477?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/3422234363736059477?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/is-for-porn.html" title="π is for porn" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-446001179182618849</id><published>2008-10-08T13:24:00.003+02:00</published><updated>2008-10-08T13:45:38.560+02:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-10-08T13:45:38.560+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="parenting" /><category scheme="http://www.blogger.com/atom/ns#" term="politics" /><title type="text">Parenting, Politics, and Manipulation</title><content type="html">One of our first parenting books was a great little volumne called &lt;a href="http://www.amazon.com/How-Con-Your-Kid-Bathtime-Anytime/dp/1594740739"&gt;How to Con your Kid&lt;/a&gt;. It offers many toddler-survival tactics, my favourite being the offer-a-choice trick.&lt;br /&gt;&lt;br /&gt;Yesterday, I was encouraging my daughter (age 2) to get into her stroller so we might go collect her brother at school.&lt;br /&gt;&lt;br /&gt;Conan: Into your stroller, sweetie-pie&lt;br /&gt;Daughter: I don't want to go into the stroller, I want to walk!&lt;br /&gt;Conan: Should I fasten the straps, or do you want to do it?&lt;br /&gt;Daughter: I want to do it.&lt;br /&gt;Conan: Hmm. Well, just this once, ok? Sit down and you can strap yourself in.&lt;br /&gt;&lt;br /&gt;And in she hops. This technique is so effective it feels like manipulation bordering on unethical. But if the only alternative is physical coercion, it's not so bad. Fortunately (or otherwise), the child eventually learns countermeasures, and it's much less effective by age 4. But by that age dialogue and mutual understanding largely replace the need for coercion.&lt;br /&gt;&lt;br /&gt;Then I noticed an eerily parallel conversation going on the other side of the Atlantic:&lt;br /&gt;&lt;br /&gt;Uncle Sam: More war, more global warming, more bailouts for rich people, more oppression for the poor.&lt;br /&gt;The People: No, we want transparent, open and fair government!&lt;br /&gt;Uncle Sam: Replublican or Democrat?&lt;br /&gt;The People: Um .. Democrat, this time, please&lt;br /&gt;Uncle Same: Hmm. Well, just this once, ok?&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/414754862" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/446001179182618849/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=446001179182618849" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/446001179182618849?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/446001179182618849?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/parenting-politics-and-manipulation.html" title="Parenting, Politics, and Manipulation" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-4989324994936906942</id><published>2008-10-07T13:58:00.007+02:00</published><updated>2008-11-07T13:17:17.627+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:17:17.627+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">HTML: Scroll table contents without scrolling headers</title><content type="html">&lt;p style="margin:-1em"&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;I stumbled upon this quite by accident: all you need do is put the content you want scrolled inside a &lt;code&gt;tbody&lt;/code&gt; tag, with &lt;code&gt;style='height:nn;overflow-y:auto'&lt;/code&gt; where &lt;code&gt;nn&lt;/code&gt; is the height you want your content. The &lt;code&gt;tr&lt;/code&gt; containing your headers goes &lt;i&gt;outside&lt;/i&gt; this &lt;code&gt;tbody&lt;/code&gt;. This has been obvious all along to people who understand CSS, but I'm only starting to get to grips with this stuff.&lt;br /&gt;&lt;br /&gt;Here's an example, hit view source to see how it works:&lt;br /&gt;&lt;br /&gt;&lt;table width="33%"&gt;&lt;tr&gt;&lt;th&gt;foo&lt;/th&gt;&lt;th&gt;bar&lt;/th&gt;&lt;/tr&gt;&lt;tbody style='height:5em;overflow-x:hidden;overflow-y:auto;'&gt;&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;y&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;y&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;y&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;x&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Beats a zillion lines of js and css to do the same thing, no?&lt;br /&gt;&lt;br /&gt;It works for Firefox 3, I haven't tested it elsewhere. If anybody out there still has IE, please let me know if it works!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; : if you want the whole table to scroll horizontally, too keeping the headers aligned with their columns (thanks, Olivier), put the it in a div with &lt;code&gt;style='width:nn;overflow-x:auto'&lt;/code&gt;. Thus:&lt;br /&gt;&lt;br /&gt;&lt;div style="width:5em;overflow-x:auto"&gt;&lt;table width="25em"&gt;&lt;tr&gt;&lt;th&gt;foo&lt;/th&gt;&lt;th&gt;bar&lt;/th&gt;&lt;th&gt;baz&lt;/th&gt;&lt;/tr&gt;&lt;tbody style='height:5em;overflow-x:hidden;overflow-y:auto;'&gt;&lt;tr&gt;&lt;td&gt;000.00&lt;/td&gt;&lt;td&gt;xxx&lt;/td&gt;&lt;td&gt;toto-titi&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;100.00&lt;/td&gt;&lt;td&gt;yyy&lt;/td&gt;&lt;td&gt;titi-didi&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;200.00&lt;/td&gt;&lt;td&gt;zzz&lt;/td&gt;&lt;td&gt;didi-dodo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;300.00&lt;/td&gt;&lt;td&gt;xxx&lt;/td&gt;&lt;td&gt;dodo-toto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;400.00&lt;/td&gt;&lt;td&gt;yyy&lt;/td&gt;&lt;td&gt;toto-titi&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;500.00&lt;/td&gt;&lt;td&gt;zzz&lt;/td&gt;&lt;td&gt;titi-didi&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;600.00&lt;/td&gt;&lt;td&gt;xxx&lt;/td&gt;&lt;td&gt;didi-dodo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;700.00&lt;/td&gt;&lt;td&gt;yyy&lt;/td&gt;&lt;td&gt;dodo-toto&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;800.00&lt;/td&gt;&lt;td&gt;zzz&lt;/td&gt;&lt;td&gt;toto-titi&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;900.00&lt;/td&gt;&lt;td&gt;xxx&lt;/td&gt;&lt;td&gt;titi-didi&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It's not so pretty - the vertical scrollbar is visible only if you've scrolled horizontally all the way to the right. But given that you can scroll with the keyboard or mouse wheel, perhaps that's not such a terrible thing ...&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/413755792" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/4989324994936906942/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=4989324994936906942" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/4989324994936906942?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/4989324994936906942?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/html-scroll-table-contents-without.html" title="HTML: Scroll table contents without scrolling headers" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-3156357384497058656</id><published>2008-10-07T13:44:00.004+02:00</published><updated>2008-11-07T13:17:30.786+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:17:30.786+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">Make Freemarker work with Prototype's Ajax.Request</title><content type="html">&lt;p style="margin:-1em;"&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;The popular &lt;a href="http://www.prototypejs.org/api/ajax/updater"&gt;Ajax.Updater&lt;/a&gt; from Prototype is great for getting little bits of HTML back from your server and sticking them in your client's page somewhere. But Prototype has some other tricks hidden up its sleeve. The &lt;a href="http://www.prototypejs.org/api/ajax/request"&gt;Ajax.Request&lt;/a&gt; utility (upon which Ajax.Updater is based) has a very nice feature: if the response comes with content-type "application/javascript" (or equivalent), it evaluates the response body as if it were javascript code. Imagine that: the server can send any arbitrary piece of code back to the client to be executed on the client's machine.&lt;br /&gt;&lt;br /&gt;Any kind of ajax-based state change could use this technique: a login, a deletion, an insertion. For example, after a deletion, you might wish to return this response:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;widget-deleted.ftl&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;${res.setContentType("application/javascript")};&lt;br /&gt;alert('widget ${widgetId} successfully deleted');&lt;br /&gt;$('div_for_widget_${widgetId}').remove();&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt; - to provide feedback to the user in two ways: an alert, followed by the disappearing of the dom element representing the deleted object.&lt;br /&gt;&lt;br /&gt;You might even find this technique useful to resolve the ages-old dilemma of whether to perform input validation on the server or on the client. Up to now, the correct answer has been (frustratingly) "both". But now you can avoid duplicating validation code in javascript: instead you have a javascript response that provides server-side validation-failed feedback to your user.&lt;br /&gt;&lt;br /&gt;Here's another example, for an application that takes comments, and requires a non-empty comment. The first indicates a successful comment submission:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;comment-ok.ftl&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;${res.setContentType("application/javascript")}&lt;br /&gt;alert("Your comments have been noted. Thank you.");&lt;br /&gt;$('commentForm').hide();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;And the second, failure:&lt;br /&gt;&lt;br /&gt;&lt;fieldset&gt;&lt;legend&gt;comment-failed.ftl&lt;/legend&gt;&lt;pre&gt;&lt;code&gt;${res.setContentType("application/javascript")}&lt;br /&gt;alert("You didn't enter a comment!");&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/fieldset&gt;&lt;br /&gt;&lt;br /&gt;Your controller decides which response template to evaluate after having performed the relevant validation. Webwork/Struts2 makes this decision based on its "struts.xml" configuration and the outcome of your action.&lt;br /&gt;&lt;br /&gt;The first line in each of these examples sets the content type of the response so that Prototype knows to interpret it as code. The MVC framework you are using should have inserted some variable in the Freemarker context to represent the response - in the case of Webwork/Struts2, it's called "res".&lt;br /&gt;&lt;br /&gt;Don't go overboard with Ajax though - keep in mind Ajax breaks bookmarkability and browsers' back/forward buttons. Distinguish between requests that access and display resources - a bookmarkable url is nice here, and actions that modify state on the server - Ajax.Request &lt;i&gt;might&lt;/i&gt; be useful here, unless the state change should take you to a new page.&lt;br /&gt;&lt;br /&gt;Let me know how this works for you!&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/413755793" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/3156357384497058656/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=3156357384497058656" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/3156357384497058656?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/3156357384497058656?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/10/make-freemarker-work-with-prototypes.html" title="Make Freemarker work with Prototype's Ajax.Request" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-245870861089746276</id><published>2008-09-30T14:36:00.006+02:00</published><updated>2008-11-07T13:17:50.678+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:17:50.678+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">Social Bookmarking Howto for Blogger</title><content type="html">&lt;blockquote&gt;&lt;/blockquote&gt;&lt;br /&gt;Attentive readers will have noticed the shiny new social bookmarking icon links around the bottom of my entries. I'm catching up with 2005 here. Last month I caught up with 2004 by including ads. You can't imagine how rich I'm feeling. Actually, you can ... but anyway. In 5 years, I'll add a flickr widget and be fully up to date with 2006. For that, I'll need something called a "photostream".&lt;br /&gt;&lt;br /&gt; ... You know, it's horrible when everyone thinks you're a geek but in fact you haven't a clue what's going on on the internet?&lt;br /&gt;&lt;br /&gt;Anyway, I followed &lt;a href="http://waronstuff.blogspot.com/2007/03/how-to-add-digg-and-delicious-icons-to.html"&gt;Enrico Suarvé's howto&lt;/a&gt; for adding these icons to a Blogger template. For someone who claims to be a hopeless blogger, he does an excellent job. Read his howto because I'm not going to repeat it here. &lt;br /&gt;&lt;br /&gt;I was able to make only one little improvement: instead of making my own "submitbuttons" CSS class as he recommends, I used Blogger's "item-action" and "icon-action" classes. So I have a little less work to do, and my Blogger template code looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre style="overflow:auto;"&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;span class='item-action'&gt;&lt;br /&gt;  &amp;lt;a Title='digg it' expr:href='&amp;quot;http://digg.com/submit?phase=3&amp;amp;url=&amp;quot; + data:post.url + &amp;quot;&amp;amp;title=&amp;quot; + data:post.title'&gt;&lt;br /&gt;    &amp;lt;img alt=' Digg ' border='0' class='icon-action' src='path/to/digg.png'/&gt;&lt;br /&gt;  &amp;lt;/a&gt;&lt;br /&gt; &lt;br /&gt;  &amp;lt;a Title='delicious' expr:href='&amp;quot;http://del.icio.us/post?url=&amp;quot; + data:post.url + &amp;quot;&amp;amp;title=&amp;quot; + data:post.title'&gt;&lt;br /&gt;    &amp;lt;img alt='Delicious' border='0' class='icon-action' src='http://path/to/delicious.png'/&gt;&lt;br /&gt;  &amp;lt;/a&gt;&lt;br /&gt; &lt;br /&gt;  &amp;lt;a Title='reddit' expr:href='&amp;quot;http://reddit.com/submit?url=&amp;quot; + data:post.url'&gt;&lt;br /&gt;    &amp;lt;img alt='Reddit' border='0' class='icon-action' src='http://path/to/reddit.png'/&gt;&lt;br /&gt;  &amp;lt;/a&gt;&lt;br /&gt; &lt;br /&gt;  &amp;lt;a Title='SlashDot' expr:href='&amp;quot;http://slashdot.org/bookmark.pl?url=&amp;quot; + data:post.url + &amp;quot;&amp;amp;title=&amp;quot; + data:post.title'&gt;&lt;br /&gt;    &amp;lt;img alt='SlashDot' border='0' class='icon-action' src='http://path/to/slashdot.png'/&gt;&lt;br /&gt;  &amp;lt;/a&gt;&lt;br /&gt;&amp;lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Paste this into your Blogger html template somewhere sensible. Tweak it until you like it. And upload the relevant icons somewhere so you can link to them.&lt;br /&gt;&lt;br /&gt;At first I thought I should include furl and fark and magnolia and a zillion other SB sites I'd never even heard of, but in the end I figured that was kind of immodest (and you should know that I'm the modestest person you're ever likely to meet), so I retained only sites that I use: &lt;a href="http://reddit.com"&gt;&lt;img src="http://daltonhouse.net/reddit.png" alt="reddit" title="reddit"/&gt;&lt;/a&gt; and &lt;a href="http://delicious.com"&gt;&lt;img src="http://daltonhouse.net/delicious.png" alt="delicious" title="delicious"/&gt;&lt;/a&gt;, and occasionally: &lt;a href="http://digg.com"&gt;&lt;img src="http://daltonhouse.net/digg.png" alt="digg" title="digg"/&gt;&lt;/a&gt;, and &lt;a href="http://slashdot.com"&gt;&lt;img src="http://daltonhouse.net/slashdot.png" alt="slashdot" title="slashdot"/&gt;&lt;/a&gt;. (Hey, I even have an account on &lt;img src="http://daltonhouse.net/reddit.png" alt="reddit"/&gt; and &lt;img src="http://daltonhouse.net/delicious.png" alt="delicious"/&gt;, now that's &lt;i&gt;so&lt;/i&gt; 2007! I'm getting there!)&lt;br /&gt;&lt;br /&gt;Damn. I've just broken my resolution to not blog about blogging. Again!&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/407252420" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/245870861089746276/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=245870861089746276" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/245870861089746276?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/245870861089746276?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/social-bookmarking-howto-for-blogger.html" title="Social Bookmarking Howto for Blogger" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-2909488728652862372</id><published>2008-09-29T14:19:00.001+02:00</published><updated>2008-11-07T13:18:05.685+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:18:05.685+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">&lt;center&gt; is deprecated: use CSS instead!</title><content type="html">&lt;blockquote&gt;&lt;/blockquote&gt;&lt;br /&gt;Being from the 1990's, I like to use tables and the good ol' &amp;lt;center&amp;gt; tag to control my layout. Apparently, that's not polite any more. When I get my CSS Guru 9th-Dan Black Belt certification, I'll let you know why.&lt;br /&gt;&lt;br /&gt;In the meantime, the &lt;a href="http://www.w3.org/Style/Examples/007/center.html"&gt;totally modern way to centre a block&lt;/a&gt;, I learned today, is to give it a fixed width and set left and right margins to "auto". Thus:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;.centred {&lt;br /&gt;  margin-left: auto;&lt;br /&gt;  margin-right: auto;&lt;br /&gt;  width: 10em;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="width:100%;border:2px solid yellow;"&gt;&lt;br /&gt;  &lt;div style="margin-left:auto;margin-right:auto;width:10em;border:2px solid blue;"&gt;&lt;br /&gt;    The block containing this text is centred relative to its parent. Note that the text itself isn't centred. Use &lt;code&gt;text-align:center&lt;/code&gt; for that.&lt;br /&gt;  &lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The difference, &lt;a href="http://www.acronymfinder.com/As-Far-As-I-Can-Tell-(AFAICT).html"&gt;afaict&lt;/a&gt;, is that nowadays the element to be centered is responsible for its centredness, whereas in the old days, the element's parent decided how the child would behave. Very modern, them dudes at &lt;a href="http://www.w3.org/"&gt;w3c&lt;/a&gt;. I try to let my children decide how to behave, too. Who would have thought CSS teaches family values?&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/407231703" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/2909488728652862372/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=2909488728652862372" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/2909488728652862372?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/2909488728652862372?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/is-deprecated-use-css-instead.html" title="&amp;lt;center&amp;gt; is deprecated: use CSS instead!" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-5590759307853557876</id><published>2008-09-26T23:20:00.005+02:00</published><updated>2008-09-28T19:13:08.802+02:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-09-28T19:13:08.802+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="nonsense" /><title type="text">Keystroke Strikes Again</title><content type="html">I wanted to preview my last blog entry just to check everything was ok and I hit control-F9, my intellij shortcut for "build project". This is very, very &lt;a href="http://www.wordreference.com/fren/grave"&gt;grave&lt;/a&gt;. It's like when I take out my bus pass to get into my house. Or this:&lt;br /&gt;&lt;br /&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_7sjE3SRdZhk/SN-6vVvm_ZI/AAAAAAAAAGA/9kEE0Psdj7Q/s400/helpf1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5251121012990737810" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:50%;"&gt;image copyright ??? please let me know!&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/404142031" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/5590759307853557876/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=5590759307853557876" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5590759307853557876?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/5590759307853557876?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/keystroke-strikes-again.html" title="Keystroke Strikes Again" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_7sjE3SRdZhk/SN-6vVvm_ZI/AAAAAAAAAGA/9kEE0Psdj7Q/s72-c/helpf1.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-934715574512417368</id><published>2008-09-26T16:46:00.007+02:00</published><updated>2008-11-07T13:36:11.241+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:36:11.241+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">One from the archives</title><content type="html">Ten years ago I wrote a little java applet (yes, an applet. Imagine that!) that plays &lt;a href="http://en.wikipedia.org/wiki/Reversi"&gt;Reversi, aka Othello&lt;/a&gt; - as an exercise for a Game Theory course I was taking.&lt;br /&gt;&lt;br /&gt;I dug it up, and amazingly it compiles, runs and even works!&lt;br /&gt;&lt;br /&gt;You can &lt;a href="http://daltonhouse.net/reversi/"&gt;play against my little Reversi here&lt;/a&gt; (and it's totally free). &lt;br /&gt;&lt;br /&gt;The program is a fine example of alpha-beta game tree pruning, using an evaluation function which takes into account the positions of the stones as well as the mobility of each player.&lt;br /&gt;&lt;br /&gt;I'm sure one could write a much more sophisticated Reversi player, but even with low lookahead (4) it crushes me.&lt;br /&gt;&lt;br /&gt;Download and play with the source code too - I'm releasing it open-source (under GPL). Just don't judge me by it - it was ten years ago, ok? There are member variables with names starting with uppercase letters ... there are ... ugh ... &lt;i&gt;comments&lt;/i&gt; ... and all sorts of other atrocities in there. OMG there was even a &lt;code&gt;new String("foo");&lt;/code&gt; in there. I got rid of those, but it was too much effort to clean it up entirely. I don't write like that any more. I would like to say I never did, but I wasn't born perfect either ...&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/404142032" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/934715574512417368/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=934715574512417368" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/934715574512417368?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/934715574512417368?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/one-from-archives.html" title="One from the archives" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-3114301977777874149</id><published>2008-09-24T11:38:00.004+02:00</published><updated>2008-11-07T13:18:25.080+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:18:25.080+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">Expand div to contain floated child elements</title><content type="html">I am sure I am one among many thousands who will be eternally indebted to Quirksmode for its wealth of browser wisdom. Today I learned &lt;a href="http://www.quirksmode.org/css/clearing.html"&gt;how to make a container expand to surround all of its floated child elements&lt;/a&gt;. On the container, put&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;style="overflow:auto;"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all! At least, for firefox - other browsers need &lt;code&gt;width&lt;/code&gt; as well.&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/401651843" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/3114301977777874149/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=3114301977777874149" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/3114301977777874149?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/3114301977777874149?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/expand-div-to-contain-floated-child.html" title="Expand div to contain floated child elements" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-4987479703950115950</id><published>2008-09-19T15:44:00.005+02:00</published><updated>2008-11-07T13:18:39.691+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:18:39.691+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">A simple Javascript function to move elements</title><content type="html">&lt;blockquote&gt;&amp;nbsp;&lt;/blockquote&gt;The proper way to drag divs or images or other html elements using javascript is, of course, to use a well-known and established framework for doing it - &lt;a href="http:script.aculo.us"&gt;scriptaculous&lt;/a&gt;, for example. Even better would be to use &lt;a href="http://developer.mozilla.org/en/Drag_and_drop_events"&gt;Firefox's fancy new drag/drop handlers&lt;/a&gt; (as soon as they work, that is). But if you suffer, as I do, from &lt;a href="http://en.wikipedia.org/wiki/Not_Invented_Here"&gt;NIH Syndrome&lt;/a&gt;, here's a roll-your-own version. &lt;br /&gt;&lt;br /&gt;Just so we agree on what dragging is: the user clicks on an object, and while moving the mouse, the object follows the mouse around, until the user releases the mouse button. In the version below, we distinguish between the &lt;code&gt;target&lt;/code&gt; (the thing to be moved), and the &lt;code&gt;handle&lt;/code&gt; (the thing the user clicks on in order to move the target. A titlebar, for example).&lt;br /&gt;&lt;br /&gt;The following has been tested on FF3 and almost certainly won't work on Exploder. But there are plenty of resources out there on hacking IE until it works.&lt;br /&gt;&lt;br /&gt;Enough talking, here's the good stuff:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;var makeMovable = function(target, handle) {&lt;br /&gt;  target = $(target);&lt;br /&gt;&lt;br /&gt;  $(handle).onmousedown = function(event) {&lt;br /&gt;    var initialXOffset = target.offsetLeft - event.pageX;&lt;br /&gt;    var initialYOffset = target.offsetTop - event.pageY;&lt;br /&gt;&lt;br /&gt;    document.onmousemove = function(event) {&lt;br /&gt;      target.style.left = event.pageX + initialXOffset;&lt;br /&gt;      target.style.top = event.pageY + initialYOffset; &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    document.onmouseup = function() {&lt;br /&gt;      document.onmousemove = null;&lt;br /&gt;      document.onmouseup = null;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Once your HTML elements are defined, all you need do is call makeMovable with the relevant params:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&amp;lt;div id="myContent" style="position:absolute;left:100px;top:100px;"&amp;gt;&lt;br /&gt;  &amp;lt;img id="myHandle" src="/images/move_handle.png" /&amp;gt;&lt;br /&gt;  you can move this text by dragging the handle&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;      makeMovable("myContent", "myHandle");&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;(Of course, you might prefer to take an &lt;a href="http://www.conandalton.net/2008/08/showhide-element-using-unobtrusive.html"&gt;unobtrusive&lt;/a&gt; approach and have all movable elements wired up using identifiers and class names. Exercise for reader).&lt;br /&gt;&lt;br /&gt;So here's what's going on. The principle is simple: &lt;code&gt;makeMovable&lt;/code&gt; sets a "mousedown" listener on the handle - since that's what we're waiting for our user to click on. Once mousedown is triggered, we calculate how far away the mouse click was from the corner of the target. This gets stored in &lt;code&gt;initialXOffset&lt;/code&gt; and &lt;code&gt;initialYOffset&lt;/code&gt;. The goal now is just to make sure that the target is always this distance from the mouse, as the mouse moves around.&lt;br /&gt;&lt;br /&gt;So we need a mousemove listener. Curiously, mousemove has to be on &lt;code&gt;document&lt;/code&gt; - you would think it should go on the handle instead because the mouse is necessarily over the handle when we get the initial mousedown - but unless it's &lt;code&gt;document.onmousemove&lt;/code&gt;, Firefox gets all confused and thinks you're trying to select text at the same time as moving stuff around.&lt;br /&gt;&lt;br /&gt;The mousemove listener does the real work: it sets the &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; style elements of the target so as to maintain the original offset from the mouse position. Note that the original style declaration of the target div includes &lt;code&gt;position:absolute&lt;/code&gt;. Without this, the top and left style elements are completely ignored, and you won't be able to move anything.&lt;br /&gt;&lt;br /&gt;Remember to &lt;code&gt;return false;&lt;/code&gt; at the end of onmousedown. Without this line, it mostly works, but FF also initiates its internal drag handler as well as yours, resulting in angry users tearing at their screens in frustration, and the resulting drag behaviour isn't at all intuitive or pleasant.&lt;br /&gt;&lt;br /&gt;The last word goes to onmouseup: once the user lets go of the mouse, we should restore document.onmousemove and document.onmouseup to whatever they were before. I'm being lazy here and setting them to &lt;code&gt;null&lt;/code&gt;. Umm ... exercise for reader ...&lt;br /&gt;&lt;br /&gt;If you're not using Prototype, replace &lt;code&gt;$(...)&lt;/code&gt; with &lt;code&gt;document.getElementById(...)&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;You can write similar code for resizing an element - you would adjust the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; style elements instead of &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Leave a comment if this works for you, or more importantly, if it doesn't. Leave a comment even if there's some other javascript question bugging you. I'm curious.&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/400284098" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/4987479703950115950/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=4987479703950115950" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/4987479703950115950?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/4987479703950115950?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/simple-javascript-function-to-move.html" title="A simple Javascript function to move elements" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-6927576467480761669</id><published>2008-09-19T14:13:00.004+02:00</published><updated>2008-11-07T13:18:54.501+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:18:54.501+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="nonsense" /><title type="text">On Learning and Falling</title><content type="html">Pascal Pratmarty's comment in a recent &lt;a href="http://extremepill.blogspot.com/2008/09/art-of-conflict-phase-2-alignment.html"&gt;article on conflict resolution&lt;/a&gt; triggered some painful yet fond memories of learning ice-skating. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;"Remember this: to be a learner, you’ve got to be willing to be a fool."&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This will sound very odd if you've only tried skating once or twice: it's very difficult to fall. Once you figure out balance, your body clings to it, not letting your delicate elbows and knees hit that hard, cold ice.&lt;br /&gt;&lt;br /&gt;I had learned to go forward, but people all around me were spinning, coasting backwards, jumping, and doing those awesome screech-skid stops. Envy filled me.&lt;br /&gt;&lt;br /&gt;Your body learns these moves, not your mind, and can learn only by experiencing them. I had to push my reluctant flesh beyond what it was comfortable with. I had to force myself to fall.&lt;br /&gt;&lt;br /&gt;It worked. Now, I can wiggle backwards around the rink, mostly avoiding collisions. I have yet to learn stopping though ...&lt;br /&gt;&lt;br /&gt;Curiously, the pain of falling is not what inhibited me the most - you get over that fast. It was the eyes of all those people. I knew they were looking at me thinking: the fool. &lt;br /&gt;&lt;br /&gt;I try hard, but I wish I could be a better fool.&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/397202975" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/6927576467480761669/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=6927576467480761669" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/6927576467480761669?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/6927576467480761669?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/on-learning-and-falling.html" title="On Learning and Falling" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-1312293931399729801</id><published>2008-09-17T22:20:00.006+02:00</published><updated>2008-11-07T13:19:19.113+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:19:19.113+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><category scheme="http://www.blogger.com/atom/ns#" term="france" /><category scheme="http://www.blogger.com/atom/ns#" term="business" /><title type="text">Hosting in France</title><content type="html">&lt;blockquote&gt;&lt;small&gt;&amp;nbsp;&lt;/small&gt;&lt;/blockquote&gt;&lt;br /&gt;It's hard to find a decent hosting provider. I used &lt;a href="http://www.textdrive.com"&gt;Textdrive&lt;/a&gt; for a while, but my rails processes kept on getting killed due to some limitation on memory consumption. I'm sure I was doing something awfully wrong (besides using fcgi), but I switched to &lt;a href="http://www.slicehost.com"&gt;slicehost&lt;/a&gt; (with apache mod-load-balancer and mongrel, a big improvement already). Still, it feels horribly slow, and again, being a bit of a rails newbie, I'm sure I'm doing something wrong.&lt;br /&gt;&lt;br /&gt;So, to get to the point, I looked for a hosting provider in France to see if I could blame the network instead of me. Ouch. One look at the &lt;a href="http://www.webhostingtalk.com/showthread.php?t=672553"&gt;webhostingtalk&lt;/a&gt; review discouraged me from ever going near OVH. &lt;a href="http://www.dedibox.fr"&gt;Dedibox&lt;/a&gt;, on the other hand, didn't seem to have such reputation issues, and their offer looks really good (physical dedicated server, 2GB RAM for EUR30/month compared to Slicehost's 256MB RAM VPS at $20/month). They have an absurd restriction on selling only to French residents, but I have a workaround for that. So I started the signup process, expecting to type in my name, email, credit card number, and choice of plan, and then be up and running in a few minutes. Slicehost sets the bar very high in this area. But no - they needed my phone number too, which they needed to confirm by sending me an SMS with a code I needed in order to proceed with signup.&lt;br /&gt;&lt;br /&gt;Credit card? No - they want your bank account. With a severe warning in red that they will sue you if you deliberately offer false details. And then you have to print out your bank details and sign an authorisation for Dedibox to charge your account, and mail it to them. Surprisingly, they don't have an option for payment by sheep or goat. I was a little bit uncomfortable typing in all this bank information without numerous assurances in the surrounding text that no charge would be made unless and until I had my server, but I had little choice if I wanted to give these guys a chance.&lt;br /&gt;&lt;br /&gt;Ok ok now you're thinking well what do you expect, that's French bureaucracy for you. Well for once in my life I figured I would click on that pesky "terms and conditions" link that I was about to claim to have read, understood, and accepted. Pretty quickly, I had read, understood, and totally not accepted that they "guarantee" that my server will be ready in a maximum of 35 days.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:35pt;font-weight:bold"&gt;35 days ???? !!!!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;"Wtf?", one might think. And: if they fail to deliver in 35 days, I have the right to cancel the contract - by sending them a registered letter to that effect. Double wtf.&lt;br /&gt;&lt;br /&gt;So the long and the short of it is, I didn't click on the "I have read and accept the terms and conditions blah blah blah" box, and I didn't click on "proceed", and I'm not getting a Dedibox. I wonder who does? Do you have to hate your customers in order to be successful in France? Is that what they teach in business schools?&lt;br /&gt;&lt;br /&gt;Now that the US is falling to pieces, the rest of the world is going to scramble to take its place on the top of the economic dunghill. Does anyone know where I might get my CV translated into Chinese or Hindi?&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/396092061" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/1312293931399729801/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=1312293931399729801" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/1312293931399729801?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/1312293931399729801?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/hosting-in-france.html" title="Hosting in France" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-1783959015397337307</id><published>2008-09-16T20:43:00.006+02:00</published><updated>2008-11-07T13:19:48.443+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:19:48.443+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="nonsense" /><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">An Incredible Thread</title><content type="html">A few weeks ago, I read &lt;a href="http://en.wikipedia.org/wiki/Halting_State"&gt;Halting State&lt;/a&gt; by Charles Stross. &lt;font color="red"&gt;WARNING: potential spoiler follows&lt;/font&gt; - It's a novel set in the future, about a bank robbery in a virtual world, said robbery having far-reaching implications for global cyberterrorisim, including compromise of Europe's root servers and digital certificates, handing control of the continent's core infrastructure (police, transport, banking, everything) to a gang of Chinese hackers who possibly think it's all just a game. The highlight is when Jack looks up to the virtual sky at a flock of virtual birds flying past and realises the bad guys are encoding messages in the detaild of their flight paths. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"The traffic looks like game-play to [...] NSA or whoever's sniffing packets; looking in-game for characters run by Abdullah and Salim holding private chat about blowing up the White House garden gnomes won't get you a handle on what's going on because they're not using the game as a lydic universe to chat in, they're using it as a transport layer! They're tunnelling TCP/IP over &lt;a href="http://en.wikipedia.org/wiki/Dungeons_&amp;_Dragons"&gt;AD&amp;D&lt;/a&gt;!" &lt;small&gt;(p.299)&lt;/small&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Oscar Wilde said "Life imitates Art". And today Wired.com describes US Congress and Pentagon fears of &lt;a href="http://blog.wired.com/defense/2008/09/world-of-warcra.html"&gt;terrorists chatting in World of Warcraft&lt;/a&gt; to plot an &lt;b&gt;ATTACK ON AMERICA&lt;/b&gt; OMG OMG GET THE DUCT TAPE HONEY WHERES MY .32??!!!1!! &lt;br /&gt;&lt;br /&gt;A Credible Threat, in order to be useful to a scaremonger, must be easily understood by the population purportedly under attack. A heavy reliance on popolar stereotypes is customary, but by broadening the stereotype to encompass virtual reality, the threat authors postpone threat fatigue and get better ROI on their fiction investment. The problem with Stross's scenario is that it's far too technical. A politucian wouldn't have a clue how to explain it, while a smart terrorist (bad, bad guys) could do a lot worse, but the guy on the street has mostly figured out online chat.&lt;br /&gt;&lt;br /&gt;Those bad, bad terrorists wouldn't even have to go as far as Strosss describes. Combine basic cryptography, steganography, and any of the myriad ways of communicating (internet or otherwise), and you can &lt;i&gt;undetectably send any message you want&lt;/i&gt; while it looks like you're only sending a picture of the kids to their grandmother in a village in Saudi Arabia. Or tunnel encrypted instructions through common misppellings or bad grammar in bot-generated chat in any of a million online forums. Wake up, CIA, you can't detect this stuff! The only solution is too shut down the world's entire telecommunications infrastructure - but that would be bad for business.&lt;br /&gt;&lt;br /&gt;Of course, the online subversion goes he other way, too. Did you know that &lt;a href="http://www.guardian.co.uk/technology/2008/jan/14/facebook"&gt;the CIA runs Facebook&lt;/a&gt;? Its &lt;a href="http://en.wikipedia.org/wiki/Hamming_distance"&gt;Hamming Distance&lt;/a&gt; from "&lt;a href="https://www.cia.gov/library/publications/the-world-factbook/"&gt;FactBook&lt;/a&gt;" (as in "The World Factbook") is &lt;b&gt;only 1&lt;/b&gt;, so it's much easier for CIA agents to tipe. I don't know whether other Facebook constpiracy theorists had spotted that. Did you spot it?&lt;br /&gt;&lt;br /&gt;What is the Truth? Somewhere out there, someone is laughing their arse off. Is it the bad guys, or the badder guys?&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/394573750" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/1783959015397337307/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=1783959015397337307" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/1783959015397337307?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/1783959015397337307?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/incredible-thread.html" title="An Incredible Thread" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-8567352802750767337</id><published>2008-09-16T14:29:00.011+02:00</published><updated>2008-11-07T13:20:01.007+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:20:01.007+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">Subpixel Rendering</title><content type="html">I just read about &lt;a href="http://en.wikipedia.org/wiki/Subpixel_rendering"&gt;sub-pixel rendering&lt;/a&gt; on good ol' wikipedia. If you've ever needed to draw a vertical line exactly between two pixels, subpixel rendering is what you're looking for.&lt;br /&gt;&lt;br /&gt;On an LCD screen, each pixel is three little bars, one red, one green, one blue. All on looks like white, all off like black.&lt;br /&gt;&lt;br /&gt;The subpixel trick is to light, for example, the blue bar of one pixel, and the red and green bars of the next pixel to the right. The effect is a white pixel, because you have a red bar, a green bar, and a blue bar, all together, all lit up.&lt;br /&gt;&lt;br /&gt;Here's a little image I made with iconfu just now:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="border:1px solid #CCC;padding:20px;" src="http://1.bp.blogspot.com/_7sjE3SRdZhk/SM-wx0Ng9uI/AAAAAAAAAFI/ppQKBGqrDTQ/s400/subpixel2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5246606460785325794" /&gt;&lt;br /&gt;&lt;br /&gt;Below is how it looks enlarged, so you see each pixel value. Notice that there are no black or white vertical lines. The appearance of black and white is a side-effect of the way sub-pixels are lined up on an LCD screen - and if you're using a CRT or an LCD that doesn't order its subpixels the same way, you'll be wondering what the hell I'm talking about.&lt;br /&gt;&lt;br /&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_7sjE3SRdZhk/SM-wx79_CdI/AAAAAAAAAFA/9mA1xAq79cY/s400/Picture+14.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5246606462867671506" /&gt;&lt;br /&gt;&lt;br /&gt;Ok, that was the enlarged image, but in case you're having doubts, this is how it looks if you take a traditional screenshot (ie, with your camera). Here you can see how each pixel is lit and where the black and white lines come from.&lt;br /&gt;&lt;br /&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_7sjE3SRdZhk/SM-0_PdfHQI/AAAAAAAAAFY/wy_HYr_NVLY/s400/Picture+15.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5246611089484881154" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;You can also see that this trick can't work horizontally: there is no alignment of pixels to exploit in this direction. This is the same image, rotated:&lt;br /&gt;&lt;br /&gt;&lt;img style="border:1px solid #CCC;padding:20px;" src="http://1.bp.blogspot.com/_7sjE3SRdZhk/SM-x1b8idII/AAAAAAAAAFQ/ecPZiSQkvDM/s400/subpixel3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5246607622502773890" /&gt;&lt;br /&gt;&lt;br /&gt;And if we rotate it once more, the alignment doesn't work at all, and we lose our black and white lines ...&lt;br /&gt;&lt;br /&gt;&lt;img style="border:1px solid #CCC;padding:20px;" src="http://2.bp.blogspot.com/_7sjE3SRdZhk/SM-2SIxMF9I/AAAAAAAAAFg/sgkUXYVc3As/s400/subpixel4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5246612513617614802" /&gt;&lt;br /&gt;&lt;br /&gt;You can't imagine how many problems this solves. Not world hunger, unfortunately, but let's say you're drawing a vertical arrow in a 16x16 icon. You want a sharp point on your arrow, but if you make the tip 1 pixel wide the arrow won't be centered in the icon. Subpixel rendering is the solution! Similarly, you can control how curvy a curve is by lighting only the pixels that make your curve smoother: a curve going up and right (the top-left corner of a rounded-corners box, for example), would benefit from a little dash of blue on the outside, whereas the curves on the other side would benefit from a little bit of red.&lt;br /&gt;&lt;br /&gt;Now, wasn't that fascinating ... back to work ...&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/394210867" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/8567352802750767337/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=8567352802750767337" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/8567352802750767337?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/8567352802750767337?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/subpixel-rendering.html" title="Subpixel Rendering" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_7sjE3SRdZhk/SM-wx0Ng9uI/AAAAAAAAAFI/ppQKBGqrDTQ/s72-c/subpixel2.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-802463530586784041</id><published>2008-09-16T10:14:00.006+02:00</published><updated>2008-11-07T13:29:15.461+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:29:15.461+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="photos" /><category scheme="http://www.blogger.com/atom/ns#" term="france" /><title type="text">please don't fix this</title><content type="html">My iPhoto (6.0.6) has an awesome bug (at least I suppose it's a bug) if you adjust exposure down after having fiddled a bit with light levels. Resulting in this magnificent stained glass from &lt;a href="http://maps.google.com/maps?q=paroisse+saint+merry+paris"&gt;St. Merry&lt;/a&gt;, next to the Pompidou Centre:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7sjE3SRdZhk/SM9sbA6V6RI/AAAAAAAAAEo/nK9DrL4aPcM/s1600-h/saint-merry-crazy-iphoto-exposure.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_7sjE3SRdZhk/SM9sbA6V6RI/AAAAAAAAAEo/nK9DrL4aPcM/s400/saint-merry-crazy-iphoto-exposure.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5246531302266890514" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The statue of the saint holding the baby reminds me how intimately connected religion and politics are.&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/394007333" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/802463530586784041/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=802463530586784041" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/802463530586784041?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/802463530586784041?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/please-dont-fix-this.html" title="please don't fix this" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_7sjE3SRdZhk/SM9sbA6V6RI/AAAAAAAAAEo/nK9DrL4aPcM/s72-c/saint-merry-crazy-iphoto-exposure.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4089129643157405806.post-4495276116350506087</id><published>2008-09-05T13:06:00.004+02:00</published><updated>2008-11-07T13:20:34.291+01:00</updated><app:edited xmlns:app="http://purl.org/atom/app#">2008-11-07T13:20:34.291+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software" /><title type="text">List foreign key constraints in Oracle and MySQL</title><content type="html">Sometimes you want a list of all the associations in your database. You're supposed to know this from your Hibernate mappings, or from your ActiveRecord declarations, but sometimes you just need to dig this stuff out of a legacy database with its cryptic prehistoric naming conventions layered on top of one another like geological rock strata.&lt;br /&gt;&lt;br /&gt;Anyway, I digress. As you might imagine, this is a simple select in MySQL, and a four-table join in Oracle.&lt;br /&gt;&lt;br /&gt;Suppose you've had the original idea of building a brilliant e-commerce site. You have Clients, Orders, Products, and LineItems, with the usual associations.&lt;br /&gt;&lt;br /&gt;This is the kind of output you want:&lt;pre&gt;&lt;br /&gt;+-----------------------+-------------+&lt;br /&gt;| foreign key           | references  |&lt;br /&gt;+-----------------------+-------------+&lt;br /&gt;| orders.client_id      | clients.id  |&lt;br /&gt;| line_items.order_id   | orders.id   |&lt;br /&gt;| line_items.product_id | products.id |&lt;br /&gt;+-----------------------+-------------+&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With MySQL:&lt;br /&gt;&lt;pre&gt;select &lt;br /&gt;    concat(table_name, '.', column_name) as 'foreign key',  &lt;br /&gt;    concat(referenced_table_name, '.', referenced_column_name) as 'references'&lt;br /&gt;from&lt;br /&gt;    information_schema.key_column_usage&lt;br /&gt;where&lt;br /&gt;    referenced_table_name is not null;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;With Oracle (9-ish, probably 10 too, it's ages since I've actually used this)&lt;br /&gt;&lt;pre&gt;select&lt;br /&gt;    col.table_name || '.' || col.column_name as foreign_key,&lt;br /&gt;    rel.table_name || '.' || rel.column_name as references&lt;br /&gt;from&lt;br /&gt;    user_tab_columns col&lt;br /&gt;    join user_cons_columns con &lt;br /&gt;      on col.table_name = con.table_name &lt;br /&gt;     and col.column_name = con.column_name&lt;br /&gt;    join user_constraints cc &lt;br /&gt;      on con.constraint_name = cc.constraint_name&lt;br /&gt;    join user_cons_columns rel &lt;br /&gt;      on cc.r_constraint_name = rel.constraint_name &lt;br /&gt;     and con.position = rel.position&lt;br /&gt;where&lt;br /&gt;    cc.constraint_type = 'R'&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I cry when I behold the beauty of Oracle's meta data model. But we'll hold off the religious wars for another day. I know it's not your fault you're using Oracle.&lt;br /&gt;&lt;br /&gt;HTH.&lt;div class="blogger-post-footer"&gt;copyright conan dalton 2007-2008 feel free to quote please link back&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/PublicInterface/~4/384124998" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.conandalton.net/feeds/4495276116350506087/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4089129643157405806&amp;postID=4495276116350506087" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/4495276116350506087?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4089129643157405806/posts/default/4495276116350506087?v=2" /><link rel="alternate" type="text/html" href="http://www.conandalton.net/2008/09/list-foreign-key-constraints-in-oracle.html" title="List foreign key constraints in Oracle and MySQL" /><author><name>conan</name><uri>http://www.blogger.com/profile/03401726635379568413</uri><email>noreply@blogger.com</email></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry></feed>
