Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
211 lines (179 sloc) 583 KB
-- MySQL dump 10.11
--
-- Host: localhost Database: better_log_development
-- ------------------------------------------------------
-- Server version 5.0.37-log
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `comments`
--
DROP TABLE IF EXISTS `comments`;
CREATE TABLE `comments` (
`id` int(11) NOT NULL auto_increment,
`post_id` int(11) NOT NULL,
`author` varchar(255) NOT NULL,
`author_url` varchar(255) NOT NULL,
`author_email` varchar(255) NOT NULL,
`author_openid_authority` varchar(255) NOT NULL,
`body` text NOT NULL,
`body_html` text NOT NULL,
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `index_comments_on_created_at` (`created_at`),
KEY `index_comments_on_post_id` (`post_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `comments`
--
LOCK TABLES `comments` WRITE;
/*!40000 ALTER TABLE `comments` DISABLE KEYS */;
INSERT INTO `comments` VALUES (6,5,'Izza','','','','Oh my god I agree with the cleaning thing so much.\r\n\r\nWe need to make a more specific roster in our house. Even I\'ve become pretty lazy and last year I was the nazi. Or the nazi who really just did everything and suffered in silence whilest bagging the hell out of everyone on my lj. James does absolutely nothing though. However much nothing I said the Maddy\'s did last year, James does more nothing.\r\n\r\nA full sink of dishes still annoys me more than anything. It has happened to me so often that Maddy has made some elaborate meal before going out for the remainder of the night and just left a mile high pile of dishes in both sinks and I come out ready to make myself dinner, look at the pile, and most of the time just make myself a sandwhich and go to bed frustrated and hungry.\r\n\r\n*is the very first person to comment on your blog*','<p>Oh my god I agree with the cleaning thing so much.</p>\n\n\n <p>We need to make a more specific roster in our house. Even I&#8217;ve become pretty lazy and last year I was the nazi. Or the nazi who really just did everything and suffered in silence whilest bagging the hell out of everyone on my lj. James does absolutely nothing though. However much nothing I said the Maddy&#8217;s did last year, James does more nothing.</p>\n\n\n <p>A full sink of dishes still annoys me more than anything. It has happened to me so often that Maddy has made some elaborate meal before going out for the remainder of the night and just left a mile high pile of dishes in both sinks and I come out ready to make myself dinner, look at the pile, and most of the time just make myself a sandwhich and go to bed frustrated and hungry.</p>\n\n\n <p><strong>is the very first person to comment on your blog</strong></p>','2006-12-29 07:15:57','2007-01-22 10:37:40'),(7,4,'Daphne','http://dinnersuck.livejournal.com','daphneshum@fastmail.com.au','','When did you stop being vegan? Did it not work out?','<p>When did you stop being vegan? Did it not work out?</p>','2006-12-29 09:27:24','2007-01-22 10:37:47'),(8,3,'Mr Hatton','','','','*frowns*. I will be forwarding this URL to my mother!','<p><strong>frowns</strong>. I will be forwarding this <span class=\"caps\">URL</span> to my mother!</p>','2006-12-30 04:51:28','2007-01-22 10:37:54'),(9,5,'Zooba','http://www.byteclub.net/blog/zooba','zooba@aanet.com.au','','I have a feeling I\'m going to be the low tolerance guy this year. Been a while since I was in shared accomodation, but there was a roster last time :-)','<p>I have a feeling I&#8217;m going to be the low tolerance guy this year. Been a while since I was in shared accomodation, but there was a roster last time :-)</p>','2007-01-01 10:43:58','2007-01-22 10:37:21'),(10,5,'Luke Corbin','http://www.morbidboard.com','luke@noreality.net','','Hey now! I\'ll admit where the bathroom is concerned I\'m pretty unruffled by a bit of mould, but the dirty kitchen is just as much your doing as it is mine.\r\n\r\nLuke\'s bad habit: \r\n - Leaving utensils in the sink for upwards of six weeks\r\n\r\nXav\'s bad habit:\r\n - Forgetting what dirty dishes were his','<p>Hey now! I&#8217;ll admit where the bathroom is concerned I&#8217;m pretty unruffled by a bit of mould, but the dirty kitchen is just as much your doing as it is mine.</p>\n\n\n <p>Luke&#8217;s bad habit: \n &#8211; Leaving utensils in the sink for upwards of six weeks</p>\n\n\n <p>Xav&#8217;s bad habit:\n &#8211; Forgetting what dirty dishes were his</p>','2007-01-01 11:41:30','2007-01-22 10:37:16'),(11,2,'Zooba','http://www.byteclub.net/blog/zooba','zooba@aanet.com.au','','Interesting thoughts. This is probably one reason why debating is so popular with staff and parents in schools - it promotes thought into differing opinions, even if they conflict with the person\'s present ideas. The implicit requirement of placing the team above the individual would/should/may cause a person to see an issue from the \'other side\'.\r\n\r\nProbably the most obvious debating topic to demonstrate this issue is the existence of God or a god. I personally am not religious and will gladly argue for the non-existence of any active deity. However, to argue that a god does exist would require stretching my mind into areas where it does not normally go, simply because it has already decided.\r\n\r\nAttempting to justify the opposite assumption is a beneficial exercise. The scientific method is based entirely on this and many important mathematical proofs are \"proof by contradiction\". However, even if the original assumption is not disproved, the wording and description of why it is correct will improve.\r\n\r\nIn terms of a debate, most people who publicly debate issues are able to select their position. Politicians, columnists and activists choose, based on their own predispositions, whether to debate for Labour or Liberal, Left, Right or Centre or for or against a particular issue. They (should) have no problems with debating against their beliefs.\r\n\r\nAcademic debates are of course a different issue, where the debater generally does not get to choose the topic or the side which they will argue for. An academic debate is in the same league as acting and a debater would be foolish to go into a debate representing their own personal beliefs. The debate and debater should be isolated from the actor, so that the personal biases of the actor do not untowardly influence the debate, and the result of the debate is not a true reflection on the actor\'s beliefs.\r\n\r\nBased on the premise that an academic debate is an exercise in acting (or presentation, or public speaking), I cannot agree with abstinence being your recommended course of action. Abstaining from participation in a purely academic debate on ethical grounds is unacceptable in this context, especially when the debater has a full understanding of the debate.\r\n\r\nHowever, while I disagree with the conclusion, the justification is totally valid. Outside of an academic thought and acting exercise, an ethical principle requires some form of sacrifice or abstinence to be worth anything. A religion with no requirements or expectations is not respected, while strict guidelines usually are.\r\n\r\nSorry for the long reply, I enjoyed the post and it got me thinking. Nice blog!\r\n\r\nCheers,\r\n\r\nZooba','<p>Interesting thoughts. This is probably one reason why debating is so popular with staff and parents in schools &#8211; it promotes thought into differing opinions, even if they conflict with the person&#8217;s present ideas. The implicit requirement of placing the team above the individual would/should/may cause a person to see an issue from the &#8216;other side&#8217;.</p>\n\n\n <p>Probably the most obvious debating topic to demonstrate this issue is the existence of God or a god. I personally am not religious and will gladly argue for the non-existence of any active deity. However, to argue that a god does exist would require stretching my mind into areas where it does not normally go, simply because it has already decided.</p>\n\n\n <p>Attempting to justify the opposite assumption is a beneficial exercise. The scientific method is based entirely on this and many important mathematical proofs are &#8220;proof by contradiction&#8221;. However, even if the original assumption is not disproved, the wording and description of why it is correct will improve.</p>\n\n\n <p>In terms of a debate, most people who publicly debate issues are able to select their position. Politicians, columnists and activists choose, based on their own predispositions, whether to debate for Labour or Liberal, Left, Right or Centre or for or against a particular issue. They (should) have no problems with debating against their beliefs.</p>\n\n\n <p>Academic debates are of course a different issue, where the debater generally does not get to choose the topic or the side which they will argue for. An academic debate is in the same league as acting and a debater would be foolish to go into a debate representing their own personal beliefs. The debate and debater should be isolated from the actor, so that the personal biases of the actor do not untowardly influence the debate, and the result of the debate is not a true reflection on the actor&#8217;s beliefs.</p>\n\n\n <p>Based on the premise that an academic debate is an exercise in acting (or presentation, or public speaking), I cannot agree with abstinence being your recommended course of action. Abstaining from participation in a purely academic debate on ethical grounds is unacceptable in this context, especially when the debater has a full understanding of the debate.</p>\n\n\n <p>However, while I disagree with the conclusion, the justification is totally valid. Outside of an academic thought and acting exercise, an ethical principle requires some form of sacrifice or abstinence to be worth anything. A religion with no requirements or expectations is not respected, while strict guidelines usually are.</p>\n\n\n <p>Sorry for the long reply, I enjoyed the post and it got me thinking. Nice blog!</p>\n\n\n <p>Cheers,</p>\n\n\n <p>Zooba</p>','2007-01-01 11:47:09','2007-01-22 10:39:56'),(14,5,'Xavier Shay','http://blog.rhnh.net','spam3@noreality.net','','_\"Xav\'s bad habit: Forgetting what dirty dishes were his\"_\r\n\r\nI\'m not blameless ... do as I say, not as I do?','<p><em>&#8220;Xav&#8217;s bad habit: Forgetting what dirty dishes were his&#8221;</em></p>\n\n\n <p>I&#8217;m not blameless &#8230; do as I say, not as I do?</p>','2007-01-22 10:42:52','2007-07-09 10:30:31'),(15,4,'Xavier Shay','http://blog.rhnh.net','spam3@noreality.net','','I was vegan for a month. I will blog an answer to your second question.','<p>I was vegan for a month. I will blog an answer to your second question.</p>','2007-01-22 10:44:32','2007-01-22 10:50:50'),(16,2,'Xavier Shay','http://blog.rhnh.net','spam3@noreality.net','','_\"Abstaining from participation in a purely academic debate on ethical grounds is unacceptable in this context, especially when the debater has a full understanding of the debate.\"_\r\n\r\nI\'m sure the debater has a good understanding, my concern is for the audience. A presentation giving a thorough grounding on both sides of a topic is quite permissible, or even playing the devil\'s advocate in a group discussion, however the limited format of a debate (you get 3 minutes only, and often no right of reply) makes it more dangerous, since there is no opportunity to \"correct\" yourself for the benefit of the audience.','<p><em>&#8220;Abstaining from participation in a purely academic debate on ethical grounds is unacceptable in this context, especially when the debater has a full understanding of the debate.&#8221;</em></p>\n\n\n <p>I&#8217;m sure the debater has a good understanding, my concern is for the audience. A presentation giving a thorough grounding on both sides of a topic is quite permissible, or even playing the devil&#8217;s advocate in a group discussion, however the limited format of a debate (you get 3 minutes only, and often no right of reply) makes it more dangerous, since there is no opportunity to &#8220;correct&#8221; yourself for the benefit of the audience.</p>','2007-01-22 10:50:35','2007-01-22 10:50:53'),(21,20,'Daphne','http://dinnersuck.livejournal.com','daphneshum@fastmail.com.au','','That looks delicious. Maybe someday a bastardised Dinnersuck version will pop up on LJ. ','<p>That looks delicious. Maybe someday a bastardised Dinnersuck version will pop up on LJ.</p>','2007-02-11 12:55:42','2007-02-14 02:04:28'),(101,100,'Daphne','http://dinnersuck.blogspot.com','daphneshum@fastmail.com.au','','RedBubble! I\'m impressed. ','<p>RedBubble! I&#8217;m impressed.</p>','2007-04-12 05:07:07','2007-04-12 05:07:07'),(134,126,'Lucien','','','','I haven’t played Go in ages. Let me know when you want a game. I think I still have a board somewhere, but right now I can’t think where it might be. I know I’ve got the stones.','<p>I haven’t played Go in ages. Let me know when you want a game. I think I still have a board somewhere, but right now I can’t think where it might be. I know I’ve got the stones.</p>','2007-07-20 10:53:00','2007-08-16 11:44:25'),(135,125,'Matthew Delves','','','','I’m most likely missing something, but how is this inconsistent?','<p>I’m most likely missing something, but how is this inconsistent?</p>','2007-08-16 11:47:06','2007-08-16 11:47:06'),(136,125,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','reject/select are the converse of each other, they should have the same return type. I think hash is correct, I have seen arguments that array would be the correct type. Either way, they should be the same.','<p>reject/select are the converse of each other, they should have the same return type. I think hash is correct, I have seen arguments that array would be the correct type. Either way, they should be the same.</p>','2007-08-16 11:48:47','2007-08-16 11:48:47'),(138,137,'Mark','','','','Hey, that’s really nice!\r\n\r\nI may start using that instead of the official ‘byteblub blogroll/comments‘\r\n\r\nSo this doesn’t look up the XML file every time, only initially to load the data? Interested in how it works.. no doubt it’s written in Ruby so it probably wouldn’t make perfect sense - though Ruby is one of prettier languages :)\r\n\r\nGood work, ','<p>Hey, that’s really nice!</p>\n\n\n <p>I may start using that instead of the official ‘byteblub blogroll/comments‘</p>\n\n\n <p>So this doesn’t look up the <span class=\"caps\">XML</span> file every time, only initially to load the data? Interested in how it works.. no doubt it’s written in Ruby so it probably wouldn’t make perfect sense &#8211; though Ruby is one of prettier languages :)</p>\n\n\n <p>Good work,</p>','2007-06-07 10:58:00','2007-08-16 11:57:03'),(139,137,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','I used the script in my previous post to help create a configuration file for \"venus\":http://intertwingly.net/code/venus/ (a fork of planet) which is a python app. I haven’t looked at the code.','<p>I used the script in my previous post to help create a configuration file for <a href=\"http://intertwingly.net/code/venus/\">venus</a> (a fork of planet) which is a python app. I haven’t looked at the code.</p>','2007-06-08 09:51:00','2007-08-16 11:56:09'),(145,144,'Mark','','','','Wow, I must say that\'s one of the most interesting graphs I have seen ;)\r\nReally, I might even pick up that book and have a read through.','<p>Wow, I must say that&#8217;s one of the most interesting graphs I have seen ;)\nReally, I might even pick up that book and have a read through.</p>','2007-08-20 09:29:53','2007-08-20 09:29:53'),(152,151,'Mark','','','','Why are the risks greater for non-developed countries? Apart from that they will not have air conditioners?','<p>Why are the risks greater for non-developed countries? Apart from that they will not have air conditioners?</p>','2007-08-31 08:19:02','2007-08-31 08:19:02'),(153,151,'Zooba','','','','In the end, for me it comes down to acceptable cost. The cost of ‘preventing’ the oncoming ‘disaster’ has been estimated in hundreds of billions, if not more. The actual cost of dealing with any changes, for example, flood protection walls or levees, is highly likely to be much lower and are extremely unlikely to affect our way of life. Any effect on the way we live will take years during which we will adapt.\r\n\r\nWhile I’m on the topic, claims that cities will ‘drown’ and people will die are ludicrous. The worst predictions don’t say any more than one metre of water rise in 100 years (and most say considerably less than this). That’s one centimetre per year. Even the Bracks government could construct a levee around Melbourne quicker than that! ','<p>In the end, for me it comes down to acceptable cost. The cost of ‘preventing’ the oncoming ‘disaster’ has been estimated in hundreds of billions, if not more. The actual cost of dealing with any changes, for example, flood protection walls or levees, is highly likely to be much lower and are extremely unlikely to affect our way of life. Any effect on the way we live will take years during which we will adapt.</p>\n\n\n <p>While I’m on the topic, claims that cities will ‘drown’ and people will die are ludicrous. The worst predictions don’t say any more than one metre of water rise in 100 years (and most say considerably less than this). That’s one centimetre per year. Even the Bracks government could construct a levee around Melbourne quicker than that!</p>','2007-08-31 08:19:24','2007-08-31 08:19:24'),(157,151,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','Basically, it’s not the developed countries we have to worry about, as they have the resources to deal with climate change. Third world countries are struggling for food, let alone having resources to provide infrastructure to adapt to global warming. For an example of drowning cities, approx 46 million people live on the Nile Delta, virtually all of which is below 2m above sea level, which faces dire consequences of even minor rises in the sea level. Consider that the destruction of agricultural land can be just as devastating as destruction of residences. Many pacific islands also face severe threat. Plenty of scholarly references around for this - just plucked \"http://www.grida.no/climate/vital/34.htm\":http://www.grida.no/climate/vital/34.htm from google. As for cost, this may sound facetious, but when the \"US is spending over $500 billion on their military\":http://www.theaustralian.news.com.au/story/0,20867,21179772-2703,00.html, I don’t think cost is going to be that much of a problem. It comes down to the fact that the developed countries can no longer justify or afford to live the affluent lifestyle they have been. The earth is a finite resource. Global warming aside, fossil fuels and oil (two prime contributors) won’t last forever. ','<p>Basically, it’s not the developed countries we have to worry about, as they have the resources to deal with climate change. Third world countries are struggling for food, let alone having resources to provide infrastructure to adapt to global warming. For an example of drowning cities, approx 46 million people live on the Nile Delta, virtually all of which is below 2m above sea level, which faces dire consequences of even minor rises in the sea level. Consider that the destruction of agricultural land can be just as devastating as destruction of residences. Many pacific islands also face severe threat. Plenty of scholarly references around for this &#8211; just plucked <a href=\"http://www.grida.no/climate/vital/34.htm\">http://www.grida.no/climate/vital/34.htm</a> from google. As for cost, this may sound facetious, but when the <a href=\"http://www.theaustralian.news.com.au/story/0,20867,21179772-2703,00.html\">US is spending over $500 billion on their military</a>, I don’t think cost is going to be that much of a problem. It comes down to the fact that the developed countries can no longer justify or afford to live the affluent lifestyle they have been. The earth is a finite resource. Global warming aside, fossil fuels and oil (two prime contributors) won’t last forever.</p>','2007-08-31 08:25:31','2007-08-31 08:25:31'),(158,128,'palb','','','','you can re-map the home row keys to match your keyboard, in your ~/.vimrc add\r\n:map j n\r\n:map k e\r\n:map l i\r\n\r\nbut then you would have to map n e and i to something else :P\r\n\r\ni have something like this to move between \"buffers\" or open files (ctrl+L moves to the file right, ctrl+k moves to the file left), also :ls will print the open buffers and :b<number> will move you to that buffer.\r\n:imap <C-l> <ESC>:bn<CR>\r\n:imap <C-k> <ESC>:bp<CR>\r\n:map <C-l> :bn<CR>\r\n:map <C-k> :bp<CR>\r\n\r\nmapping info here:\r\nhttp://www.vim.org/htmldoc/map.html\r\n','<p>you can re-map the home row keys to match your keyboard, in your ~/.vimrc add\n:map j n\n:map k e\n:map l i</p>\n\n\n <p>but then you would have to map n e and i to something else :P</p>\n\n\n <p>i have something like this to move between &#8220;buffers&#8221; or open files (ctrl+L moves to the file right, ctrl+k moves to the file left), also :ls will print the open buffers and :b<number> will move you to that buffer.\n:imap <C-l> <ESC>:bn<CR>\n:imap <C-k> <ESC>:bp<CR>\n:map <C-l> :bn<CR>\n:map <C-k> :bp<CR></p>\n\n\n <p>mapping info here:\nhttp://www.vim.org/htmldoc/map.html</p>','2007-09-08 20:55:50','2007-09-08 20:55:50'),(161,160,'Justin French','','','','Hi-lar-i-ous ','<p>Hi-lar-i-ous</p>','2007-09-17 07:01:19','2007-09-17 07:01:19'),(164,160,'Mark Mansour','','','','did you 0wnz Mario?','<p>did you 0wnz Mario?</p>','2007-09-26 08:40:02','2007-09-26 08:40:02'),(166,160,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','no, poor guy never recovered from the crash :(','<p>no, poor guy never recovered from the crash :(</p>','2007-09-27 08:54:36','2007-09-27 08:54:36'),(195,128,'Isaac','','','','Wow, I\'ve only been using jEdit for the past 5 minutes and already I\'m loving it! I\'m so sick of switching between PSPad, Notepad++, Bluefish, and Textmate (most of those are Windows programs I run in Wine).','<p>Wow, I&#8217;ve only been using jEdit for the past 5 minutes and already I&#8217;m loving it! I&#8217;m so sick of switching between PSPad, Notepad++, Bluefish, and Textmate (most of those are Windows programs I run in Wine).</p>','2007-10-28 21:39:03','2007-10-28 21:39:03'),(198,197,'Blake Mizerany','','','','Wonderful. You may shorten the layout with \r\n\r\nlayout \'main.erb\'\r\n\r\nThat will determine and read the file for you. This may change by the next version allowing you to use a symbol without the ext.','<p>Wonderful. You may shorten the layout with</p>\n\n\n <p>layout &#8216;main.erb&#8217;</p>\n\n\n <p>That will determine and read the file for you. This may change by the next version allowing you to use a symbol without the ext.</p>','2007-10-30 18:50:55','2007-10-30 18:50:55'),(200,199,'Dylan','','','','You can has IMAP too!','<p>You can has <span class=\"caps\">IMAP</span> too!</p>','2007-10-31 07:28:50','2007-10-31 07:28:50'),(201,199,'Me','','','','Nice timing. Gmail has just started offering IMAP too.','<p>Nice timing. Gmail has just started offering <span class=\"caps\">IMAP</span> too.</p>','2007-10-31 09:11:11','2007-10-31 09:11:11'),(202,197,'Justin French','','','','I too have a love for Frank (we\'re on a first name basis).','<p>I too have a love for Frank (we&#8217;re on a first name basis).</p>','2007-10-31 11:26:58','2007-10-31 11:26:58'),(207,206,'Gatesy','','','','Well, my 6230 has something even more annoying: a default number for sending SMSes. I have no idea how this was set, or if it’s possible to change it, but it has resulted in me sending more than a few messages to a random number (not in my phone book, so I have no idea who they are). I haven’t mustered the courage to ring it and find out who it is…','<p>Well, my 6230 has something even more annoying: a default number for sending SMSes. I have no idea how this was set, or if it’s possible to change it, but it has resulted in me sending more than a few messages to a random number (not in my phone book, so I have no idea who they are). I haven’t mustered the courage to ring it and find out who it is…</p>','2006-12-23 00:00:00','2007-11-03 01:03:39'),(211,210,'Lucien','','','','At my new job, the single main reason I can’t use my Mac for *everything* I do is that code lives in an VSS repository, and the version we use doesn’t have a Mac client. I’m told the newer version supports WebDav, and thus accessible from the Mac, but that would require a new license (and money).\r\n\r\nBut if I too could get everyone to convert to SVN… ','<p>At my new job, the single main reason I can’t use my Mac for <strong>everything</strong> I do is that code lives in an <span class=\"caps\">VSS</span> repository, and the version we use doesn’t have a Mac client. I’m told the newer version supports WebDav, and thus accessible from the Mac, but that would require a new license (and money).</p>\n\n\n <p>But if I too could get everyone to convert to <span class=\"caps\">SVN</span>…</p>','2006-09-10 00:00:00','2007-11-03 01:18:14'),(212,210,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','I switched ITS from VSS to subversion, so you have a precedent…\r\n','<p>I switched <span class=\"caps\">ITS</span> from <span class=\"caps\">VSS</span> to subversion, so you have a precedent…</p>','2007-11-03 01:29:49','2007-11-03 01:29:49'),(218,197,'MattyG','','','','Ohhh website for a dance troupe. What dance troupe could that be? Is this a secret? Should i know?','<p>Ohhh website for a dance troupe. What dance troupe could that be? Is this a secret? Should i know?</p>','2007-11-11 01:05:38','2007-11-11 01:05:38'),(219,197,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','\'Artful Dodgers\', one of Jodie\'s things','<p>&#8216;Artful Dodgers&#8217;, one of Jodie&#8217;s things</p>','2007-11-12 09:58:51','2007-11-12 09:58:51'),(220,197,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','Also, if you use the layout \'main.erb\' trick you don\'t get a default layout and have to pass it in by hand for each action, which I don\'t think is cool','<p>Also, if you use the layout &#8216;main.erb&#8217; trick you don&#8217;t get a default layout and have to pass it in by hand for each action, which I don&#8217;t think is cool</p>','2007-11-12 10:18:40','2007-11-12 10:18:40'),(221,197,'Blake Mizerany','','','','Correct. Sinatra looks for a erb/haml file named layout by default. There is no need to name something else unless you have multiple layouts. \r\n\r\ni.e. views/layout.erb will work without explicitly declaring it as the layout.','<p>Correct. Sinatra looks for a erb/haml file named layout by default. There is no need to name something else unless you have multiple layouts.</p>\n\n\n <p>i.e. views/layout.erb will work without explicitly declaring it as the layout.</p>','2007-11-12 18:24:50','2007-11-12 18:24:50'),(248,203,'Melissa','','','','you don\'t know me but - hahaha. This is also why I do not watch tv\r\n\r\nDon\'t use deodarant it gives you cancer, eat more animal it will make you healthier and sit undigested in your intestines for years. Woo I love to stink and have rotting meat sitting inside me\r\n\r\nNice','<p>you don&#8217;t know me but &#8211; hahaha. This is also why I do not watch tv</p>\n\n\n <p>Don&#8217;t use deodarant it gives you cancer, eat more animal it will make you healthier and sit undigested in your intestines for years. Woo I love to stink and have rotting meat sitting inside me</p>\n\n\n <p>Nice</p>','2007-11-22 00:19:01','2007-11-22 00:19:01'),(264,263,'Tim Lucas','http://toolmantim.com','','http://www.myopenid.com/server','Your talk was really good. I haven\'t used Bazaar yet, but I must say Git really is not for the weak at heart. To use it at all you have to really get your head around it, and then there\'s figuring out the workflow you want and configuring git accordingly which takes a bit more blood and tears. I\'m definitely going to check out bazaar for a simpler solution. Using a tool written in Python rather than C with 144 Perl scripts certainly has appeal.','<p>Your talk was really good. I haven&#8217;t used Bazaar yet, but I must say Git really is not for the weak at heart. To use it at all you have to really get your head around it, and then there&#8217;s figuring out the workflow you want and configuring git accordingly which takes a bit more blood and tears. I&#8217;m definitely going to check out bazaar for a simpler solution. Using a tool written in Python rather than C with 144 Perl scripts certainly has appeal.</p>','2007-11-27 08:00:20','2007-11-27 08:00:20'),(331,330,'Justin French','','','','how about `rake log:clear`?','<p>how about `rake log:clear`?</p>','2007-12-17 21:58:18','2007-12-17 21:58:18'),(332,330,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','Oh yes I\'ve learned now, but that\'s not going to clear out the logs in all of the 10 active projects I have...','<p>Oh yes I&#8217;ve learned now, but that&#8217;s not going to clear out the logs in all of the 10 active projects I have&#8230;</p>','2007-12-17 22:01:10','2007-12-17 22:01:10'),(334,333,'dmh2000','','','','You might also add a section just before \'the solution\' that uses a named recursive function that calls itself, but using a parameter. this might make it more clear later on when you explain x.call(x) for anonymous functions\r\n\r\nI think this is right (I\'m not a Ruby guy)\r\n\r\ndef make_hash(x)\r\n Hash.new {|h,k| h[k] = x.call(x)}\r\nend\r\n\r\nhash = make_hash(method(:make_hash))\r\nhash[1][2][3][4][5]\r\n','<p>You might also add a section just before &#8216;the solution&#8217; that uses a named recursive function that calls itself, but using a parameter. this might make it more clear later on when you explain x.call(x) for anonymous functions</p>\n\n\n <p>I think this is right (I&#8217;m not a Ruby guy)</p>\n\n\n <p>def make_hash(x)\n Hash.new {|h,k| h[k] = x.call(x)}\nend</p>\n\n\n <p>hash = make_hash(method(:make_hash))\nhash<a href=\"2\">1</a><a href=\"4\">3</a>[5]</p>','2007-12-20 17:12:01','2007-12-20 17:12:01'),(335,333,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','Yes good idea, I added it where I introduce x.call(x)','<p>Yes good idea, I added it where I introduce x.call(x)</p>','2007-12-20 21:15:02','2007-12-20 21:15:02'),(337,336,'Marcus Crafter','crafterm.myopenid.com','','http://www.myopenid.com/server','Awesome mate, had this exact issue yesterday upgrading another app to Rails 2.0.2 and seeing many test failures - I\'d expect a 2.0.3 to be out pretty soon then :)','<p>Awesome mate, had this exact issue yesterday upgrading another app to Rails 2.0.2 and seeing many test failures &#8211; I&#8217;d expect a 2.0.3 to be out pretty soon then :)</p>','2007-12-21 03:23:38','2007-12-21 03:23:38'),(340,333,'pluskid','','','','Since Ruby allow named recursive function, we can simply write a y combinator in named recursive syntax:\r\n\r\ndef y_combinator(f)\r\n # lambda used to delay evaluation\r\n lambda { |*args| f.call(y_combinator(f)).call(*args) }\r\nend\r\n\r\nwhich can then be used to construct anonymous recursive functions. :)','<p>Since Ruby allow named recursive function, we can simply write a y combinator in named recursive syntax:</p>\n\n\n <p>def y_combinator(f)\n # lambda used to delay evaluation\n lambda { |<strong>args| f.call(y_combinator(f)).call(</strong>args) }\nend</p>\n\n\n <p>which can then be used to construct anonymous recursive functions. :)</p>','2007-12-21 08:56:13','2007-12-21 08:56:13'),(341,333,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','If you\'re going to use named recursion, you may as well just do:\n\n@result = (func = lambda {|n| n == 1 ? 1 : n * func[n - 1]})[5]@\n\n... which is my prefferred way of doing once off recursion in everyday coding.\n\nIn other news, I really need to get code highlighting and preview in comments...\n','<p>If you&#8217;re going to use named recursion, you may as well just do:</p>\n\n\n <p><code>result = (func = lambda {|n| n == 1 ? 1 : n * func[n - 1]})[5]</code></p>\n\n\n <p>... which is my prefferred way of doing once off recursion in everyday coding.</p>\n\n\n <p>In other news, I really need to get code highlighting and preview in comments&#8230;</p>','2007-12-22 05:15:15','2007-12-22 05:18:30'),(348,347,'Justin French','indent.myopenid.com','','http://www.myopenid.com/server','--- Ruby\r\nrequire \'lobbyist\'\r\n5.times { Lobby','<p>--- Ruby<br />\nrequire \'lobbyist\'<br />\n5.times { Lobby</p>','2007-12-27 14:23:22','2007-12-27 14:23:22'),(352,347,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','*ahem*\r\n\r\n--- Ruby\r\nrequire \'bugfixer\'\r\n\r\nbugs = [\"Long OpenID comments are broke\", \"comment helper doesn\'t provide lesstile link\"]\r\nbugs.each do |bug|\r\n bug.fix!\r\nend\r\n---','<p>*ahem*<br />\n<br />\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">bugfixer</span><span class=\"dl\">\'</span></span><tt>\n</tt><tt>\n</tt>bugs = [<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Long OpenID comments are broke</span><span class=\"dl\">&quot;</span></span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">comment helper doesn\'t provide lesstile link</span><span class=\"dl\">&quot;</span></span>]<tt>\n</tt>bugs.each <span class=\"r\">do</span> |bug|<tt>\n</tt> bug.fix!<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n</p>','2007-12-28 01:25:37','2007-12-28 01:25:37'),(361,354,'Lis','','','','Woohoo!!','<p>Woohoo!!</p>','2007-12-29 13:38:09','2007-12-29 13:38:09'),(363,354,'Fairs','','','','I am so very impressed... and realised that I too own marble run from Aldi!! Well done boys..','<p>I am so very impressed... and realised that I too own marble run from Aldi!! Well done boys..</p>','2007-12-30 10:35:59','2007-12-30 10:35:59'),(368,366,'Ryan Allen','','','','Call it Roboblog!','<p>Call it Roboblog!</p>','2008-01-04 01:23:45','2008-01-04 01:23:45'),(369,355,'Tim Lucas','http://toolmantim.com','','http://www.myopenid.com/server','Ok I\'m seriously impressed. I too, now, want to make a coffee liqueur gateau.','<p>Ok I\'m seriously impressed. I too, now, want to make a coffee liqueur gateau.</p>','2008-01-04 06:36:04','2008-01-04 06:36:04'),(371,366,'josh susser','','','','Interesting. I\'m doing the same thing, for much the same reasons. I don\'t even have a UsersController in admin, just a rake task to create users (which will probably only ever happen once). It\'s just about functional now and it\'s only 500 lines of code (not counting plugins). Should be ready for prime time in a week or so... How\'s yours going?','<p>Interesting. I\'m doing the same thing, for much the same reasons. I don\'t even have a UsersController in admin, just a rake task to create users (which will probably only ever happen once). It\'s just about functional now and it\'s only 500 lines of code (not counting plugins). Should be ready for prime time in a week or so... How\'s yours going?</p>','2008-01-06 21:07:55','2008-01-06 21:07:55'),(372,367,'josh susser','','','','I looked at the patch, which looks nice. But I find the :tag_uri option in the rdoc a little confusing, since it doesn\'t map to anything in the code. Is that meant to be the :schema_date instead?','<p>I looked at the patch, which looks nice. But I find the :tag_uri option in the rdoc a little confusing, since it doesn\'t map to anything in the code. Is that meant to be the :schema_date instead?</p>','2008-01-06 21:22:35','2008-01-06 21:22:35'),(373,367,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','Thanks Josh, you\'re right - http://dev.rubyonrails.org/ticket/10729','<p>Thanks Josh, you\'re right - http://dev.rubyonrails.org/ticket/10729</p>','2008-01-07 03:02:01','2008-01-07 03:02:01'),(382,366,'Clifford Heath','','','','Sounds great Xavier! I started writing a blog engine just b/c I wanted to have comments per-paragraph (like on Jack Slocum\'s blog *), and TinyMCE instead of markup for input. I\'m not going to finish it, but if you want, you\'re welcome to what I\'ve done.\r\n\r\n--- HTML\r\n* <a href=\"http://www.jackslocum.com/blog/2006/12/29/preview-drag-and-drop-enhancements-and-the-new-treepanel/\">Jack Slocum\'s blog</a>\r\n---','<p>Sounds great Xavier! I started writing a blog engine just b/c I wanted to have comments per-paragraph (like on Jack Slocum\'s blog *), and TinyMCE instead of markup for input. I\'m not going to finish it, but if you want, you\'re welcome to what I\'ve done.<br />\n<br />\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">* <span class=\"ta\">&lt;a</span> <span class=\"an\">href</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://www.jackslocum.com/blog/2006/12/29/preview-drag-and-drop-enhancements-and-the-new-treepanel/</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">&gt;</span>Jack Slocum\'s blog<span class=\"ta\">&lt;/a&gt;</span><tt>\n</tt></pre></td>\n</tr></table>\n</p>','2008-01-14 01:41:28','2008-01-14 01:41:28'),(388,366,'Mark','','','','Sounds very interesting Xavier, I\'d love to see it and hack away at it once it\'s finished.\r\n\r\nI think you should call it Moke..','<p>Sounds very interesting Xavier, I\'d love to see it and hack away at it once it\'s finished.<br />\n<br />\nI think you should call it Moke..</p>','2008-01-17 22:02:22','2008-01-17 22:02:22'),(427,197,'Doodee','','','','Thanks for sharing','<p>Thanks for sharing</p>','2008-02-01 23:56:30','2008-02-02 07:11:22'),(456,336,'matt','','','','thank you so much','<p>thank you so much</p>','2008-02-08 04:01:52','2008-02-08 04:01:52'),(502,465,'Ricky','','','','Dude - diggin the class last Tues and lookin forward to next week.\r\n\r\nMay even try out some Coconut Oats with fruit between now and then!\r\n\r\nKeep it up man.\r\n\r\nRicky','<p>Dude - diggin the class last Tues and lookin forward to next week.<br />\n<br />\nMay even try out some Coconut Oats with fruit between now and then!<br />\n<br />\nKeep it up man.<br />\n<br />\nRicky</p>','2008-02-21 23:50:32','2008-02-21 23:50:32'),(505,336,'Gabe','','','','That was a really nasty regression for a double point release. Spent a couple hours trying to figure out what went wrong because I had just done a 10,000 line merge.','<p>That was a really nasty regression for a double point release. Spent a couple hours trying to figure out what went wrong because I had just done a 10,000 line merge.</p>','2008-02-22 06:46:13','2008-02-22 06:46:13'),(627,118,'Mike','','','','For doctypes, Haml gives you more options than just \"!!!\".\r\nSee http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html','<p>For doctypes, Haml gives you more options than just &quot;!!!&quot;.<br />\nSee http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html</p>','2008-03-12 17:17:09','2008-03-12 17:17:09'),(632,507,'Chris Broadfoot','http://chrisbroadfoot.myopenid.com/','','http://www.myopenid.com/server','<script>alert(\'hi xavier, love from broady from #roro\')</script>','<p>&lt;script&gt;alert(\'hi xavier, love from broady from #roro\')&lt;/script&gt;</p>','2008-03-13 12:05:49','2008-03-13 12:05:49'),(657,374,'hts','','','','lets see this work','<p>lets see this work</p>','2008-03-16 20:39:27','2008-03-16 20:39:27'),(701,660,'Mark Mansour','','','','\"I hate you, Paypal\" - The funniest thing I\'ve read today\r\n','<p>&quot;I hate you, Paypal&quot; - The funniest thing I\'ve read today</p>','2008-03-21 13:19:45','2008-03-21 13:19:45'),(748,374,'John','','','','You might want to add some sort of highlight when the user starts typing (yellow fade technique maybe). I skipped right over the empty comment and expected the live comment to appear beneath the comment box.','<p>You might want to add some sort of highlight when the user starts typing (yellow fade technique maybe). I skipped right over the empty comment and expected the live comment to appear beneath the comment box.</p>','2008-03-27 16:47:03','2008-03-27 16:47:03'),(750,429,'volte','','','','why not use timemachine? or was this prior to its release?','<p>why not use timemachine? or was this prior to its release?</p>','2008-03-27 22:15:46','2008-03-27 22:15:46'),(755,429,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','Can\'t use time machine on FAT32. It appears there may also be an issue doing incremental backup to FAT32, so I might just rethink that requirement and reformat it again.','<p>Can\'t use time machine on FAT32. It appears there may also be an issue doing incremental backup to FAT32, so I might just rethink that requirement and reformat it again.</p>','2008-03-28 02:45:14','2008-03-28 02:45:14'),(763,213,'volte','','','','Hmm. I\'m having trouble with this. I finally got all my ports configured to work together (more complicated because I had apache and other dependancies) but now I get this in my logs.\r\n---\r\nMar 28 11:24:42 November slapd[9636]: @(#) $OpenLDAP: slapd 2.4.8 (Mar 28 2008 10:32:38) $ root@November:/usr/ports/net/openldap24-server/work/openldap-2.4.8/servers/slapd\r\nMar 28 11:24:42 November slapd[9636]: daemon: bind(6) failed errno=49 (Can\'t assign requested address)\r\nMar 28 11:24:42 November slapd[9636]: slapd stopped.\r\nMar 28 11:24:42 November slapd[9636]: connections_destroy: nothing to destroy.\r\n---\r\nAny help?','<p>Hmm. I\'m having trouble with this. I finally got all my ports configured to work together (more complicated because I had apache and other dependancies) but now I get this in my logs.<br />\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">Mar 28 11:24:42 November slapd[9636]: @(#) $OpenLDAP: slapd 2.4.8 (Mar 28 2008 10:32:38) $ root@November:/usr/ports/net/openldap24-server/work/openldap-2.4.8/servers/slapd<tt>\n</tt>Mar 28 11:24:42 November slapd[9636]: daemon: bind(6) failed errno=49 (Can\'t assign requested address)<tt>\n</tt>Mar 28 11:24:42 November slapd[9636]: slapd stopped.<tt>\n</tt>Mar 28 11:24:42 November slapd[9636]: connections_destroy: nothing to destroy.<tt>\n</tt></pre></td>\n</tr></table>\nAny help?</p>','2008-03-28 18:32:05','2008-03-28 18:32:05'),(769,213,'Xavier Shay','http://rhnh.net','','http://www.myopenid.com/server','I\'m just guessing ... maybe double check the hostname/IP and port your are trying to start on aren\'t already in use or somehow restricted. Check it\'s not already running.','<p>I\'m just guessing ... maybe double check the hostname/IP and port your are trying to start on aren\'t already in use or somehow restricted. Check it\'s not already running.</p>','2008-03-29 03:51:58','2008-03-29 03:51:58'),(770,374,'catss','','','','manager for pussycat dolls <a href=\" http://www.volny.cz/tgp20/adult-fanfic.html \">adult fanfic</a>\r\n<a href=\" http://www.volny.cz/tgp29/manager-for-pussycat-dolls.html \">manager for pussycat dolls</a>\r\n<a href=\" http://www.volny.cz/tgp12/gay-characteristics.html \">gay characteristics</a>\r\n<a href=\" http://www.volny.cz/tgp38/cartoons-with-big-tits.html \">cartoons with big tits</a>\r\n<a href=\" http://www.volny.cz/tgp7/live-sex-com.html \">live sex com</a>\r\n<a href=\" http://www.volny.cz/tgp8/gay-jerking-off-pictures.html \">gay jerking off pictures</a>\r\n<a href=\" http://www.volny.cz/tgp27/teen-escorts.html \">teen escorts</a>\r\n<a href=\" http://www.volny.cz/tgp19/http.-www-pornking-com.html \">http. www pornking com</a>\r\n<a href=\" http://www.volny.cz/tgp43/tenchi-muyo-hentai.html \">tenchi muyo hentai</a>\r\n<a href=\" http://www.volny.cz/tgp48/tickling-naked-pussy-videos.html \">tickling naked pussy videos</a>\r\n<a href=\" http://www.volny.cz/tgp1/teen-age-girls-and-sex.html \">teen age girls and sex</a>\r\n<a href=\" http://www.volny.cz/tgp6/shemale-escorts-in-boston-ma.html \">shemale escorts in boston ma</a>\r\n<a href=\" http://www.volny.cz/tgp27/pussy-gallery.html \">pussy gallery</a>\r\n<a href=\" http://www.volny.cz/tgp17/shemale-free-movies.html \">shemale free movies</a>\r\n<a href=\" http://www.volny.cz/tgp26/animal-fuck-teen.html \">animal fuck teen</a>\r\n<a href=\" http://www.volny.cz/tgp49/cartoon-network-teen-titans.html \">cartoon network teen titans</a>\r\n<a href=\" http://www.volny.cz/tgp35/xxx-now.html \">xxx now</a>\r\n<a href=\" http://www.volny.cz/tgp28/animated-teenage-mutant-ninja-turtles.html \">animated teenage mutant ninja turtles</a>\r\n<a href=\" http://www.volny.cz/tgp44/alt-adultfriend-finder.html \">alt adultfriend finder</a>\r\n<a href=\" http://www.volny.cz/tgp25/yngwie-malmsteen-canon-tabs.html \">yngwie malmsteen canon tabs</a>\r\n<a href=\" http://www.volny.cz/tgp8/teen-teaser.html \">teen teaser</a>\r\n<a href=\" http://www.volny.cz/tgp42/asexual-reproduction-cells.html \">asexual reproduction cells</a>\r\n<a href=\" http://www.volny.cz/tgp12/free-very-young-preteens-nude.html \">free very young preteens nude</a>\r\n<a href=\" http://www.volny.cz/tgp37/wetest-pussy.html \">wetest pussy</a>\r\n<a href=\" http://www.volny.cz/tgp3/rules-of-cyber-sex.html \">rules of cyber sex</a>\r\n<a href=\" http://www.volny.cz/tgp9/submissive-fucking-sluts.html \">submissive fucking sluts</a>\r\n<a href=\" http://www.volny.cz/tgp5/posed-free-porn.html \">posed free porn</a>\r\n<a href=\" http://www.volny.cz/tgp37/7600gt-xxx-reviews.html \">7600gt xxx reviews</a>\r\n<a href=\" http://www.volny.cz/tgp2/gay-quiz.html \">gay quiz</a>\r\n<a href=\" http://www.volny.cz/tgp46/free-porno-clips.html \">free porno clips</a>','<p>manager for pussycat dolls &lt;a href=&quot; http://www.volny.cz/tgp20/adult-fanfic.html &quot;&gt;adult fanfic&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp29/manager-for-pussycat-dolls.html &quot;&gt;manager for pussycat dolls&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp12/gay-characteristics.html &quot;&gt;gay characteristics&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp38/cartoons-with-big-tits.html &quot;&gt;cartoons with big tits&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp7/live-sex-com.html &quot;&gt;live sex com&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp8/gay-jerking-off-pictures.html &quot;&gt;gay jerking off pictures&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp27/teen-escorts.html &quot;&gt;teen escorts&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp19/http.-www-pornking-com.html &quot;&gt;http. www pornking com&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp43/tenchi-muyo-hentai.html &quot;&gt;tenchi muyo hentai&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp48/tickling-naked-pussy-videos.html &quot;&gt;tickling naked pussy videos&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp1/teen-age-girls-and-sex.html &quot;&gt;teen age girls and sex&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp6/shemale-escorts-in-boston-ma.html &quot;&gt;shemale escorts in boston ma&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp27/pussy-gallery.html &quot;&gt;pussy gallery&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp17/shemale-free-movies.html &quot;&gt;shemale free movies&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp26/animal-fuck-teen.html &quot;&gt;animal fuck teen&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp49/cartoon-network-teen-titans.html &quot;&gt;cartoon network teen titans&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp35/xxx-now.html &quot;&gt;xxx now&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp28/animated-teenage-mutant-ninja-turtles.html &quot;&gt;animated teenage mutant ninja turtles&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp44/alt-adultfriend-finder.html &quot;&gt;alt adultfriend finder&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp25/yngwie-malmsteen-canon-tabs.html &quot;&gt;yngwie malmsteen canon tabs&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp8/teen-teaser.html &quot;&gt;teen teaser&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp42/asexual-reproduction-cells.html &quot;&gt;asexual reproduction cells&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp12/free-very-young-preteens-nude.html &quot;&gt;free very young preteens nude&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp37/wetest-pussy.html &quot;&gt;wetest pussy&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp3/rules-of-cyber-sex.html &quot;&gt;rules of cyber sex&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp9/submissive-fucking-sluts.html &quot;&gt;submissive fucking sluts&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp5/posed-free-porn.html &quot;&gt;posed free porn&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp37/7600gt-xxx-reviews.html &quot;&gt;7600gt xxx reviews&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp2/gay-quiz.html &quot;&gt;gay quiz&lt;/a&gt;<br />\n&lt;a href=&quot; http://www.volny.cz/tgp46/free-porno-clips.html &quot;&gt;free porno clips&lt;/a&gt;</p>','2008-04-12 01:46:53','2008-04-12 01:46:53');
/*!40000 ALTER TABLE `comments` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `pages`
--
DROP TABLE IF EXISTS `pages`;
CREATE TABLE `pages` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`slug` varchar(255) NOT NULL,
`body` text NOT NULL,
`body_html` text NOT NULL,
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `index_pages_on_created_at` (`created_at`),
KEY `index_pages_on_title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `pages`
--
LOCK TABLES `pages` WRITE;
/*!40000 ALTER TABLE `pages` DISABLE KEYS */;
INSERT INTO `pages` VALUES (1,'About','about','Xavier has been *coding* for over 10 years, and has worked with everything from x86 Assembler to *Ruby* (his current language of choice), from business applications to video games. In particular he has a *passion* for artificing *beautiful, robust, and testable code*. He is currently working with *Ruby on Rails* at \"RedBubble\":http://www.redbubble.com, an online art gallery, which he enjoys very much.\r\n\r\nWhen he\'s not coding, Xavier can be found *dancing* around the local swing scene, *running* through the streets of Melbourne, talking about himself in third person, or just chilling to some smooth-as-honey *jazz*. \r\n\r\nYou may find references to a Xavier Shay at places other than here: \"RedBubble\":http://www.redbubble.com/people/xshay, \"FaceBook\":http://www.facebook.com/s.php?k=10080&id=696061081, \"SwingPatrol\":http://mel.swingpatrol.com.au/profile/100034/xavier-shay.php. You will be surprised to learn that these are all the same person!','<p>Xavier has been <strong>coding</strong> for over 10 years, and has worked with everything from x86 Assembler to <strong>Ruby</strong> (his current language of choice), from business applications to video games. In particular he has a <strong>passion</strong> for artificing <strong>beautiful, robust, and testable code</strong>. He is currently working with <strong>Ruby on Rails</strong> at <a href=\"http://www.redbubble.com\">RedBubble</a>, an online art gallery, which he enjoys very much.</p>\n\n\n <p>When he&#8217;s not coding, Xavier can be found <strong>dancing</strong> around the local swing scene, <strong>running</strong> through the streets of Melbourne, talking about himself in third person, or just chilling to some smooth-as-honey <strong>jazz</strong>.</p>\n\n\n <p>You may find references to a Xavier Shay at places other than here: <a href=\"http://www.redbubble.com/people/xshay\">RedBubble</a>, <a href=\"http://www.facebook.com/s.php?k=10080&#38;id=696061081\">FaceBook</a>, <a href=\"http://mel.swingpatrol.com.au/profile/100034/xavier-shay.php\">SwingPatrol</a>. You will be surprised to learn that these are all the same person!</p>','2008-02-23 11:35:13','2008-02-23 11:36:08'),(2,'Projects','projects','h3(projects). Major contributions\r\n\r\n|_. Project |_. Description |\r\n|\"Enki\":http://enkiblog.com | A ruby on rails blogging app for the fashionable developer. |\r\n|\"Clerk Simon\":http://rhnh.net/2007/11/3/introducing-clerk-simon | Update an LDAP address book by forwarding emails |\r\n|\"SQL Counter\":http://rhnh.net/2007/12/13/logging-sql-statistics-in-rails | Log SQL statistics in rails |\r\n| \"Rant\":http://rant.rubyforge.org/ | Flexible, Ruby based make, an alternative to rake |\r\n| \"YAML Helper\":http://rhnh.svnrepository.com/svn/public/ruby/yaml_helper/ | Transient fields and post-deserialization hooks for yaml4r |\r\n\r\nh3(projects). Minor contributions\r\n\r\n|_. Date |_. Project |_. Description |_. Revision/Version |\r\n| 26 Mar 2008 | Rails | Nazi grammar fix | \"[9093]\":http://dev.rubyonrails.org/changeset/9093 |\r\n| 7 Feb 2008 | Rails | FormBuilder doc typo fix | \"[8812]\":http://dev.rubyonrails.org/changeset/8812 |\r\n| 2 Jan 2008 | Rails | atom_feed_helper fixes | \"[8529]\":http://dev.rubyonrails.org/changeset/8529 |\r\n| 24 Dec 2007 | test/spec | Nested setup/teardown | \"0.5\":http://chneukirchen.org/repos/testspec/README |\r\n| 13 Dec 2007 | Rails | validates_acceptance_of doc fix | \"[8379]\":http://dev.rubyonrails.org/changeset/8379 |\r\n| 30 Sep 2007 | Rails | has_many :through fix | \"[7692]\":http://dev.rubyonrails.org/changeset/7692 |\r\n| 20 Sep 2007 | Cerberus | Rant support, RSS bug fix | \"[89]\":http://cerberus.rubyforge.org/svn/ |','<h3 class=\"projects\">Major contributions</h3>\n\n\n <table>\n <tr>\n <th>Project </th>\n <th>Description </th>\n </tr>\n <tr>\n <td><a href=\"http://enkiblog.com\">Enki</a> </td>\n <td> A ruby on rails blogging app for the fashionable developer. </td>\n </tr>\n <tr>\n <td><a href=\"http://rhnh.net/2007/11/3/introducing-clerk-simon\">Clerk Simon</a> </td>\n <td> Update an <span class=\"caps\">LDAP</span> address book by forwarding emails </td>\n </tr>\n <tr>\n <td><a href=\"http://rhnh.net/2007/12/13/logging-sql-statistics-in-rails\"><span class=\"caps\">SQL</span> Counter</a> </td>\n <td> Log <span class=\"caps\">SQL</span> statistics in rails </td>\n </tr>\n <tr>\n <td> <a href=\"http://rant.rubyforge.org/\">Rant</a> </td>\n <td> Flexible, Ruby based make, an alternative to rake </td>\n </tr>\n <tr>\n <td> <a href=\"http://rhnh.svnrepository.com/svn/public/ruby/yaml_helper/\"><span class=\"caps\">YAML</span> Helper</a> </td>\n <td> Transient fields and post-deserialization hooks for yaml4r </td>\n </tr>\n </table>\n\n\n\n\n <h3 class=\"projects\">Minor contributions</h3>\n\n\n <table>\n <tr>\n <th>Date </th>\n <th>Project </th>\n <th>Description </th>\n <th>Revision/Version </th>\n </tr>\n <tr>\n <td> 26 Mar 2008 </td>\n <td> Rails </td>\n <td> Nazi grammar fix </td>\n <td> <a href=\"http://dev.rubyonrails.org/changeset/9093\">[9093]</a> </td>\n </tr>\n <tr>\n <td> 7 Feb 2008 </td>\n <td> Rails </td>\n <td> FormBuilder doc typo fix </td>\n <td> <a href=\"http://dev.rubyonrails.org/changeset/8812\">[8812]</a> </td>\n </tr>\n <tr>\n <td> 2 Jan 2008 </td>\n <td> Rails </td>\n <td> atom_feed_helper fixes </td>\n <td> <a href=\"http://dev.rubyonrails.org/changeset/8529\">[8529]</a> </td>\n </tr>\n <tr>\n <td> 24 Dec 2007 </td>\n <td> test/spec </td>\n <td> Nested setup/teardown </td>\n <td> <a href=\"http://chneukirchen.org/repos/testspec/README\">0.5</a> </td>\n </tr>\n <tr>\n <td> 13 Dec 2007 </td>\n <td> Rails </td>\n <td> validates_acceptance_of doc fix </td>\n <td> <a href=\"http://dev.rubyonrails.org/changeset/8379\">[8379]</a> </td>\n </tr>\n <tr>\n <td> 30 Sep 2007 </td>\n <td> Rails </td>\n <td> has_many :through fix </td>\n <td> <a href=\"http://dev.rubyonrails.org/changeset/7692\">[7692]</a> </td>\n </tr>\n <tr>\n <td> 20 Sep 2007 </td>\n <td> Cerberus </td>\n <td> Rant support, <span class=\"caps\">RSS</span> bug fix </td>\n <td> <a href=\"http://cerberus.rubyforge.org/svn/\">[89]</a> </td>\n </tr>\n </table>','2008-02-23 11:39:13','2008-04-12 12:05:04');
/*!40000 ALTER TABLE `pages` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `posts`
--
DROP TABLE IF EXISTS `posts`;
CREATE TABLE `posts` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`slug` varchar(255) NOT NULL,
`body` text NOT NULL,
`body_html` text NOT NULL,
`active` tinyint(1) NOT NULL default '1',
`approved_comments_count` int(11) NOT NULL default '0',
`cached_tag_list` varchar(255) default NULL,
`published_at` datetime default NULL,
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `index_posts_on_published_at` (`published_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `posts`
--
LOCK TABLES `posts` WRITE;
/*!40000 ALTER TABLE `posts` DISABLE KEYS */;
INSERT INTO `posts` VALUES (2,'Debating','debating','Is it unethical to participate in a debate opposing a belief you feel strongly about?\r\n\r\nA debate is judged by an adjudicator, with the team that presents their case most effectively declared the victor. Participants are evaluated against many criteria, including presentation, structure of argument and use of language. Overall, it can be said that the winner of a debate is the most persuasive team.\r\n\r\nIt follows from this that when participating in a debate, one should aim to be as persuasive as possible to maximise their contribution to the team. In other words, your goal in a debate is to convince the audience (be that an auditorium or a single adjudicator) of the validity of your argument. We can see here a quandary emerging - if the topic you are assigned to argue is contrary to your beliefs (and to be thorough, a belief you believe others should hold), surely it is ethically irresponsible to attempt to persuade an audience otherwise?\r\n\r\nWhile a debate may be understood by the audience to be an academic exercise, by very definition a competent debater should be able to persuade her audience outside of this context. The formal setting of a debate does not preclude any such persuasion taking place. Whether or not the debater is actually capable of persuading her audience is irrelevant - the potential for persuasion to take place is what matters.\r\n\r\nHowever, if a belief that is held is well founded, then arguing for the negation of that belief, even to the best of your ability, would not be persuasive enough to convert a rational audience. This tenet assumes the opposing team to be of equal competence and possessing the ability to sufficiently rebut the opinions you present, since in order to be most persuasive, you will have to omit facts and arguments you feel to be supportive of your actual belief. Even with familiar knowledge of the opposing team, this is a high risk justification. \r\n\r\nDeliberately not performing to the best of your ability is another possibility to be considered. The problems with this approach are two-fold. First, you are misrepresenting your own capability which may have greater repercussions in future interactions with your audience or team members. Secondly, by not presenting a robust case in the opposition of your primary belief, it may reduce the effectiveness of your oppositions argument since a position that can triumph over a strong attack is strengthened by this demonstrated resilience [citation?].\r\n\r\nWhat remains is the recommended course of action: abstinence. Not participating in activities contrary to our ethical beliefs is of paramount importance - not only does it demonstrate the integrity required for ethical principles to be of maximal importance, in many cases it clearly signals to others our belief.\r\n\r\nIn this discussion we assume no extraordinary consequence resting on the outcome of the debate. Of course, in any real situation the consequence of not participating in the debate should be taken into consideration.','<p>Is it unethical to participate in a debate opposing a belief you feel strongly about?</p>\n\n\n <p>A debate is judged by an adjudicator, with the team that presents their case most effectively declared the victor. Participants are evaluated against many criteria, including presentation, structure of argument and use of language. Overall, it can be said that the winner of a debate is the most persuasive team.</p>\n\n\n <p>It follows from this that when participating in a debate, one should aim to be as persuasive as possible to maximise their contribution to the team. In other words, your goal in a debate is to convince the audience (be that an auditorium or a single adjudicator) of the validity of your argument. We can see here a quandary emerging &#8211; if the topic you are assigned to argue is contrary to your beliefs (and to be thorough, a belief you believe others should hold), surely it is ethically irresponsible to attempt to persuade an audience otherwise?</p>\n\n\n <p>While a debate may be understood by the audience to be an academic exercise, by very definition a competent debater should be able to persuade her audience outside of this context. The formal setting of a debate does not preclude any such persuasion taking place. Whether or not the debater is actually capable of persuading her audience is irrelevant &#8211; the potential for persuasion to take place is what matters.</p>\n\n\n <p>However, if a belief that is held is well founded, then arguing for the negation of that belief, even to the best of your ability, would not be persuasive enough to convert a rational audience. This tenet assumes the opposing team to be of equal competence and possessing the ability to sufficiently rebut the opinions you present, since in order to be most persuasive, you will have to omit facts and arguments you feel to be supportive of your actual belief. Even with familiar knowledge of the opposing team, this is a high risk justification.</p>\n\n\n <p>Deliberately not performing to the best of your ability is another possibility to be considered. The problems with this approach are two-fold. First, you are misrepresenting your own capability which may have greater repercussions in future interactions with your audience or team members. Secondly, by not presenting a robust case in the opposition of your primary belief, it may reduce the effectiveness of your oppositions argument since a position that can triumph over a strong attack is strengthened by this demonstrated resilience [citation?].</p>\n\n\n <p>What remains is the recommended course of action: abstinence. Not participating in activities contrary to our ethical beliefs is of paramount importance &#8211; not only does it demonstrate the integrity required for ethical principles to be of maximal importance, in many cases it clearly signals to others our belief.</p>\n\n\n <p>In this discussion we assume no extraordinary consequence resting on the outcome of the debate. Of course, in any real situation the consequence of not participating in the debate should be taken into consideration.</p>',1,2,NULL,'2006-12-20 00:37:00','2006-12-20 00:38:04','2007-10-27 01:35:23'),(3,'Peanut Butter Balls','peanut-butter-balls','These things rock. I make them for all party occasions. This year batches were given as Christmas gifts. Everybody else loves them also. Full credit for this recipe goes to a lovely woman named Alex, a friend\'s mum who always caters awesome parties. First, the original recipe, my notes after.\r\n\r\nh3. Alex\'s Peanut Butter Balls\r\n\r\nMakes 100\r\n\r\nh4. Ingredients\r\n\r\n * 1 cup crunchy peanut butter\r\n * 50g butter, softened\r\n * 1 cup icing-sugar\r\n * 2 cups Coco Pops\r\n * 375g Milk Choc Melts, melted\r\n * 1 1/2 teaspoons vegetable oil\r\n\r\nh4. Method\r\n \r\n # Beat peanut butter, butter and icing sugar in a small bowl with an electric mixer until combined. Stir in Coco Pops and mix well\r\n # Roll teaspoons of mixture into balls and place on baking paper-lined trays. Refrigerate until firm.\r\n # Combine Choc Melts and oil in a medium bowl. Using two forks, dip one ball at a time into chocolate mixture until coated; drain away excess chocolate. Return balls to trays; Refrigerate until set.\r\n \r\nFirst of all, I just can\'t get 100 out of this recipe. Record is 56. I think my teaspoon is too big. I don\'t have an electric mixer, so I put the whole lot into a large bowl and just give it a good old fashioned mix. I have broken a wooden spoon doing this, so soften the butter a little more than normal to assist. In making the last batch I accidentally melted the butter, which made mixing really easy, but a little more difficult to spoon balls. \r\n\r\nDo Choc Melts stay melted if you take them off heat? I have no idea - I melted them by putting them in a metal bowl on top of a saucepan of simmering water, and then dipped the balls straight into that.\r\n\r\nFor bonus Christmas Cheer, I tried painting mini bits of holly on each with cake decoration material. I think it\'s cool, but I don\'t think anyone else will recognize them for what they are.','<p>These things rock. I make them for all party occasions. This year batches were given as Christmas gifts. Everybody else loves them also. Full credit for this recipe goes to a lovely woman named Alex, a friend&#8217;s mum who always caters awesome parties. First, the original recipe, my notes after.</p>\n\n\n <h3>Alex&#8217;s Peanut Butter Balls</h3>\n\n\n <p>Makes 100</p>\n\n\n <h4>Ingredients</h4>\n\n\n <ul>\n <li>1 cup crunchy peanut butter</li>\n <li>50g butter, softened</li>\n <li>1 cup icing-sugar</li>\n <li>2 cups Coco Pops</li>\n <li>375g Milk Choc Melts, melted</li>\n <li>1 1/2 teaspoons vegetable oil</li>\n </ul>\n\n\n <h4>Method</h4>\n\n\n <ol>\n <li>Beat peanut butter, butter and icing sugar in a small bowl with an electric mixer until combined. Stir in Coco Pops and mix well</li>\n <li>Roll teaspoons of mixture into balls and place on baking paper-lined trays. Refrigerate until firm.</li>\n <li>Combine Choc Melts and oil in a medium bowl. Using two forks, dip one ball at a time into chocolate mixture until coated; drain away excess chocolate. Return balls to trays; Refrigerate until set.</li>\n </ol>\n\n\n <p>First of all, I just can&#8217;t get 100 out of this recipe. Record is 56. I think my teaspoon is too big. I don&#8217;t have an electric mixer, so I put the whole lot into a large bowl and just give it a good old fashioned mix. I have broken a wooden spoon doing this, so soften the butter a little more than normal to assist. In making the last batch I accidentally melted the butter, which made mixing really easy, but a little more difficult to spoon balls.</p>\n\n\n <p>Do Choc Melts stay melted if you take them off heat? I have no idea &#8211; I melted them by putting them in a metal bowl on top of a saucepan of simmering water, and then dipped the balls straight into that.</p>\n\n\n <p>For bonus Christmas Cheer, I tried painting mini bits of holly on each with cake decoration material. I think it&#8217;s cool, but I don&#8217;t think anyone else will recognize them for what they are.</p>',1,1,NULL,'2006-12-20 03:49:00','2006-12-20 03:42:56','2007-07-17 11:54:40'),(4,'Summertime Tagliarini','summertime-tagliarini','My folks recently had a garage sale, which saw Mum trying to offload her vast collection of food magazines. The ones she is always telling me to buy and that I never do. My thriftiness paid off - parental love compelled her to waive the $2 asking price for the 5th birthday collector\'s edition of \"Delicious\" magazine. There is a lot of good stuff in here - a whole section on cooking with chocolate practically guarantees that - so expect to see a few more recipes from within show up here.\r\n\r\nFeatured is an extract from one of Jamie Oliver\'s new books, containing a recipe for what he calls \"Summertime Tagliarini\". As \"Kathryn observes\":http://kathryncookswithjamie.blogspot.com/2006/11/summertime-tagliarini.html , it\'s pretty much just pasta with pesto and pinenuts. I\'ve been eating this all week, a result of buying the ingredients as listed to serve 4. The plan was to make 2 serves, but the first one turned out slightly too big, so I split the last batch into two and just added a bit more pasta. Which means I used the same quantity of pasta and halved the sauce, which was ill advised because it turned out a little bland, but I\'m a hungry man who needs his pasta. Next time I\'ll do 3 serves - but with a recipe that calls for 2 lemons there may be some non-trivial math involved.\r\n\r\nMy palette isn\'t quite refined enough to identify the qualities the small amount of pecorino cheese brings to this dish - I plan to try it without to find out. Got parmesan from the deli rather than the pre-packaged shaved stuff I normally buy - once again not sure how to describe the difference. I guess this is the first time I\'ve cooked with cheese since I came off being vegan, so that has probably got something to do with it.\r\n\r\nStupid Noob Tip: Don\'t squeeze the lemons directly into your mix, since you\'ll then have to get all the pips out, which takes way too long because they are particularly slippery and hard to distinguish from the pinenuts. And you\'ll know if you miss one when you are eating it. Rather, squeeze them into a glass then pour through a fork to catch the pips. Or you know, use a proper juicer.\r\n\r\nI could not find tagliarini at the supermarket (but have since located it! Next time...), so I just used normal spaghetti, which worked fine. I served in our One Good Bowl. Unfortunately we don\'t have any Good plates as I ended up with a large quantity of dressing falling at the bottom after I had eaten all the pasta. I am moving home soon, which means I get to use all her good stuff (which incidentally includes a camera so I can take photos).\r\n\r\nI really like the way Jamie writes his recipes. Almost conversational, with a focus on tasting throughout and how to adjust. This is particularly handy since I don\'t really know how to relate individual ingredients with the end product.\r\n\r\nServe with a nice white - I picked up a 2006 Jacob\'s Creek Riesling for $8 which was surprisingly good. This dish is very tasty. I consider this the current crown of my pasta dishes (which total 3 - the other 2 are homebrew concoctions), and it\'s also the only non-tomato based one. I will probably make it the next time someone is over for dinner.','<p>My folks recently had a garage sale, which saw Mum trying to offload her vast collection of food magazines. The ones she is always telling me to buy and that I never do. My thriftiness paid off &#8211; parental love compelled her to waive the $2 asking price for the 5th birthday collector&#8217;s edition of &#8220;Delicious&#8221; magazine. There is a lot of good stuff in here &#8211; a whole section on cooking with chocolate practically guarantees that &#8211; so expect to see a few more recipes from within show up here.</p>\n\n\n <p>Featured is an extract from one of Jamie Oliver&#8217;s new books, containing a recipe for what he calls &#8220;Summertime Tagliarini&#8221;. As <a href=\"http://kathryncookswithjamie.blogspot.com/2006/11/summertime-tagliarini.html\">Kathryn observes</a> , it&#8217;s pretty much just pasta with pesto and pinenuts. I&#8217;ve been eating this all week, a result of buying the ingredients as listed to serve 4. The plan was to make 2 serves, but the first one turned out slightly too big, so I split the last batch into two and just added a bit more pasta. Which means I used the same quantity of pasta and halved the sauce, which was ill advised because it turned out a little bland, but I&#8217;m a hungry man who needs his pasta. Next time I&#8217;ll do 3 serves &#8211; but with a recipe that calls for 2 lemons there may be some non-trivial math involved.</p>\n\n\n <p>My palette isn&#8217;t quite refined enough to identify the qualities the small amount of pecorino cheese brings to this dish &#8211; I plan to try it without to find out. Got parmesan from the deli rather than the pre-packaged shaved stuff I normally buy &#8211; once again not sure how to describe the difference. I guess this is the first time I&#8217;ve cooked with cheese since I came off being vegan, so that has probably got something to do with it.</p>\n\n\n <p>Stupid Noob Tip: Don&#8217;t squeeze the lemons directly into your mix, since you&#8217;ll then have to get all the pips out, which takes way too long because they are particularly slippery and hard to distinguish from the pinenuts. And you&#8217;ll know if you miss one when you are eating it. Rather, squeeze them into a glass then pour through a fork to catch the pips. Or you know, use a proper juicer.</p>\n\n\n <p>I could not find tagliarini at the supermarket (but have since located it! Next time&#8230;), so I just used normal spaghetti, which worked fine. I served in our One Good Bowl. Unfortunately we don&#8217;t have any Good plates as I ended up with a large quantity of dressing falling at the bottom after I had eaten all the pasta. I am moving home soon, which means I get to use all her good stuff (which incidentally includes a camera so I can take photos).</p>\n\n\n <p>I really like the way Jamie writes his recipes. Almost conversational, with a focus on tasting throughout and how to adjust. This is particularly handy since I don&#8217;t really know how to relate individual ingredients with the end product.</p>\n\n\n <p>Serve with a nice white &#8211; I picked up a 2006 Jacob&#8217;s Creek Riesling for $8 which was surprisingly good. This dish is very tasty. I consider this the current crown of my pasta dishes (which total 3 &#8211; the other 2 are homebrew concoctions), and it&#8217;s also the only non-tomato based one. I will probably make it the next time someone is over for dinner.</p>',1,2,NULL,'2006-12-22 03:54:00','2006-12-22 03:56:05','2007-07-17 11:54:03'),(5,'Advice for shared living','advice-for-shared-living','I have a number of friends moving into shared accommodation next year. At the same time, I will be ending a number of years in the same, as I move back home in preparation for international travel. At this auspicious time, I elect to bestow a small portion of my accumulated wisdom on the matter upon the general populace.\r\n\r\nh3. Cleaning\r\n\r\nHave a cleaning roster for the bathroom and lounge room. Even when all parties maintain the best of intentions in regard to keeping shared facilities clean, without a roster it won\'t get done. You will become accustomed to conditions far more untamed than your familiar experience. Without a roster, the house mate with the lowest tolerance for filth will either take on the cleaning load themselves, or become an annoying reminder for the others. Neither is desirable.\r\n\r\nKeep the kitchen sink empty. Dirty dishes are acceptable, as long as they are not required by others (think fry pans), and they don\'t prevent others from cleaning (as a full sink does).\r\n\r\nh3. Food\r\n\r\nIf communal cooking is desired, keep a receipt bucket that is reconciled every now and then. Eating other\'s food is acceptable as long as a usable portion remains and it is promptly replaced. Perishables past their use-by date are fair game unless explicitly marked - keeping your fridge clean and fresh!\r\n\r\nh3. Bills\r\n\r\nWhenever a bill is paid mark on the bill your name and the date. Throw these all in to the one drawer/folder, to be reconciled regularly. Unless telephone usage is severely skewed (excessive long distance calls, for example), it is usually easier to just split this down the middle.\r\n\r\nMostly common sense, these tips have worked for the households I\'ve resided in. Well, except the cleaning roster one. That\'s something I really wish we had. Guess who the low tolerance guy was...','<p>I have a number of friends moving into shared accommodation next year. At the same time, I will be ending a number of years in the same, as I move back home in preparation for international travel. At this auspicious time, I elect to bestow a small portion of my accumulated wisdom on the matter upon the general populace.</p>\n\n\n <h3>Cleaning</h3>\n\n\n <p>Have a cleaning roster for the bathroom and lounge room. Even when all parties maintain the best of intentions in regard to keeping shared facilities clean, without a roster it won&#8217;t get done. You will become accustomed to conditions far more untamed than your familiar experience. Without a roster, the house mate with the lowest tolerance for filth will either take on the cleaning load themselves, or become an annoying reminder for the others. Neither is desirable.</p>\n\n\n <p>Keep the kitchen sink empty. Dirty dishes are acceptable, as long as they are not required by others (think fry pans), and they don&#8217;t prevent others from cleaning (as a full sink does).</p>\n\n\n <h3>Food</h3>\n\n\n <p>If communal cooking is desired, keep a receipt bucket that is reconciled every now and then. Eating other&#8217;s food is acceptable as long as a usable portion remains and it is promptly replaced. Perishables past their use-by date are fair game unless explicitly marked &#8211; keeping your fridge clean and fresh!</p>\n\n\n <h3>Bills</h3>\n\n\n <p>Whenever a bill is paid mark on the bill your name and the date. Throw these all in to the one drawer/folder, to be reconciled regularly. Unless telephone usage is severely skewed (excessive long distance calls, for example), it is usually easier to just split this down the middle.</p>\n\n\n <p>Mostly common sense, these tips have worked for the households I&#8217;ve resided in. Well, except the cleaning roster one. That&#8217;s something I really wish we had. Guess who the low tolerance guy was&#8230;</p>',1,4,NULL,'2006-12-29 04:47:00','2006-12-29 04:50:56','2007-07-17 11:51:04'),(12,'Pantry Raiders #1','pantry-raiders-1','I am finally back in the family home for an evening, and the family decides to holiday up to Melbourne, leaving me here on my ownsome. Not keen to shop, I venture forth into the larder...\r\n\r\nh3. Fake Maharagwe\r\n\r\nServes 2 hungry people\r\n\r\nh4. Ingredients\r\n\r\n * 2 tomatoes\r\n * 1 can evaporated milk\r\n * 1 can chickpeas\r\n * 1 red onion\r\n * 1 clove garlic\r\n * Parsley\r\n * 1 tablespoon oil\r\n * Tumeric, Chilli, whatever other curry spices you have in your rack\r\n\r\nh4. Method\r\n\r\n # Chop onion and garlic, fry in pan with oil and seasonings\r\n # Add chickpeas, fry for a short time only\r\n # Add milk, tomatoes, parsley\r\n # Serve over rice\r\n\r\nThis dish looks exactly like Maharagwe. Almost tastes like it, but my substitutes really are poor. Coriander over parsley and coconut milk instead make for a much tastier and lighter meal (although I also got stuck with abborio rice, which is too gluggy). Also makes it vegan, which is a plus. I\'m unsure of the red onion - Mum is sold on them and won\'t use brown onions for anything - but it just feels wrong frying onions of the wrong color. They\'re only adding bulk not taste to this recipe, so I guess it doesn\'t matter. Tomatoes are the most important ingredient, as without them it is a little bland.\r\n\r\nI actually did remember that I needed to take a photo of this one, but I couldn\'t find a camera anywhere. The family must have taken it to Melbourne with them. Next meal gets pictures, honest.','<p>I am finally back in the family home for an evening, and the family decides to holiday up to Melbourne, leaving me here on my ownsome. Not keen to shop, I venture forth into the larder&#8230;</p>\n\n\n <h3>Fake Maharagwe</h3>\n\n\n <p>Serves 2 hungry people</p>\n\n\n <h4>Ingredients</h4>\n\n\n <ul>\n <li>2 tomatoes</li>\n <li>1 can evaporated milk</li>\n <li>1 can chickpeas</li>\n <li>1 red onion</li>\n <li>1 clove garlic</li>\n <li>Parsley</li>\n <li>1 tablespoon oil</li>\n <li>Tumeric, Chilli, whatever other curry spices you have in your rack</li>\n </ul>\n\n\n <h4>Method</h4>\n\n\n <ol>\n <li>Chop onion and garlic, fry in pan with oil and seasonings</li>\n <li>Add chickpeas, fry for a short time only</li>\n <li>Add milk, tomatoes, parsley</li>\n <li>Serve over rice</li>\n </ol>\n\n\n <p>This dish looks exactly like Maharagwe. Almost tastes like it, but my substitutes really are poor. Coriander over parsley and coconut milk instead make for a much tastier and lighter meal (although I also got stuck with abborio rice, which is too gluggy). Also makes it vegan, which is a plus. I&#8217;m unsure of the red onion &#8211; Mum is sold on them and won&#8217;t use brown onions for anything &#8211; but it just feels wrong frying onions of the wrong color. They&#8217;re only adding bulk not taste to this recipe, so I guess it doesn&#8217;t matter. Tomatoes are the most important ingredient, as without them it is a little bland.</p>\n\n\n <p>I actually did remember that I needed to take a photo of this one, but I couldn&#8217;t find a camera anywhere. The family must have taken it to Melbourne with them. Next meal gets pictures, honest.</p>',1,0,NULL,'2007-01-22 10:36:00','2007-01-22 10:36:53','2007-07-17 11:50:06'),(17,'Food Choices','food-choices','I am a vegetarian. Inevitably, people ask me \"why?\". I think it would be more productive for them to ask themselves why they are not, but that is by the by. For me the interesting question is \"why am I not vegan?\", which I will get to after I briefly cover the first.\r\n\r\nThere were two distinct ideas that led to my change of diet (I was an omnivore until mid way through last year). The first was a realisation that living the \"examined\" life (as Socrates put it) actually led to a dramatic increase in my quality of life, and in a similar vein that I was responsible for everything in my life (Satre\'s idea of freedom). This will be the subject of future writings, but it culminated in me trying to rid my life of \"contradictions\", of which my food choices presented many.\r\n\r\nFor vegetarianism, the deciding scenario was first introduced to me by Peter Singer in a public lecture he delivered at Melbourne Uni. It appeals to me because it avoids the need to take a non-mainstream stance on animal rights, but rather draws logical conclusions from common attitudes towards animals. Activities that harm animals for entertainment - bull/cock/dog fighting, for instance - are frowned upon by our culture, evidenced by the fact that they are banned by law. However, the more widespread harm of animals for non-essential food - in the form of battery farming - is condoned. This is a contradiction that I could not allow to stand, and so vowed to avoid battery farmed produce. Theoretically it is possible to continue an omnivorous diet within this constraint, but in practice finding (and affording!) organic meat is non-trivial, so I chose to abstain from meat all together. In addition, on non-ethical grounds I wanted to try the purported health benefits of vegetarianism, and also wanted to expand my cooking repertoire, which was depressingly confined to omnivorous cuisine.\r\n\r\nAfter getting comfortable with vegetarianism, I decided to try veganism. The only ethical justification for this was that livestock are an order of magnitude more expensive (in near all measures of the term) than grain and vegetable sources, and as such are a burden that our growing society simply cannot sustain. Contrary to many vegans, I do not believe that animals deserve the same rights as humans, drawing the (admittedly grey) line at self-reflection and higher order thought. To illustrate, the jury is still out on chimpanzees, but farmyard animals have not demonstrated to my satisfaction that they possess the necessary reasoning, desires or aspirations to be apportioned rights akin to our own.\r\n\r\nTo allow my body and habits to adjust I mandated a one month trial period. I discovered a number of new ways to cut animal products out of my diet, for example my sandwiches do not benefit for cheese or margarine, and soy milk is a much better alternative over cereal - adjustments I still hold to today. However my social life suffered. Not having any vegan friends, and knowing only one or two vegetarians, I found it difficult to eat out anywhere (since vegan meals are generally lacking if it is not the restaurant\'s main trade), and while people will usually be all too happy to cook a vegetarian meal for you, they generally blanch at the prospect of not using cheese. Not to mention that it eliminates virtually all desserts(!), and many types of beer(!!). In addition, I felt my alertness waning, and could not find ways to affordably maintain an athlete\'s diet (most notably protein sources - one can only eat so much peanut butter, and it is quite high in fat).\r\n\r\nMy quality of life diminished, both socially and in health, and I could not justify this by the one ethical tenet by which I had made my decision. I feel I can contribute more to activist and economical causes to offset such a choice if the rest of life is in order, so after a month of a vegan diet I returned to eggs and dairy (and choice beer).\r\n\r\nAfter more than 6 months of vegetarianism, I look and feel healthier than I have ever been. (For balance, I have also been exercising regularly, but do not feel constrained by the lack of meat in my diet). I will potentially try veganism again in the future - I feel support from my social group would help in this regard (which I can\'t see happening any time soon!) - and have no desire whatsoever to return to being an omnivore. I no longer crave steak, and the only time I feel my diet is restricted is in certain restaurants that do not pay enough attention to their menu. \r\n\r\nTo end with a quick rant, the \"Real Men Eat Meat\" mantra I so often encounter is, if you\'ll excuse the term, bullshit, and used as a facade by those too lazy to take control of their lives. I can accept you eating meat, just show me you have actually made an informed choice rather than blindly digesting the empty catchphrases employed by your ignorant peers.','<p>I am a vegetarian. Inevitably, people ask me &#8220;why?&#8221;. I think it would be more productive for them to ask themselves why they are not, but that is by the by. For me the interesting question is &#8220;why am I not vegan?&#8221;, which I will get to after I briefly cover the first.</p>\n\n\n <p>There were two distinct ideas that led to my change of diet (I was an omnivore until mid way through last year). The first was a realisation that living the &#8220;examined&#8221; life (as Socrates put it) actually led to a dramatic increase in my quality of life, and in a similar vein that I was responsible for everything in my life (Satre&#8217;s idea of freedom). This will be the subject of future writings, but it culminated in me trying to rid my life of &#8220;contradictions&#8221;, of which my food choices presented many.</p>\n\n\n <p>For vegetarianism, the deciding scenario was first introduced to me by Peter Singer in a public lecture he delivered at Melbourne Uni. It appeals to me because it avoids the need to take a non-mainstream stance on animal rights, but rather draws logical conclusions from common attitudes towards animals. Activities that harm animals for entertainment &#8211; bull/cock/dog fighting, for instance &#8211; are frowned upon by our culture, evidenced by the fact that they are banned by law. However, the more widespread harm of animals for non-essential food &#8211; in the form of battery farming &#8211; is condoned. This is a contradiction that I could not allow to stand, and so vowed to avoid battery farmed produce. Theoretically it is possible to continue an omnivorous diet within this constraint, but in practice finding (and affording!) organic meat is non-trivial, so I chose to abstain from meat all together. In addition, on non-ethical grounds I wanted to try the purported health benefits of vegetarianism, and also wanted to expand my cooking repertoire, which was depressingly confined to omnivorous cuisine.</p>\n\n\n <p>After getting comfortable with vegetarianism, I decided to try veganism. The only ethical justification for this was that livestock are an order of magnitude more expensive (in near all measures of the term) than grain and vegetable sources, and as such are a burden that our growing society simply cannot sustain. Contrary to many vegans, I do not believe that animals deserve the same rights as humans, drawing the (admittedly grey) line at self-reflection and higher order thought. To illustrate, the jury is still out on chimpanzees, but farmyard animals have not demonstrated to my satisfaction that they possess the necessary reasoning, desires or aspirations to be apportioned rights akin to our own.</p>\n\n\n <p>To allow my body and habits to adjust I mandated a one month trial period. I discovered a number of new ways to cut animal products out of my diet, for example my sandwiches do not benefit for cheese or margarine, and soy milk is a much better alternative over cereal &#8211; adjustments I still hold to today. However my social life suffered. Not having any vegan friends, and knowing only one or two vegetarians, I found it difficult to eat out anywhere (since vegan meals are generally lacking if it is not the restaurant&#8217;s main trade), and while people will usually be all too happy to cook a vegetarian meal for you, they generally blanch at the prospect of not using cheese. Not to mention that it eliminates virtually all desserts(!), and many types of beer(!!). In addition, I felt my alertness waning, and could not find ways to affordably maintain an athlete&#8217;s diet (most notably protein sources &#8211; one can only eat so much peanut butter, and it is quite high in fat).</p>\n\n\n <p>My quality of life diminished, both socially and in health, and I could not justify this by the one ethical tenet by which I had made my decision. I feel I can contribute more to activist and economical causes to offset such a choice if the rest of life is in order, so after a month of a vegan diet I returned to eggs and dairy (and choice beer).</p>\n\n\n <p>After more than 6 months of vegetarianism, I look and feel healthier than I have ever been. (For balance, I have also been exercising regularly, but do not feel constrained by the lack of meat in my diet). I will potentially try veganism again in the future &#8211; I feel support from my social group would help in this regard (which I can&#8217;t see happening any time soon!) &#8211; and have no desire whatsoever to return to being an omnivore. I no longer crave steak, and the only time I feel my diet is restricted is in certain restaurants that do not pay enough attention to their menu.</p>\n\n\n <p>To end with a quick rant, the &#8220;Real Men Eat Meat&#8221; mantra I so often encounter is, if you&#8217;ll excuse the term, bullshit, and used as a facade by those too lazy to take control of their lives. I can accept you eating meat, just show me you have actually made an informed choice rather than blindly digesting the empty catchphrases employed by your ignorant peers.</p>',1,0,NULL,'2007-01-22 11:43:00','2007-01-22 11:43:42','2007-07-17 11:49:22'),(18,'Peanut Butter Solutions','peanut-butter-solutions','I normally have peanut butter on bread (sometimes cooked!) before bed. But last night I had no bread. Dilemma!\r\n\r\nh3. Peanut Butter on Marie Biscuit\r\n\r\nServes 1/4\r\n\r\nh4. Ingredients\r\n\r\n * Peanut Butter, preferably crunchy\r\n * Marie Biscuit\r\n\r\nh4. Recipe\r\n\r\n # Spread as much peanut butter as possible on to biscuit\r\n\r\nSimple! I would not normally have said anything for such a trivial item, but this is so damn tasty I could not help myself.','<p>I normally have peanut butter on bread (sometimes cooked!) before bed. But last night I had no bread. Dilemma!</p>\n\n\n <h3>Peanut Butter on Marie Biscuit</h3>\n\n\n <p>Serves 1/4</p>\n\n\n <h4>Ingredients</h4>\n\n\n <ul>\n <li>Peanut Butter, preferably crunchy</li>\n <li>Marie Biscuit</li>\n </ul>\n\n\n <h4>Recipe</h4>\n\n\n <ol>\n <li>Spread as much peanut butter as possible on to biscuit</li>\n </ol>\n\n\n <p>Simple! I would not normally have said anything for such a trivial item, but this is so damn tasty I could not help myself.</p>',1,0,NULL,'2007-01-24 01:46:00','2007-01-24 01:50:53','2007-07-17 11:47:04'),(19,'Ethics of Zelda','ethics-of-zelda','It is the middle of the night, we find our hero, Link, stalking a woman through the streets. He is playing vigilante - she has been accused of stealing and Link plans to catch her in the act. The woman employs a host of devious tactics, but is unable to throw her tail. Eventually, we confront the woman as she attempts to steal from the open air market safe.\r\n\r\nThe woman tells her story of poverty - how her family riches were lost, how she is trying to \"make it\" by working during the day - But, oh! It is so good to talk to someone, I am so glad I ran into you! You have saved my soul, I will not steal any more!\r\n\r\n_Here, take this gift for listening to me_\r\n\r\n*Xavier (Me):* That, my friend, is the very definition of a bribe<br />\r\n*Jared (Brother):* Never! Nothing more than a genuine expression of gratitude!<br />\r\n*X:* You believe that cock and bull story?<br />\r\n*J:* All she needed was a sympathetic ear<br />\r\n*X:* You caught her committing a criminal offence. She clearly just bought you.<br />\r\n*J:* She won\'t steal again. I reformed her. That\'s true justice!<br />\r\n*X:* Civilians can\'t take justice into their own hands! She was a case for the courts.<br />\r\n*J:* I don\'t think Hyrule has much of a justice system...<br />\r\n*X:* I guess Link does do a lot of killin\'...<br />\r\n','<p>It is the middle of the night, we find our hero, Link, stalking a woman through the streets. He is playing vigilante &#8211; she has been accused of stealing and Link plans to catch her in the act. The woman employs a host of devious tactics, but is unable to throw her tail. Eventually, we confront the woman as she attempts to steal from the open air market safe.</p>\n\n\n <p>The woman tells her story of poverty &#8211; how her family riches were lost, how she is trying to &#8220;make it&#8221; by working during the day &#8211; But, oh! It is so good to talk to someone, I am so glad I ran into you! You have saved my soul, I will not steal any more!</p>\n\n\n <p><em>Here, take this gift for listening to me</em></p>\n\n\n <p><strong>Xavier (Me):</strong> That, my friend, is the very definition of a bribe<br />\n<strong>Jared (Brother):</strong> Never! Nothing more than a genuine expression of gratitude!<br />\n<strong>X:</strong> You believe that cock and bull story?<br />\n<strong>J:</strong> All she needed was a sympathetic ear<br />\n<strong>X:</strong> You caught her committing a criminal offence. She clearly just bought you.<br />\n<strong>J:</strong> She won&#8217;t steal again. I reformed her. That&#8217;s true justice!<br />\n<strong>X:</strong> Civilians can&#8217;t take justice into their own hands! She was a case for the courts.<br />\n<strong>J:</strong> I don&#8217;t think Hyrule has much of a justice system&#8230;<br />\n<strong>X:</strong> I guess Link does do a lot of killin&#8217;...<br /></p>',1,0,NULL,'2007-02-02 07:29:00','2007-02-02 07:28:00','2007-02-02 07:30:13'),(20,'Pizza','pizza','Team effort with my Dad for Sunday night dinner.\r\n\r\nh3. Pizza\r\n\r\nMakes 2 large pizzas\r\n\r\nh4. Ingredients\r\n\r\nh5. Dough\r\n\r\n * 2 cup water\r\n * 3+ cup plain flour (wholemeal if you\'re a hippy and don\'t have to appease your family)\r\n * 14g (2 sachet) yeast\r\n\r\nh5. Toppings\r\n\r\n * 2 Capsicum (1 red, 1 green)\r\n * 2 tomatoes\r\n * 3-4 Onions\r\n * Mushrooms\r\n * Baby spinach and/or rocket\r\n * Mozarella cheese\r\n * Tomato paste\r\n * Basil\r\n\r\nh4. Method\r\n\r\n # Combine all dough ingredients in a large bowl and mix together. Add flour until dough is a ... doughy consistency. Cover and leave to rise for a few hours\r\n # Dust tabletop liberally with flour and knead/roll out two thin pizza bases (they will rise when cooked)\r\n # Combine tomato paste and basil, then spread over the pizza bases right to the edges\r\n # Chop up all choppable toppings and throw evenly over the pizza. Also put the spinach and cheese on.\r\n # Put in a preheated oven (~180C) for 10-15 minutes, or until the cheese is well browned.\r\n\r\nOf course, as with any pizza there are many options here. This gives quite a doughy base - which I like - so to make a thinner, crustier base put the base by itself into the oven for a minute or so until it starts to rise. We did this for the second pizza and my folks preferred it. Nothing on this pizza really needs cooking, so I normally eat it a little bit undercooked - each to his own, I say.\r\n\r\nI actually thought the dough recipe was 1:1 flour:water, but that just gave me a soup, so I just kept adding flour. Probably ended up with about 4 cups total.\r\n\r\nI normally put olives on my pizza, but we forgot to buy any. Ricotta cheese also works well. My sister had a cheese pizza, which I thought rather boring but still tasty.\r\n\r\nAnd I finally remembered to take a picture!\r\n\r\n<img src=\"/assets/2007/2/11/pizza_1.jpg\" alt=\"Pizza\" />','<p>Team effort with my Dad for Sunday night dinner.</p>\n\n\n <h3>Pizza</h3>\n\n\n <p>Makes 2 large pizzas</p>\n\n\n <h4>Ingredients</h4>\n\n\n <h5>Dough</h5>\n\n\n <ul>\n <li>2 cup water</li>\n <li>3+ cup plain flour (wholemeal if you&#8217;re a hippy and don&#8217;t have to appease your family)</li>\n <li>14g (2 sachet) yeast</li>\n </ul>\n\n\n <h5>Toppings</h5>\n\n\n <ul>\n <li>2 Capsicum (1 red, 1 green)</li>\n <li>2 tomatoes</li>\n <li>3-4 Onions</li>\n <li>Mushrooms</li>\n <li>Baby spinach and/or rocket</li>\n <li>Mozarella cheese</li>\n <li>Tomato paste</li>\n <li>Basil</li>\n </ul>\n\n\n <h4>Method</h4>\n\n\n <ol>\n <li>Combine all dough ingredients in a large bowl and mix together. Add flour until dough is a &#8230; doughy consistency. Cover and leave to rise for a few hours</li>\n <li>Dust tabletop liberally with flour and knead/roll out two thin pizza bases (they will rise when cooked)</li>\n <li>Combine tomato paste and basil, then spread over the pizza bases right to the edges</li>\n <li>Chop up all choppable toppings and throw evenly over the pizza. Also put the spinach and cheese on.</li>\n <li>Put in a preheated oven (~180C) for 10-15 minutes, or until the cheese is well browned.</li>\n </ol>\n\n\n <p>Of course, as with any pizza there are many options here. This gives quite a doughy base &#8211; which I like &#8211; so to make a thinner, crustier base put the base by itself into the oven for a minute or so until it starts to rise. We did this for the second pizza and my folks preferred it. Nothing on this pizza really needs cooking, so I normally eat it a little bit undercooked &#8211; each to his own, I say.</p>\n\n\n <p>I actually thought the dough recipe was 1:1 flour:water, but that just gave me a soup, so I just kept adding flour. Probably ended up with about 4 cups total.</p>\n\n\n <p>I normally put olives on my pizza, but we forgot to buy any. Ricotta cheese also works well. My sister had a cheese pizza, which I thought rather boring but still tasty.</p>\n\n\n <p>And I finally remembered to take a picture!</p>\n\n\n <p><img src=\"/assets/2007/2/11/pizza_1.jpg\" alt=\"Pizza\" /></p>',1,1,NULL,'2007-02-11 09:31:00','2007-02-11 09:15:50','2007-07-17 11:46:39'),(96,'Buffalo+','buffalo','Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo\r\n\r\nCue my inner language geek. This seemingly inane tribute is in fact a valid sentence. I was certainly confused. There are 3 readings of the word necessary to comprehend:\r\n\r\n # The animal\r\n # The city\r\n # The verb, meaning to confuse, deceive, or intimidate\r\n\r\nReplacing each with an appropriate substitute, we could rewrite the sentence as:\r\nBuffalo people [that] Buffalo people intimidate[,] intimidate Buffalo people. The cool thing is you can do a similar thing (you need to add plurals) with any animal/verb pair, albeit without the city: Badgers badgers badger badger badgers; Dogs dogs dog dog dogs.\r\n\r\n<em>Explanation taken from \"Wikipedia\":http://en.wikipedia.org/wiki/Buffalo_buffalo_Buffalo_buffalo_buffalo_buffalo_Buffalo_buffalo.</em>\r\n\r\nUPDATE: Badger is also a city! Located in Minnesota.\r\n\r\n.... Badger badgers Badger badgers badger badger Badger badgers','<p>Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo</p>\n\n\n <p>Cue my inner language geek. This seemingly inane tribute is in fact a valid sentence. I was certainly confused. There are 3 readings of the word necessary to comprehend:</p>\n\n\n <ol>\n <li>The animal</li>\n <li>The city</li>\n <li>The verb, meaning to confuse, deceive, or intimidate</li>\n </ol>\n\n\n <p>Replacing each with an appropriate substitute, we could rewrite the sentence as:\nBuffalo people [that] Buffalo people intimidate[,] intimidate Buffalo people. The cool thing is you can do a similar thing (you need to add plurals) with any animal/verb pair, albeit without the city: Badgers badgers badger badger badgers; Dogs dogs dog dog dogs.</p>\n\n\n <p><em>Explanation taken from <a href=\"http://en.wikipedia.org/wiki/Buffalo_buffalo_Buffalo_buffalo_buffalo_buffalo_Buffalo_buffalo\">Wikipedia</a>.</em></p>\n\n\n <p><span class=\"caps\">UPDATE</span>: Badger is also a city! Located in Minnesota.</p>\n\n\n <p>.... Badger badgers Badger badgers badger badger Badger badgers</p>',1,0,NULL,'2007-02-20 03:57:00','2007-02-20 04:10:19','2007-02-20 22:34:22'),(97,'Eggplant Stacks','eggplant-stacks','I attended a murder mystery party last weekend, tasked with supplying an entree. Wanting to impress, I tried to work some eggplant magic.\r\n\r\n\"Baked Eggplant with Goat\'s Cheese and Cream (This is the recipe)\":http://www.taste.com.au/recipes/6458/baked+eggplant+with+goats+cheese+cream\r\n\r\nThere were going to be logistical headaches, since I was going to have to make most of it in Geelong a day before, take it on the train, and then finish preparation on location. At home, I sliced the eggplant and fried it (took 4 lots through the pan), then stored layered on paper towel in a large tupperware container, which went into the fridge overnight. \r\n\r\nOnce I got to the party the next day, I got the oven warming up while I prepared the cheese and tomato sauces. I used Persian fetta instead of goat\'s cheese and neglected to add any Thyme. I felt these were acceptable compromises. I probably should have made these sauces the day before also and brought them up on ice, since you want to spend as little time as possible preparing when there is a murder afoot.\r\n\r\nIt would also be prudent to cut more slices than you strictly need - I had catered for 8 and 10 showed up, so many of our stacks were only two high.\r\n\r\nPerhaps because the eggplant hadn\'t come straight out of the frying pan, I needed to leave it in the oven for an extra 5-10 minutes, and even then there were small portions that were not quite cooked. It could have used longer, but having not made the dish (or indeed, anything similar) before I was afraid of burning it.\r\n\r\nI was impressed with the outcome, but only half the plates were returned completely empty, so it may not be as tasty as I think. I\'ll be trying it again though that\'s for sure.','<p>I attended a murder mystery party last weekend, tasked with supplying an entree. Wanting to impress, I tried to work some eggplant magic.</p>\n\n\n <p><a href=\"http://www.taste.com.au/recipes/6458/baked+eggplant+with+goats+cheese+cream\" title=\"This is the recipe\">Baked Eggplant with Goat&#8217;s Cheese and Cream</a></p>\n\n\n <p>There were going to be logistical headaches, since I was going to have to make most of it in Geelong a day before, take it on the train, and then finish preparation on location. At home, I sliced the eggplant and fried it (took 4 lots through the pan), then stored layered on paper towel in a large tupperware container, which went into the fridge overnight.</p>\n\n\n <p>Once I got to the party the next day, I got the oven warming up while I prepared the cheese and tomato sauces. I used Persian fetta instead of goat&#8217;s cheese and neglected to add any Thyme. I felt these were acceptable compromises. I probably should have made these sauces the day before also and brought them up on ice, since you want to spend as little time as possible preparing when there is a murder afoot.</p>\n\n\n <p>It would also be prudent to cut more slices than you strictly need &#8211; I had catered for 8 and 10 showed up, so many of our stacks were only two high.</p>\n\n\n <p>Perhaps because the eggplant hadn&#8217;t come straight out of the frying pan, I needed to leave it in the oven for an extra 5-10 minutes, and even then there were small portions that were not quite cooked. It could have used longer, but having not made the dish (or indeed, anything similar) before I was afraid of burning it.</p>\n\n\n <p>I was impressed with the outcome, but only half the plates were returned completely empty, so it may not be as tasty as I think. I&#8217;ll be trying it again though that&#8217;s for sure.</p>',1,0,NULL,'2007-03-02 04:59:00','2007-03-02 05:12:22','2007-03-02 05:19:19'),(98,'It doesn\'t matter','it-doesn-t-matter','My secret to life: <em>I don\'t mind what happens.</em>\r\n\r\nQuoted just the other day by \"Everyday Wonderland\":http://everydaywonderland.com/notes/you-can-relax-now\r\n\r\nIf I get a job, if I go to Japan, if I keep living at home, if I move to Melbourne - my happiness is not dependent on any of these outcomes. \r\n\r\nThe key to this is framing traditionally depressing events - missing out on a job, finding yourself with nowhere to sleep, getting rejected - as challenges. <strong>They keep life interesting</strong>. I don\'t claim to be a master at this, but I am working on it.\r\n\r\nCombine this with an acceptance of events outside of your control. You can reasonably expect trains to run on time, so if one is delayed and you are late to an event, there is nothing you can do. Why stress? <strong>It doesn\'t solve anything and makes you feel like crap.</strong>\r\n\r\nAs with anything, this takes practice. Next time you find yourself getting stressed, think \"why can\'t I just accept these circumstances?\". Either you will find you can do something productive to alleviate them (even it that is as abstract as working on your time management skills so you\'re not pressed to deadline next time...), or find a sense of peace in succumbing to the greater forces of the universe.','<p>My secret to life: <em>I don&#8217;t mind what happens.</em></p>\n\n\n <p>Quoted just the other day by <a href=\"http://everydaywonderland.com/notes/you-can-relax-now\">Everyday Wonderland</a></p>\n\n\n <p>If I get a job, if I go to Japan, if I keep living at home, if I move to Melbourne &#8211; my happiness is not dependent on any of these outcomes.</p>\n\n\n <p>The key to this is framing traditionally depressing events &#8211; missing out on a job, finding yourself with nowhere to sleep, getting rejected &#8211; as challenges. <strong>They keep life interesting</strong>. I don&#8217;t claim to be a master at this, but I am working on it.</p>\n\n\n <p>Combine this with an acceptance of events outside of your control. You can reasonably expect trains to run on time, so if one is delayed and you are late to an event, there is nothing you can do. Why stress? <strong>It doesn&#8217;t solve anything and makes you feel like crap.</strong></p>\n\n\n <p>As with anything, this takes practice. Next time you find yourself getting stressed, think &#8220;why can&#8217;t I just accept these circumstances?&#8221;. Either you will find you can do something productive to alleviate them (even it that is as abstract as working on your time management skills so you&#8217;re not pressed to deadline next time&#8230;), or find a sense of peace in succumbing to the greater forces of the universe.</p>',1,0,NULL,'2007-03-05 03:23:00','2007-03-05 03:41:36','2007-07-09 10:25:44'),(99,'Dulce de leche','dulce-de-leche','In Guys and Dolls when Sky takes Sarah to Cuba, he orders a \"Dulce de leche\". I got pretty excited when I saw this in my recipe book.\r\n\r\nh3. Dulce de leche\r\n\r\nMakes 4-6\r\n\r\nh4. Ingredients\r\n\r\n * 3 cups milk\r\n * 1/2 cup sugar\r\n * 2 tablespoons ground almonds\r\n * 1 cinammon stick\r\n\r\nh4. Method\r\n\r\n # Throw everything into a saucepan, bring to the boil then cook over a medium heat for 30 minutes, stirring constantly\r\n # Remove cinammon stick, then serve warm\r\n\r\nThis is supposed to thicken up and go more like caramel, but mine patently didn\'t. The reference picture had an almond sitting on top of it - my almond sunk :( I\'ll have to check out other recipes to find out why this is so.\r\n\r\nIt was still very tasty. It wasn\'t quite what Sky would have liked, containing no \"preservative\" (rum), but it seemed to go down well with the company I had around. I put some icecream with mine right near the end, an addition I can highly recommend. You can apparently also drink this cold - I suspect that would work better if it was a bit thicker.','<p>In Guys and Dolls when Sky takes Sarah to Cuba, he orders a &#8220;Dulce de leche&#8221;. I got pretty excited when I saw this in my recipe book.</p>\n\n\n <h3>Dulce de leche</h3>\n\n\n <p>Makes 4-6</p>\n\n\n <h4>Ingredients</h4>\n\n\n <ul>\n <li>3 cups milk</li>\n <li>1/2 cup sugar</li>\n <li>2 tablespoons ground almonds</li>\n <li>1 cinammon stick</li>\n </ul>\n\n\n <h4>Method</h4>\n\n\n <ol>\n <li>Throw everything into a saucepan, bring to the boil then cook over a medium heat for 30 minutes, stirring constantly</li>\n <li>Remove cinammon stick, then serve warm</li>\n </ol>\n\n\n <p>This is supposed to thicken up and go more like caramel, but mine patently didn&#8217;t. The reference picture had an almond sitting on top of it &#8211; my almond sunk :( I&#8217;ll have to check out other recipes to find out why this is so.</p>\n\n\n <p>It was still very tasty. It wasn&#8217;t quite what Sky would have liked, containing no &#8220;preservative&#8221; (rum), but it seemed to go down well with the company I had around. I put some icecream with mine right near the end, an addition I can highly recommend. You can apparently also drink this cold &#8211; I suspect that would work better if it was a bit thicker.</p>',1,0,NULL,'2007-04-01 00:17:00','2007-04-01 00:29:39','2007-07-17 11:42:22'),(100,'New Job at RedBubble','new-job-at-redbubble','As of two weeks ago, I am now a full time software developer for <a href=\"http://www.redbubble.com\">RedBubble</a> - A website for artists and people who want to buy said artist\'s work. They have some really high quality prints available (the framed ones are gorgeous), and I\'m keen to buy some stuff myself once I have a house with walls to put it on...\r\n\r\nThey are based in Fitzroy, which is a really groovy area, but also kind of hard to get a house near - 2 weeks in and I\'m still homeless. I ride my bike to work, so there\'s a fairly large radius in which I\'m looking for a place, and a lot of houses available too, but there are just a ridiculous number of people applying for them.\r\n\r\nAnyway, they\'re a very cool startup with awesome people doing some really nice things with Ruby on Rails (my framework of choice). I\'m exceedingly happy to be working with them.','<p>As of two weeks ago, I am now a full time software developer for <a href=\"http://www.redbubble.com\">RedBubble</a> &#8211; A website for artists and people who want to buy said artist&#8217;s work. They have some really high quality prints available (the framed ones are gorgeous), and I&#8217;m keen to buy some stuff myself once I have a house with walls to put it on&#8230;</p>\n\n\n <p>They are based in Fitzroy, which is a really groovy area, but also kind of hard to get a house near &#8211; 2 weeks in and I&#8217;m still homeless. I ride my bike to work, so there&#8217;s a fairly large radius in which I&#8217;m looking for a place, and a lot of houses available too, but there are just a ridiculous number of people applying for them.</p>\n\n\n <p>Anyway, they&#8217;re a very cool startup with awesome people doing some really nice things with Ruby on Rails (my framework of choice). I&#8217;m exceedingly happy to be working with them.</p>',1,1,NULL,'2007-04-01 00:30:00','2007-04-01 00:35:32','2007-04-01 00:35:33'),(114,'Snippets in SVN','snippets-in-svn','Added a \"snippets section\":http://rhnh.svnrepository.com/svn/public/ruby/snippets/ to my public subversion repository, to hold random ruby goodies that I happen to have found useful at some point or another. Maybe you too will find something enticing?','<p>Added a <a href=\"http://rhnh.svnrepository.com/svn/public/ruby/snippets/\">snippets section</a> to my public subversion repository, to hold random ruby goodies that I happen to have found useful at some point or another. Maybe you too will find something enticing?</p>',1,0,NULL,'2007-02-13 09:51:00','2007-07-17 10:52:38','2007-07-17 10:52:38'),(115,'Gmail and PGP','gmail-and-pgp','Recently I set myself up to be able to use PGP signing and encryption with Thunderbird. Privacy: it\'ll cure what ails ya. That\'s all well and great when I\'m at home, but it\'s kind of hard to use my desktop when I\'m roaming the wild savannah of Africa. The two webmail products I use don\'t support PGP (gmail and the one provided by my hosting). So I\'ve started work on a \"mouseHole\":http://code.whytheluckystiff.net/mouseHole script - \"PgPirate\":http://rhnh.svnrepository.com/svn/public/ruby/pgpirate/ - that checks the code before it hits my browser and processes all the PGP stuff for me. Next step is to get it installed on a USB flash drive with \"ProxyLike\":http://whytheluckystiff.net/mouseHole/proxylike.user.rb so I can use it on most any other computer I happen to find myself using.','<p>Recently I set myself up to be able to use <span class=\"caps\">PGP</span> signing and encryption with Thunderbird. Privacy: it&#8217;ll cure what ails ya. That&#8217;s all well and great when I&#8217;m at home, but it&#8217;s kind of hard to use my desktop when I&#8217;m roaming the wild savannah of Africa. The two webmail products I use don&#8217;t support <span class=\"caps\">PGP</span> (gmail and the one provided by my hosting). So I&#8217;ve started work on a <a href=\"http://code.whytheluckystiff.net/mouseHole\">mouseHole</a> script &#8211; <a href=\"http://rhnh.svnrepository.com/svn/public/ruby/pgpirate/\">PgPirate</a> &#8211; that checks the code before it hits my browser and processes all the <span class=\"caps\">PGP</span> stuff for me. Next step is to get it installed on a <span class=\"caps\">USB</span> flash drive with <a href=\"http://whytheluckystiff.net/mouseHole/proxylike.user.rb\">ProxyLike</a> so I can use it on most any other computer I happen to find myself using.</p>',1,0,NULL,'2006-10-12 10:53:00','2007-07-17 10:54:07','2007-07-17 10:54:07'),(116,'Rant 0.5.7 Released','rant-0-5-7-released','Recently I have had the need to do some C# work. Not wanting to stray too far from ruby, I used \"Rant\":http://make.rubyforge.org to build my project. Initially, Rant wasn\'t quite up to the task, so I rolled up my sleeves and did something about it. The culmination of my efforts comprise the primary component of today\'s 0.5.7 release, so check it out for some white hot C# build file action.','<p>Recently I have had the need to do some C# work. Not wanting to stray too far from ruby, I used <a href=\"http://make.rubyforge.org\">Rant</a> to build my project. Initially, Rant wasn&#8217;t quite up to the task, so I rolled up my sleeves and did something about it. The culmination of my efforts comprise the primary component of today&#8217;s 0.5.7 release, so check it out for some white hot C# build file action.</p>',1,0,NULL,'2006-10-12 10:54:00','2007-07-17 10:54:55','2007-07-17 10:54:55'),(117,'The Colemak Diaries','the-colemak-diaries','I spend more than 50 hours per week in front of my computer. A large proportion of that time is spent coding or typing. It is well known that QWERTY is an inefficient keyboard layout, so in my quest to do things the Right Way I decided to do something about it.\r\n\r\nI considered two alternatives - \"Dvorak\":http://www.mwbrooks.com/dvorak/ and \"Colemak\":http://colemak.com/. The former is the more popular, but Colemak - a newer layout - appears to have addressed many of the issues people have found with Dvorak. No point going into detail here, I\'ll just link you to the \"Colemak FAQ\":http://colemak.com/faq.\r\n\r\nAnd so here presented is a diary of my transition to Colemak.\r\n\r\nh3. Day 1 - August 23\r\n\r\nA Wednesday evening after a long day at work. No mood to code, what is on my list of low-priority-things-to-be-done? Ah yes, this one has been here for a while - \"Learn Colemak\". Biggest concern is the productivity hit my coding will take while getting my WPM(Words Per Minute) back (80+ in QWERTY). I\'m coming to the end of a project at my day job, which means less typing than normal as I\'m testing/debugging. Busy social calendar will keep me away from my contract work for a bit, so it would seem that now is the time.\r\n\r\nInstallation on ubuntu linux is dead simple. I\'m stuck into Lesson 1 on the wiki in under 5 minutes. Freaking weird - I have to concentrate really hard lest I go into autopilot and let my fingers sneakily revert to QWERTY. Brings back memories of \"Home Row\" on the Apple IIe from grade 2.\r\n\r\nA remapped caps lock is the best thing since scoped closures. Within my first few mistakes I\'m tapping away with that left pinky like it\'s my job. Even if I go back to QWERTY I am keeping this mapping. I can\'t recommend it enough.\r\n\r\nAfter a about an hour on and off I\'m fairly confident with level 4. WPM on proper text is a frustrating 15.\r\n\r\nh3. Day 2 - August 24\r\n\r\nNo, this won\'t normally be a daily affair, but these first few days are going to be the interesting ones. Today I was a little anxious about taking Colemak to work, but confident I could get through the day and at least get something done. Installation on windows was also simple. Not quite as straight forward as linux because you have to manually install the language, but it ain\'t rocket science.\r\n\r\n\"Automatic\" words such as usernames and passwords are the trickiest. Passwords especially because you don\'t get any feedback on what you\'re typing.\r\n\r\nI use .NET at work, and Intellisense is a godsend. I hardly have to type at all! Going back to \"automatic\" behaviour, shortcuts such as CTRL+S are particularly difficult. It\'s a good thing CTRL+R isn\'t mapped to anything destructive in any of my apps! Semicolon is another tricky one, I think partially because (in QWERTY also) I never hit it with the right finger - my hand is usually moving to a shortcut or the mouse.\r\n\r\nI was planning to stay away from QWERTY as much as possible, but I have to use VMWare a lot and none of my VMs have Colemak installed. It would be impractical to do so. To try and minimize the detriment this would have on my Colemak skills I made a point of typing two fingered and looking at the keys while I type. Sad thing is I can still go faster than when I\'m typing Colemak, so I deliberately slowed myself down. Whether this was of benefit is a question maybe one day science can answer.\r\n\r\nThe day started with patience. By 4:30 I\'m ready to smash my keyboard through the monitor. Breath in. Breath out. I\'m going home.\r\n\r\nh3. Day 3 - August 25\r\n\r\nThis exercise has been promoted fairy near the top of my things-not-to-do-hungover list. Right there above \"Go to work\".\r\nI found the easiest (not necessarily best) way to type is by staring at the keyboard picture I printed out and sat below my monitor. WPM is up to 25.\r\n\r\nh3. Day 11 - August 31\r\n\r\nMaybe it wasn\'t such a good time to start learning. I was away all weekend, and then am out every night because it is production week for the musical I\'m doing. I haven\'t spent any more time at all practising, only just general usage at work. It is no longer painful to type, but I feel I could be a lot faster if I dedicated some time to practice. \r\n\r\nh3. Day 15 - September 5\r\n\r\nPanic sets in as I try to type something one handed while talking to someone on the phone. It just isn\'t happening. Encountered other problems when having to type with a colleague looking over my shoulder. Calm down - look at my print out. Occasionally they want to type on my machine, and here the windows language manager really fails me. You can set the current input language quickly through an icon on the toolbar, but it remembers what setting you had for each application. So I change it, they type some code, switch to a browser, flail their fingers, and before they know it have searched for gibberish. I can\'t think of a scenario where this behaviour would be beneficial - there should at least be an option to disable.\r\n\r\nh3. Day 17 - September 6\r\n\r\nHad to do some typing at a friends place. Took a minute or two to install Colemak, typed to my hearts content, then uninstalled when I was done. I can still type QWERTY if I have to, but the cases where this is required is less than you would think. When doing tech support on a foreign machine, often it is better to let the user drive anyway.\r\n\r\nh3. Day 28 - September 17\r\n\r\nOne month. Just tried lesson 12 in gtypist and got 47 WPM. I feel that is pretty good since every word has a z or an x in it. Error rate is still fairly high (7%) but I think I would have had that problem on QWERTY anyways. Seriously, who types that many bottom row characters? I find the things that slow me down the most when coding is punctuation. With ruby underscores give me all sorts of grief because they are such a big stretch. I\'m considering remapping it to an easier to reach key, but not sure what could be replaced. Maybe switch it with semicolon (on Colemak - where P is on QWERTY). Having learnt an alternate layout has given me confidence to further modify my keyboard to achieve typing nirvana. Note that the underscore is in the same place as it is on QWERTY. Learning Colemak has dramatically improved my technique, and at the same time exposed some potential hurdles to higher speeds. My QWERTY speed was largely a result of extend usage.\r\n\r\nOverall I am very satisfied with switching to Colemak. It was frustrating at first, but is much more comfortable to type now that I am used to it. My speed is still not quite what it was with QWERTY, but is fast enough for everyday usage. A few more weeks of usage and I\'ll be faster than a greased pig.\r\n\r\nNot to mention the awesome geek cred... (* tumble weeds *).\r\n\r\nLooks like I may need to bring forward my purchase of a \"keyboard fitting of my elite typing status\":http://daskeyboard.com. The connection at the back of my old keyboard is getting dodgy anyways. Honest.\r\n','<p>I spend more than 50 hours per week in front of my computer. A large proportion of that time is spent coding or typing. It is well known that <span class=\"caps\">QWERTY</span> is an inefficient keyboard layout, so in my quest to do things the Right Way I decided to do something about it.</p>\n\n\n <p>I considered two alternatives &#8211; <a href=\"http://www.mwbrooks.com/dvorak/\">Dvorak</a> and <a href=\"http://colemak.com/\">Colemak</a>. The former is the more popular, but Colemak &#8211; a newer layout &#8211; appears to have addressed many of the issues people have found with Dvorak. No point going into detail here, I&#8217;ll just link you to the <a href=\"http://colemak.com/faq\">Colemak <span class=\"caps\">FAQ</span></a>.</p>\n\n\n <p>And so here presented is a diary of my transition to Colemak.</p>\n\n\n <h3>Day 1 &#8211; August 23</h3>\n\n\n <p>A Wednesday evening after a long day at work. No mood to code, what is on my list of low-priority-things-to-be-done? Ah yes, this one has been here for a while &#8211; &#8220;Learn Colemak&#8221;. Biggest concern is the productivity hit my coding will take while getting my <acronym title=\"Words Per Minute\">WPM</acronym> back (80+ in <span class=\"caps\">QWERTY</span>). I&#8217;m coming to the end of a project at my day job, which means less typing than normal as I&#8217;m testing/debugging. Busy social calendar will keep me away from my contract work for a bit, so it would seem that now is the time.</p>\n\n\n <p>Installation on ubuntu linux is dead simple. I&#8217;m stuck into Lesson 1 on the wiki in under 5 minutes. Freaking weird &#8211; I have to concentrate really hard lest I go into autopilot and let my fingers sneakily revert to <span class=\"caps\">QWERTY</span>. Brings back memories of &#8220;Home Row&#8221; on the Apple IIe from grade 2.</p>\n\n\n <p>A remapped caps lock is the best thing since scoped closures. Within my first few mistakes I&#8217;m tapping away with that left pinky like it&#8217;s my job. Even if I go back to <span class=\"caps\">QWERTY I</span> am keeping this mapping. I can&#8217;t recommend it enough.</p>\n\n\n <p>After a about an hour on and off I&#8217;m fairly confident with level 4. <span class=\"caps\">WPM</span> on proper text is a frustrating 15.</p>\n\n\n <h3>Day 2 &#8211; August 24</h3>\n\n\n <p>No, this won&#8217;t normally be a daily affair, but these first few days are going to be the interesting ones. Today I was a little anxious about taking Colemak to work, but confident I could get through the day and at least get something done. Installation on windows was also simple. Not quite as straight forward as linux because you have to manually install the language, but it ain&#8217;t rocket science.</p>\n\n\n <p>&#8220;Automatic&#8221; words such as usernames and passwords are the trickiest. Passwords especially because you don&#8217;t get any feedback on what you&#8217;re typing.</p>\n\n\n <p>I use .NET at work, and Intellisense is a godsend. I hardly have to type at all! Going back to &#8220;automatic&#8221; behaviour, shortcuts such as <span class=\"caps\">CTRL</span>+S are particularly difficult. It&#8217;s a good thing <span class=\"caps\">CTRL</span>+R isn&#8217;t mapped to anything destructive in any of my apps! Semicolon is another tricky one, I think partially because (in <span class=\"caps\">QWERTY</span> also) I never hit it with the right finger &#8211; my hand is usually moving to a shortcut or the mouse.</p>\n\n\n <p>I was planning to stay away from <span class=\"caps\">QWERTY</span> as much as possible, but I have to use VMWare a lot and none of my VMs have Colemak installed. It would be impractical to do so. To try and minimize the detriment this would have on my Colemak skills I made a point of typing two fingered and looking at the keys while I type. Sad thing is I can still go faster than when I&#8217;m typing Colemak, so I deliberately slowed myself down. Whether this was of benefit is a question maybe one day science can answer.</p>\n\n\n <p>The day started with patience. By 4:30 I&#8217;m ready to smash my keyboard through the monitor. Breath in. Breath out. I&#8217;m going home.</p>\n\n\n <h3>Day 3 &#8211; August 25</h3>\n\n\n <p>This exercise has been promoted fairy near the top of my things-not-to-do-hungover list. Right there above &#8220;Go to work&#8221;.\nI found the easiest (not necessarily best) way to type is by staring at the keyboard picture I printed out and sat below my monitor. <span class=\"caps\">WPM</span> is up to 25.</p>\n\n\n <h3>Day 11 &#8211; August 31</h3>\n\n\n <p>Maybe it wasn&#8217;t such a good time to start learning. I was away all weekend, and then am out every night because it is production week for the musical I&#8217;m doing. I haven&#8217;t spent any more time at all practising, only just general usage at work. It is no longer painful to type, but I feel I could be a lot faster if I dedicated some time to practice.</p>\n\n\n <h3>Day 15 &#8211; September 5</h3>\n\n\n <p>Panic sets in as I try to type something one handed while talking to someone on the phone. It just isn&#8217;t happening. Encountered other problems when having to type with a colleague looking over my shoulder. Calm down &#8211; look at my print out. Occasionally they want to type on my machine, and here the windows language manager really fails me. You can set the current input language quickly through an icon on the toolbar, but it remembers what setting you had for each application. So I change it, they type some code, switch to a browser, flail their fingers, and before they know it have searched for gibberish. I can&#8217;t think of a scenario where this behaviour would be beneficial &#8211; there should at least be an option to disable.</p>\n\n\n <h3>Day 17 &#8211; September 6</h3>\n\n\n <p>Had to do some typing at a friends place. Took a minute or two to install Colemak, typed to my hearts content, then uninstalled when I was done. I can still type <span class=\"caps\">QWERTY</span> if I have to, but the cases where this is required is less than you would think. When doing tech support on a foreign machine, often it is better to let the user drive anyway.</p>\n\n\n <h3>Day 28 &#8211; September 17</h3>\n\n\n <p>One month. Just tried lesson 12 in gtypist and got 47 <span class=\"caps\">WPM</span>. I feel that is pretty good since every word has a z or an x in it. Error rate is still fairly high (7%) but I think I would have had that problem on <span class=\"caps\">QWERTY</span> anyways. Seriously, who types that many bottom row characters? I find the things that slow me down the most when coding is punctuation. With ruby underscores give me all sorts of grief because they are such a big stretch. I&#8217;m considering remapping it to an easier to reach key, but not sure what could be replaced. Maybe switch it with semicolon (on Colemak &#8211; where P is on <span class=\"caps\">QWERTY</span>). Having learnt an alternate layout has given me confidence to further modify my keyboard to achieve typing nirvana. Note that the underscore is in the same place as it is on <span class=\"caps\">QWERTY</span>. Learning Colemak has dramatically improved my technique, and at the same time exposed some potential hurdles to higher speeds. My <span class=\"caps\">QWERTY</span> speed was largely a result of extend usage.</p>\n\n\n <p>Overall I am very satisfied with switching to Colemak. It was frustrating at first, but is much more comfortable to type now that I am used to it. My speed is still not quite what it was with <span class=\"caps\">QWERTY</span>, but is fast enough for everyday usage. A few more weeks of usage and I&#8217;ll be faster than a greased pig.</p>\n\n\n <p>Not to mention the awesome geek cred&#8230; (* tumble weeds *).</p>\n\n\n <p>Looks like I may need to bring forward my purchase of a <a href=\"http://daskeyboard.com\">keyboard fitting of my elite typing status</a>. The connection at the back of my old keyboard is getting dodgy anyways. Honest.</p>',1,0,NULL,'2006-09-18 10:55:00','2007-07-17 10:57:21','2007-07-17 10:57:22'),(118,'HAML Tutorial','haml-tutorial','toc. \r\n# \"Introduction\":#intro\r\n# \"Fundamentals\":#fundamentals\r\n# \"XHTML techniques\":#xhtml_techniques\r\n# \"Ruby techniques\":#ruby_techniques\r\n# \"Conclusion\":#conclusion\r\n\r\nanchor. intro\r\n\r\n\"HAML\":http://dev.hamptoncatlin.com/haml is, and is an acronym for, an HTML Abstraction Markup Language. It is a replacement for the RHTML templates we are so used to in rails applications. If you are interested in _why_ one would need such a thing, please read John Philip Green\'s excellent \"HAML introduction\":http://unspace.ca/discover/haml. If you are more interested in _how_ one would use such a thing, read on!\r\n\r\nh3.#installation). Installation\r\n\r\nFirst things first, install the plugin:\r\n--- bash\r\n./script/plugin install -x svn://hamptoncatlin.com/haml/trunk haml\r\n---\r\n\r\nThis gives you a library to parse HAML templates, and also registers the .haml extension with rails. What this means is that to start using HAML you only need to rename your template from \'index.rhtml\' to \'index.haml\'. Do that now (in a new test app, an existing app, whatever), as we are about to get our first taste of ham ... (l).\r\n\r\nh3.#fundamentals). Fundamentals\r\n\r\n--- haml\r\n%h1 HAML Example\r\n%div\r\n %blockquote \r\n Farewell, Emily. It was fun, but you were a robot. \r\n You had no heart. \r\n--- \r\n\r\nIn the same vein as YAML and Python, *indentation matters* in HAML. It allows the parser to cleverly close our tags without being explicitly told to do so. Equals less typing for us lazy sloths. 2 spaces per indent is the rule. The first non-whitespace character of each line is what is used to decide how to parse the line. As may be evident, the % character indicates an XHTML tag. There are only 5 others, which we will cover in due course. Lines that do not begin with a special character are treated as normal text.\r\n\r\nh3.#xhtml_techniques). XHTML techniques\r\n\r\nBeing a prime requirement of a templating language, outputting XHTML is as simple as you would expect. I\'m not even going to write a full paragraph, this annotated listing should suffice:\r\n--- haml\r\n/ The slash character specifies an XHTML comment,\r\n/ but if after a tag name it self closes that tag\r\n%br/\r\n\r\n/ Attributes are specified by a hash provided directly after \r\n/ the tag name. There is NO SPACE between the tag and the hash\r\n%a{\"name\" => \"top\"}\r\n\r\n/ \"class\" is such a common attribute that it has a shortcut syntax\r\n%span.important Tada!\r\n\r\n/ Combine the two to impress you friends\r\n%span.extra{\"style\"=>\"color: red\"} Tada! Tada!\r\n\r\n/ A div with id is also common, so it too has a shortcut syntax\r\n#content\r\n This is a div with id \"content\"\r\n\r\n/ As does a div with class\r\n.fancy\r\n This is a div with class \"fancy\"\r\n---\r\n\r\nThe one curly aspect of generating XHTML you only need to deal with once - the doctype. You can use three exclamation marks on the first line of a template (hopefully a layout template) to output a doctype declaration. The problem is that it makes your document XHTML 1.0 transitional. Always. It also forgets to give you an XML prolog, so for now I specify these without using HAML, which brings up another point - you can mix normal XHTML tags and HAML code (although why you would want to outside of this fix eludes me).\r\n\r\n--- haml\r\n!!!\r\n%html{\"xmlns\"=>\"http://www.w3.org/1999/xhtml\", \"xml:lang\"=>\"en\"}\r\n %head\r\n %title Layout Example\r\n %body= @content_for_layout\r\n---\r\n--- haml\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\r\n \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n%html{\"xmlns\"=>\"http://www.w3.org/1999/xhtml\", \"xml:lang\"=>\"en\"}\r\n %head\r\n %title Layout Example\r\n %body= @content_for_layout\r\n---\r\nThose on the edge may want to keep an eye on \"this ticket\":http://dev.hamptoncatlin.com/haml/ticket/13 , which proposes a fix.\r\n\r\nh3.#ruby_techniques). Ruby techniques\r\n\r\n--- haml\r\n= link_to :controller => \'home\'\r\n= 1 + 2 # => 3\r\n%span= 1 + 2\r\n---\r\n\r\nText after an equals sign is evaluated as ruby code. It is roughly equivalent to\r\n@<%= 1 + 2 # => 3 %>@, but with one fairly major caveat: Each is evaluated independent of the rest of the template. Meaning the follow *will not* work, because the first line is evaluated as an entire ruby snippet, and does not find the @end@ it requires to be valid.\r\n\r\n--- haml\r\n= for i in (1..10)\r\n= i\r\n= end\r\n---\r\n\r\nThere is currently no way around this. There is a \"ticket\":http://dev.hamptoncatlin.com/haml/ticket/16 on the HAML trac with a proposed fix, but at the time of authoring the patch has not been attached. This is not as shocking as it may first appear. Ask yourself why you are using a loop or an if block in your code. If it cannot be reduced to a one liner, maybe it should be moved it out into a partial.\r\n\r\n--- haml\r\n= (1..10).inject(\'\') { |buffer, i| buffer + i.to_s }\r\n= render :partial => \'secret\', :collection => @secrets if cia?\r\n---\r\n\r\nAn alternative way to evaluate ruby code is to use a tilde instead of equals. This has the effect of searching in the evaluated string and replacing all newlines found in @pre@, @code@ or @textarea@ tags with an XHTML entity (@&#000A;@). This allows you to create neat markup even when displaying large chunks of preformatted text.\r\n\r\n--- haml\r\n ~ \"<textarea>\\n\\n\\n\\n\\n\\nYo</textarea>\"\r\n---\r\n\r\nKeep in mind that your ruby expression must not span more than one line - only the first line will be parsed and the rest will be treated as plain text. There is a \"proposed fix\":http://dev.hamptoncatlin.com/haml/ticket/6 (that makes 3! I want a pony) on the HAML Trac, if you are in to that sort of thing.\r\n\r\nh3.#conclusion). Conclusion\r\n\r\nHAML may not be quite as powerful as RHTML yet, but it drastically reduces the size of your views while greatly increasing readability and the quality of the markup. The best part is you can mix and match - you can start writing HAML templates in your existing project right now and keep all your old RHTML code hanging around.\r\n','toc. \n <ol>\n <li><a href=\"#intro\">Introduction</a></li>\n <li><a href=\"#fundamentals\">Fundamentals</a></li>\n <li><a href=\"#xhtml_techniques\"><span class=\"caps\">XHTML</span> techniques</a></li>\n <li><a href=\"#ruby_techniques\">Ruby techniques</a></li>\n <li><a href=\"#conclusion\">Conclusion</a></li>\n </ol>\n\n\n <p>anchor. intro</p>\n\n\n <p><a href=\"http://dev.hamptoncatlin.com/haml\"><span class=\"caps\">HAML</span></a> is, and is an acronym for, an <span class=\"caps\">HTML</span> Abstraction Markup Language. It is a replacement for the <span class=\"caps\">RHTML</span> templates we are so used to in rails applications. If you are interested in <em>why</em> one would need such a thing, please read John Philip Green&#8217;s excellent <a href=\"http://unspace.ca/discover/haml\"><span class=\"caps\">HAML</span> introduction</a>. If you are more interested in <em>how</em> one would use such a thing, read on!</p>\n\n\n <p>h3.#installation). Installation</p>\n\n\nFirst things first, install the plugin:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">./script/plugin install -x svn://hamptoncatlin.com/haml/trunk haml<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>This gives you a library to parse <span class=\"caps\">HAML</span> templates, and also registers the .haml extension with rails. What this means is that to start using <span class=\"caps\">HAML</span> you only need to rename your template from &#8216;index.rhtml&#8217; to &#8216;index.haml&#8217;. Do that now (in a new test app, an existing app, whatever), as we are about to get our first taste of ham &#8230; (l).</p>\n\n\n <p>h3.#fundamentals). Fundamentals</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">%h1 HAML Example<tt>\n</tt>%div<tt>\n</tt> %blockquote <tt>\n</tt> Farewell, Emily. It was fun, but you were a robot. <tt>\n</tt> You had no heart. <tt>\n</tt></pre></td>\n</tr></table>\n \n\n <p>In the same vein as <span class=\"caps\">YAML</span> and Python, <strong>indentation matters</strong> in <span class=\"caps\">HAML</span>. It allows the parser to cleverly close our tags without being explicitly told to do so. Equals less typing for us lazy sloths. 2 spaces per indent is the rule. The first non-whitespace character of each line is what is used to decide how to parse the line. As may be evident, the % character indicates an <span class=\"caps\">XHTML</span> tag. There are only 5 others, which we will cover in due course. Lines that do not begin with a special character are treated as normal text.</p>\n\n\n <p>h3.#xhtml_techniques). <span class=\"caps\">XHTML</span> techniques</p>\n\n\nBeing a prime requirement of a templating language, outputting <span class=\"caps\">XHTML</span> is as simple as you would expect. I&#8217;m not even going to write a full paragraph, this annotated listing should suffice:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">/ The slash character specifies an XHTML comment,<tt>\n</tt>/ but if after a tag name it self closes that tag<tt>\n</tt>%br/<tt>\n</tt><tt>\n</tt>/ Attributes are specified by a hash provided directly after <tt>\n</tt>/ the tag name. There is NO SPACE between the tag and the hash<tt>\n</tt>%a{&quot;name&quot; =&gt; &quot;top&quot;}<tt>\n</tt><tt>\n</tt>/ &quot;class&quot; is such a common attribute that it has a shortcut syntax<tt>\n</tt>%span.important Tada!<tt>\n</tt><tt>\n</tt>/ Combine the two to impress you friends<tt>\n</tt>%span.extra{&quot;style&quot;=&gt;&quot;color: red&quot;} Tada! Tada!<tt>\n</tt><tt>\n</tt>/ A div with id is also common, so it too has a shortcut syntax<tt>\n</tt>#content<tt>\n</tt> This is a div with id &quot;content&quot;<tt>\n</tt><tt>\n</tt>/ As does a div with class<tt>\n</tt>.fancy<tt>\n</tt> This is a div with class &quot;fancy&quot;<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>The one curly aspect of generating <span class=\"caps\">XHTML</span> you only need to deal with once &#8211; the doctype. You can use three exclamation marks on the first line of a template (hopefully a layout template) to output a doctype declaration. The problem is that it makes your document <span class=\"caps\">XHTML 1</span>.0 transitional. Always. It also forgets to give you an <span class=\"caps\">XML</span> prolog, so for now I specify these without using <span class=\"caps\">HAML</span>, which brings up another point &#8211; you can mix normal <span class=\"caps\">XHTML</span> tags and <span class=\"caps\">HAML</span> code (although why you would want to outside of this fix eludes me).</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">!!!<tt>\n</tt>%html{&quot;xmlns&quot;=&gt;&quot;http://www.w3.org/1999/xhtml&quot;, &quot;xml:lang&quot;=&gt;&quot;en&quot;}<tt>\n</tt> %head<tt>\n</tt> %title Layout Example<tt>\n</tt> %body= @content_for_layout<tt>\n</tt></pre></td>\n</tr></table>\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<tt>\n</tt>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot;<tt>\n</tt> &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;<tt>\n</tt>%html{&quot;xmlns&quot;=&gt;&quot;http://www.w3.org/1999/xhtml&quot;, &quot;xml:lang&quot;=&gt;&quot;en&quot;}<tt>\n</tt> %head<tt>\n</tt> %title Layout Example<tt>\n</tt> %body= @content_for_layout<tt>\n</tt></pre></td>\n</tr></table>\n\nThose on the edge may want to keep an eye on <a href=\"http://dev.hamptoncatlin.com/haml/ticket/13\">this ticket</a> , which proposes a fix.\n\n <p>h3.#ruby_techniques). Ruby techniques</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">= link_to :controller =&gt; \'home\'<tt>\n</tt>= 1 + 2 # =&gt; 3<tt>\n</tt>%span= 1 + 2<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Text after an equals sign is evaluated as ruby code. It is roughly equivalent to\n<code>&lt;%= 1 + 2 # =&gt; 3 %&gt;</code>, but with one fairly major caveat: Each is evaluated independent of the rest of the template. Meaning the follow <strong>will not</strong> work, because the first line is evaluated as an entire ruby snippet, and does not find the <code>end</code> it requires to be valid.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">= for i in (1..10)<tt>\n</tt>= i<tt>\n</tt>= end<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>There is currently no way around this. There is a <a href=\"http://dev.hamptoncatlin.com/haml/ticket/16\">ticket</a> on the <span class=\"caps\">HAML</span> trac with a proposed fix, but at the time of authoring the patch has not been attached. This is not as shocking as it may first appear. Ask yourself why you are using a loop or an if block in your code. If it cannot be reduced to a one liner, maybe it should be moved it out into a partial.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">= (1..10).inject(\'\') { |buffer, i| buffer + i.to_s }<tt>\n</tt>= render :partial =&gt; \'secret\', :collection =&gt; @secrets if cia?<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>An alternative way to evaluate ruby code is to use a tilde instead of equals. This has the effect of searching in the evaluated string and replacing all newlines found in <code>pre</code>, <code>code</code> or <code>textarea</code> tags with an <span class=\"caps\">XHTML</span> entity (<code>&amp;#000A;</code>). This allows you to create neat markup even when displaying large chunks of preformatted text.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"> ~ &quot;&lt;textarea&gt;\\n\\n\\n\\n\\n\\nYo&lt;/textarea&gt;&quot;<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Keep in mind that your ruby expression must not span more than one line &#8211; only the first line will be parsed and the rest will be treated as plain text. There is a <a href=\"http://dev.hamptoncatlin.com/haml/ticket/6\">proposed fix</a> (that makes 3! I want a pony) on the <span class=\"caps\">HAML</span> Trac, if you are in to that sort of thing.</p>\n\n\n <p>h3.#conclusion). Conclusion</p>\n\n\n <p><span class=\"caps\">HAML</span> may not be quite as powerful as <span class=\"caps\">RHTML</span> yet, but it drastically reduces the size of your views while greatly increasing readability and the quality of the markup. The best part is you can mix and match &#8211; you can start writing <span class=\"caps\">HAML</span> templates in your existing project right now and keep all your old <span class=\"caps\">RHTML</span> code hanging around.</p>',1,1,NULL,'2006-09-16 11:00:00','2007-07-17 11:00:57','2007-07-17 11:41:13'),(119,'Checkbox Set Helper Released','checkbox-set-helper-released','Tremble before my newest Ruby on Rails plugin: \"Checkbox Set Helper\":http://rhnh.net/projects/checkbox+set+helper. Behind its rather dry name hides some rather handy code for when you are trying to use checkboxes to manage multiple items, especially @has_many@ relationships.','<p>Tremble before my newest Ruby on Rails plugin: <a href=\"http://rhnh.net/projects/checkbox+set+helper\">Checkbox Set Helper</a>. Behind its rather dry name hides some rather handy code for when you are trying to use checkboxes to manage multiple items, especially <code>has_many</code> relationships.</p>',1,0,NULL,'2006-09-01 11:02:00','2007-07-17 11:03:21','2007-07-17 11:03:21'),(120,'DOM Quirks','dom-quirks','toc. \r\n# \"Introduction\":#intro\r\n# \"Creating Elements\":#creating\r\n# \"Appending Elements\":#appending\r\n# \"Finding Elements\":#finding\r\n# \"Conclusion\":#conclusion\r\n\r\nUnobtrusive javascript is undoubtably the nicest way to add Javascript behaviours to a web page. It keeps the HTML clean and (hopefully) ensures it will degrade properly in older browsers. That said, the methods you generally use for this type of design (see \"Unobtrusive Javascript\":http://www.onlinetools.org/articles/unobtrusivejavascript/ for an excellent introduction) contain a number of quirks you should be aware, of which this article addresses a few. In particular, unexpected or non-obvious behaviour in @createElement@, @appendChild@, and @getElementsByTagName@.\r\n\r\nh3(#creating). Creating Elements\r\n\r\nThe @createElement@ function allows the dynamic creation of HTML elements. It takes one parameter: the type of element to create. It is used in conjunction with @setAttribute@ to modify the attributes of a new element. Elements created in this way will not actually be displayed in the document until added with @appendChild@, @insertBefore@ or @replaceChild@. The following code creates an image (but does not display it):\r\n--- javascript\r\nelement = document.createElement(\"img\");\r\nelement.setAttribute(\"src\", \"img1.jpg\");\r\n---\r\n\r\nWhile support for this is good in the major browsers, there is a small quirk in IE that can cause some pain when creating forms. To quote \"MSDN\":http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/createelement.asp:\r\n\r\nbq. Attributes can be included with the sTag as long as the entire string is valid HTML. You should do this if you wish to include the NAME attribute at run time on objects created with the createElement method.\r\n\r\nWhat this means is that in IE, you can do the following (which is equivalent to the above snippet of code):\r\n--- javascript\r\nstr = \'<img src=\"img1.jpg\" />\';\r\nelement = document.createElement(str);\r\n---\r\nWhile IE supports the first method shown for most attributes, if you want to set the \"name\" attribute of an element you *must* use the second method. This is a problem since Mozilla will throw an exception on the latter. Thankfully, we can use exception handling for an easy workaround:\r\n--- javascript\r\ntry {\r\n str = \"<input name=\'aradiobutton\' type=\'radio\' />\"\r\n element = document.createElement(str);\r\n} catch (e) {\r\n element = document.createElement(\"input\");\r\n element.setAttribute(\"name\", \"aradiobutton\");\r\n element.setAttribute(\"type\", \"radio\");\r\n}\r\n---\r\n\r\nh3(#appending). Appending Elements\r\n\r\nUsing @appendChild@ (or @replaceChild@) is the \"correct\" way to add content to a DOM, rather than the more popular @innerHTML@ property. \r\n\r\nWhen using this function to add rows to a table, you should add the rows to a @tbody@ or equivalent tag inside the table, not the @table@ tag itself. Mozilla and Opera will pick up the new rows if you add them directly to the table tag, whereas IE will not.\r\n\r\nh3(#finding). Finding Elements\r\n\r\nYou can get a collection of all tags of a specific type using the @getElementsByTagName@ function. Not only is this handy for standard unobtrusive javascript behaviours, you can also use it to do cool things like automatically process all elements in a form.\r\n\r\n--- javascript\r\nfunction showData(form) {\r\n inputs = form.getElementsByTagName(\"input\");\r\n buffer = \"\";\r\n for (i = 0; i < inputs.length; i++)\r\n buffer += inputs[i].name + \"=\" + inputs[i].value + \"\\n\";\r\n\r\n alert(buffer);\r\n}\r\n---\r\n\r\nAlthough it may appear to act like an array, it is very important to remember that the returned object is actually an @HTMLCollection@. It does not support any array-like functions (concat, splice, etc...) bar those presented above. This is because the @HTMLCollection@ is a live representation of the page\'s HTML, and such functions would interfere.\r\n\r\n--- javascript\r\n// Assume an empty document\r\nimages = document.getElementsByTagName(\"img\"); \r\n// images.length = 0\r\naddImgElementToDocument(); // function implemented elsewhere \r\n// images.length = 1;\r\n---\r\n\r\nThis can be an annoyance when we know that the HTML structure will not be changing, and is easily worked around:\r\n\r\n--- javascript\r\nfunction collectionToArray(col) {\r\n a = new Array();\r\n for (i = 0; i < col.length; i++)\r\n a[a.length] = col[i];\r\n return a;\r\n}\r\n\r\nfunction showData(form) {\r\n elems = form.getElementsByTagName(\"input\");\r\n inputs = collectionToArray(elems);\r\n elems = form.getElementsByTagName(\"select\");\r\n inputs = inputs.concat(collectionToArray(elems));\r\n buffer = \"\";\r\n for (i = 0; i < inputs.length; i++)\r\n buffer += inputs[i].name + \"=\" + inputs[i].value + \"\\n\";\r\n \r\n alert(buffer);\r\n}\r\n---\r\n\r\nIt would be nice if the @collectionToArray@ function above could be added to @HTMLCollection@\'s prototype, however for some reason it is read-only.\r\n\r\nh3(#conclusion). Conclusion\r\n\r\nThese quirks may be minor and their solutions trivial, but it helps to be aware of them when coding any sort of unobtrusive javascript as it can reduce the amount of time you spend debugging seemingly illogical behaviour.\r\n','toc. \n <ol>\n <li><a href=\"#intro\">Introduction</a></li>\n <li><a href=\"#creating\">Creating Elements</a></li>\n <li><a href=\"#appending\">Appending Elements</a></li>\n <li><a href=\"#finding\">Finding Elements</a></li>\n <li><a href=\"#conclusion\">Conclusion</a></li>\n </ol>\n\n\n <p>Unobtrusive javascript is undoubtably the nicest way to add Javascript behaviours to a web page. It keeps the <span class=\"caps\">HTML</span> clean and (hopefully) ensures it will degrade properly in older browsers. That said, the methods you generally use for this type of design (see <a href=\"http://www.onlinetools.org/articles/unobtrusivejavascript/\">Unobtrusive Javascript</a> for an excellent introduction) contain a number of quirks you should be aware, of which this article addresses a few. In particular, unexpected or non-obvious behaviour in <code>createElement</code>, <code>appendChild</code>, and <code>getElementsByTagName</code>.</p>\n\n\n <h3 id=\"creating\">Creating Elements</h3>\n\n\nThe <code>createElement</code> function allows the dynamic creation of <span class=\"caps\">HTML</span> elements. It takes one parameter: the type of element to create. It is used in conjunction with <code>setAttribute</code> to modify the attributes of a new element. Elements created in this way will not actually be displayed in the document until added with <code>appendChild</code>, <code>insertBefore</code> or <code>replaceChild</code>. The following code creates an image (but does not display it):\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">element = document.createElement(&quot;img&quot;);<tt>\n</tt>element.setAttribute(&quot;src&quot;, &quot;img1.jpg&quot;);<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>While support for this is good in the major browsers, there is a small quirk in IE that can cause some pain when creating forms. To quote <a href=\"http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/createelement.asp\"><span class=\"caps\">MSDN</span></a>:</p>\n\n\n <blockquote>\n <p>Attributes can be included with the sTag as long as the entire string is valid <span class=\"caps\">HTML</span>. You should do this if you wish to include the <span class=\"caps\">NAME</span> attribute at run time on objects created with the createElement method.</p>\n </blockquote>\n\n\nWhat this means is that in IE, you can do the following (which is equivalent to the above snippet of code):\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">str = \'&lt;img src=&quot;img1.jpg&quot; /&gt;\';<tt>\n</tt>element = document.createElement(str);<tt>\n</tt></pre></td>\n</tr></table>\n\nWhile IE supports the first method shown for most attributes, if you want to set the &#8220;name&#8221; attribute of an element you <strong>must</strong> use the second method. This is a problem since Mozilla will throw an exception on the latter. Thankfully, we can use exception handling for an easy workaround:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">try {<tt>\n</tt> str = &quot;&lt;input name=\'aradiobutton\' type=\'radio\' /&gt;&quot;<tt>\n</tt> element = document.createElement(str);<tt>\n</tt>} catch (e) {<tt>\n</tt> element = document.createElement(&quot;input&quot;);<tt>\n</tt> element.setAttribute(&quot;name&quot;, &quot;aradiobutton&quot;);<tt>\n</tt> element.setAttribute(&quot;type&quot;, &quot;radio&quot;);<tt>\n</tt>}<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"appending\">Appending Elements</h3>\n\n\n <p>Using <code>appendChild</code> (or <code>replaceChild</code>) is the &#8220;correct&#8221; way to add content to a <span class=\"caps\">DOM</span>, rather than the more popular <code>innerHTML</code> property.</p>\n\n\n <p>When using this function to add rows to a table, you should add the rows to a <code>tbody</code> or equivalent tag inside the table, not the <code>table</code> tag itself. Mozilla and Opera will pick up the new rows if you add them directly to the table tag, whereas IE will not.</p>\n\n\n <h3 id=\"finding\">Finding Elements</h3>\n\n\n <p>You can get a collection of all tags of a specific type using the <code>getElementsByTagName</code> function. Not only is this handy for standard unobtrusive javascript behaviours, you can also use it to do cool things like automatically process all elements in a form.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">function showData(form) {<tt>\n</tt> inputs = form.getElementsByTagName(&quot;input&quot;);<tt>\n</tt> buffer = &quot;&quot;;<tt>\n</tt> for (i = 0; i &lt; inputs.length; i++)<tt>\n</tt> buffer += inputs[i].name + &quot;=&quot; + inputs[i].value + &quot;\\n&quot;;<tt>\n</tt><tt>\n</tt> alert(buffer);<tt>\n</tt>}<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Although it may appear to act like an array, it is very important to remember that the returned object is actually an <code>HTMLCollection</code>. It does not support any array-like functions (concat, splice, etc&#8230;) bar those presented above. This is because the <code>HTMLCollection</code> is a live representation of the page&#8217;s <span class=\"caps\">HTML</span>, and such functions would interfere.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">// Assume an empty document<tt>\n</tt>images = document.getElementsByTagName(&quot;img&quot;); <tt>\n</tt>// images.length = 0<tt>\n</tt>addImgElementToDocument(); // function implemented elsewhere <tt>\n</tt>// images.length = 1;<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>This can be an annoyance when we know that the <span class=\"caps\">HTML</span> structure will not be changing, and is easily worked around:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">function collectionToArray(col) {<tt>\n</tt> a = new Array();<tt>\n</tt> for (i = 0; i &lt; col.length; i++)<tt>\n</tt> a[a.length] = col[i];<tt>\n</tt> return a;<tt>\n</tt>}<tt>\n</tt><tt>\n</tt>function showData(form) {<tt>\n</tt> elems = form.getElementsByTagName(&quot;input&quot;);<tt>\n</tt> inputs = collectionToArray(elems);<tt>\n</tt> elems = form.getElementsByTagName(&quot;select&quot;);<tt>\n</tt> inputs = inputs.concat(collectionToArray(elems));<tt>\n</tt> buffer = &quot;&quot;;<tt>\n</tt> for (i = 0; i &lt; inputs.length; i++)<tt>\n</tt> buffer += inputs[i].name + &quot;=&quot; + inputs[i].value + &quot;\\n&quot;;<tt>\n</tt> <tt>\n</tt> alert(buffer);<tt>\n</tt>}<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>It would be nice if the <code>collectionToArray</code> function above could be added to <code>HTMLCollection</code>&#8217;s prototype, however for some reason it is read-only.</p>\n\n\n <h3 id=\"conclusion\">Conclusion</h3>\n\n\n <p>These quirks may be minor and their solutions trivial, but it helps to be aware of them when coding any sort of unobtrusive javascript as it can reduce the amount of time you spend debugging seemingly illogical behaviour.</p>',1,0,NULL,'2006-07-23 11:04:00','2007-07-17 11:05:54','2007-07-17 11:08:12'),(121,'Building Firefox Extensions','building-firefox-extensions','toc. \r\n# \"Introduction\":#intro\r\n# \"Extension Basics\":#basics\r\n# \"Rant\":#rant\r\n# \"Making the JAR\":#jar\r\n# \"Cleaning\":#cleaning\r\n# \"Making the XPI\":#xpi\r\n# \"Final Touches\":#final\r\n# \"The Completed Rakefile\":#completed\r\n\r\nanchor. intro\r\n\r\nThis article will introduce the basics of \"Ruby Rant\":http://make.rubyforge.org by creating a Rantfile to build \"Firefox\":http://www.mozilla.com/firefox/ extensions. You don\'t actually need to know anything about extensions to follow along, but if you are interested may I recommend this \"tutorial by roachfiend\":http://roachfiend.com/archives/2004/12/08/how-to-create-firefox-extensions/. You will note that that article (and many others on the same topic) use a batch file to build their extensions. While this is quick to set up for simple development, a build file saves time and effort in the long run, and gives more flexibility.\r\n\r\nI assume you at least know what Rant is - a replacement for Rake - and have it installed and working. Please visit their website for more information on this topic. This is also not a build file tutorial - you should know what a task and a dependency are.\r\n\r\nh3(#basics). Extension Basics\r\n\r\nThe first step is to decide on directory structure for your project. Firefox extensions are comprised of two main portions - the install instructions, and the actual content of the extension. A Firefox extension (an XPI file) is really just a zip file with a different extension. You can open it up using your favourite archive manager and see the following structure:\r\n\r\n--- none\r\nmyextension.xpi/\r\n install.js\r\n install.rdf\r\n chrome/\r\n myextension.jar/\r\n ... myextension content ...\r\n---\r\n\r\nLikewise, the JAR file is also a zip file with an alternate extension. We can see that there are two major portions of the extension that need building, the JAR and the XPI (which contains the JAR). As such, we will use a source structure that looks like this (\"download the source code\":source):\r\n\r\n--- none\r\nmyextension/\r\n Rantfile\r\n src/\r\n install/\r\n jar/\r\n---\r\n\r\nClearly, the install folder will only contain our @install.js@ and @install.rdf@ files, and the jar folder will contain the contents of our jar. \r\n\r\nh3(#rant). Rant\r\n\r\nEnough introduction, let\'s get started with Rant. Rant is a replacement for Rake. I won\'t go into detail here, but one of the advantages for our purposes is portable zip creation without the need for external libraries. Rant is similar to Rake in that you define all your build tasks in a file in your root directory - the Rantfile. We will create 3 tasks - package, clean, and clobber. The first obviously packages up our extension into a zip file and gives it a @.xpi@ extension. \"clean\" removes temporary files used to package the extension, and \"clobber\" removes all generated artefacts (basically the same as clean but also removes the XPI file).\r\n\r\nh3(#jar). Making the JAR\r\n\r\nBaby steps steps though - first of all we want to create the JAR file for our extension. We can do this using the Archive::Zip generator provided by Rant:\r\n\r\n--- ruby\r\nimport \"archive/zip\"\r\nrequire \"archive_rootdir_fix\"\r\n\r\ngen Archive::Zip, \"build/helloworld\", \r\n :files => sys[\"src/jar/**/*\"],\r\n :rootdir => \"src/jar\",\r\n :extension => \".jar\"\r\n---\r\n\r\nThis generator creates a task called \"build/helloworld.jar\" that creates exactly that archive, containing all the files from @src/jar@. \"@**/*@\" tells rant to recursively add all files. The @rootdir@ parameter is necessary so that the generator knows where to start adding files. Without it, the created JAR will have the \"@src/jar@\" folders inside it, which is undesirable.\r\n\r\nI draw your attention to the @archive_rootdir_fix@ file that is being required. Support for the @rootdir@ parameter is currently not in Rant. I\'ve submitted a patch, but until it is accepted, you need this particular file. It is included in the \"example source code\":source for you convenience.\r\n\r\nThe generated task name is quite cumbersome, but it is quite trivial to create an alias to it using a blank task with a sole dependency. But what happens when we change our extension name or build directory? We also have to recode our alias task. Thankfully, the generator returns an object with information about the generated task, so that we can use it later in our Rantfile:\r\n--- ruby\r\nimport \"archive/zip\"\r\n\r\njar_t = gen Archive::Zip, \"build/helloworld\", \r\n :files => sys[\"src/jar/**/*\"],\r\n :rootdir => \"src/jar\",\r\n :extension => \".jar\"\r\n\r\ntask :build_jar => jar_t.path\r\n---\r\n\r\nh3(#cleaning). Cleaning\r\n\r\nBefore we proceed, let us quickly set up our clean and clobber tasks, as they are required for the next section. Rant makes this trivially easy, so I\'m just going to show you some code and move on.\r\n\r\n--- ruby\r\nimport \"clean\"\r\n\r\ngen Clean, :clean\r\nvar[:clean] << \"build\"\r\n\r\ngen Clean, :clobber\r\nvar[:clobber] << \"build\"\r\nvar[:clobber] << \"bin\"\r\n---\r\n\r\nh3(#xpi). Making the XPI\r\n\r\nAs you can imagine, the next step - packaging up the XPI file - is more of the same. A small amount of trickery is required to get the JAR file into the chrome directory - we actually move files around and prepare the XPI file in the build directory, so that our zip task only has to zip the single directory. You can do this using methods of the @sys@ object. Since it uses standard shell commands it is fairly self explanatory, as you\'ll see in the following example. See that we can keep using the @jar_t@ object through out build file.\r\n\r\n--- ruby\r\nxpitask = gen Archive::Zip, \"bin/helloworld\",\r\n :version => \"1.0.0\",\r\n :files => sys[\"build/**/*\"],\r\n :rootdir => \"build\",\r\n :extension => \".xpi\"\r\ntask :build_xpi => xpitask.path \r\n\r\ntask :prepare => [:build_jar] do |t|\r\n sys.mkdir_p \"build/chrome\"\r\n sys.mv jar_t.path, \"build/chrome/helloworld.jar\"\r\n sys.cp sys[\"src/install/**/*\"], \"build\"\r\nend\r\n\r\ntask :package => [:prepare, :build_xpi]\r\n---\r\n\r\nNote that we\'ve added a version parameter to the zip task - this automatically appends a version string to our output file. \r\n\r\nh3(#final). Final Touches\r\n\r\nNow we just need to add the finishing touches to our build file. For maintainability, we will extract common names (such as the \"helloworld\" title and the \"build\" directory) into variables, so that changing them once will change them throughout the entire buildfile. You can use normal ruby variables for this, but it is preferable to use the \"var\" construct since it means you have the option of using them in @Command@ generators later on (maybe I will cover it in another tutorial). It is more verbose, however, so you may choose not to use it in your own projects.\r\n\r\nFinally, we move our public tasks to the top of file for readability and give them descriptions so they are displayed when executing \"@rant -T@\". And there you have it folks, an automated build script for firefox extensions. Please \"download the source code\":source to peruse at your leisure.\r\n\r\nh3(#completed). The Completed Rantfile\r\n\r\n--- ruby\r\n# Rantfile for building Firefox Extension\r\n# Xavier Shay (xshay@rhnh.net), July 2006\r\n\r\nimport \"archive/zip\"\r\nrequire \"archive_rootdir_fix\"\r\nimport \"clean\"\r\n\r\n# Configuration\r\nvar :title => \"helloworld\"\r\nvar :version => \"1.0.0\"\r\nvar :build_dir => \"build\"\r\nvar :bin_dir => \"bin\"\r\nvar :src_dir => \"src\"\r\n\r\n# Primary tasks\r\ndesc \"Package up the XPI file for release\"\r\ntask :package => [:prepare, :build_xpi]\r\n\r\ndesc \"Cleanup temporary files\"\r\ngen Clean, :clean\r\nvar[:clean] << \"build\"\r\n\r\ndesc \"Cleanup all generated artifacts\"\r\ngen Clean, :clobber\r\nvar[:clobber] << \"build\"\r\nvar[:clobber] << \"bin\"\r\n\r\n# Support tasks\r\njar_t = gen Archive::Zip, \"#{var :build_dir}/#{var :title}\", \r\n :files => sys[\"#{var :src_dir}/jar/**/*\"],\r\n :rootdir => \"#{var :src_dir}/jar\",\r\n :extension => \".jar\"\r\ntask :build_jar => jar_t.path\r\n\r\nxpi_t = gen Archive::Zip, \"#{var :bin_dir}/#{var :title}\",\r\n :version => \"#{var :version}\",\r\n :files => sys[\"#{var :build_dir}/**/*\"],\r\n :rootdir => \"#{var :build_dir}\",\r\n :extension => \".xpi\"\r\ntask :build_xpi => xpi_t.path \r\n\r\ntask :prepare => [:clean, :build_jar] do |t|\r\n sys.mkdir_p \"#{var :build_dir}/chrome\"\r\n sys.mv jar_t.path, \"#{var :build_dir}/chrome/#{var :title}.jar\"\r\n sys.cp sys[\"#{var :src_dir}/install/**/*\"], \"#{var :build_dir}\"\r\nend\r\n---\r\n\r\n[source]http://rhnh.net/files/building-firefox-extensions.zip\r\n','toc. \n <ol>\n <li><a href=\"#intro\">Introduction</a></li>\n <li><a href=\"#basics\">Extension Basics</a></li>\n <li><a href=\"#rant\">Rant</a></li>\n <li><a href=\"#jar\">Making the <span class=\"caps\">JAR</span></a></li>\n <li><a href=\"#cleaning\">Cleaning</a></li>\n <li><a href=\"#xpi\">Making the <span class=\"caps\">XPI</span></a></li>\n <li><a href=\"#final\">Final Touches</a></li>\n <li><a href=\"#completed\">The Completed Rakefile</a></li>\n </ol>\n\n\n <p>anchor. intro</p>\n\n\n <p>This article will introduce the basics of <a href=\"http://make.rubyforge.org\">Ruby Rant</a> by creating a Rantfile to build <a href=\"http://www.mozilla.com/firefox/\">Firefox</a> extensions. You don&#8217;t actually need to know anything about extensions to follow along, but if you are interested may I recommend this <a href=\"http://roachfiend.com/archives/2004/12/08/how-to-create-firefox-extensions/\">tutorial by roachfiend</a>. You will note that that article (and many others on the same topic) use a batch file to build their extensions. While this is quick to set up for simple development, a build file saves time and effort in the long run, and gives more flexibility.</p>\n\n\n <p>I assume you at least know what Rant is &#8211; a replacement for Rake &#8211; and have it installed and working. Please visit their website for more information on this topic. This is also not a build file tutorial &#8211; you should know what a task and a dependency are.</p>\n\n\n <h3 id=\"basics\">Extension Basics</h3>\n\n\n <p>The first step is to decide on directory structure for your project. Firefox extensions are comprised of two main portions &#8211; the install instructions, and the actual content of the extension. A Firefox extension (an <span class=\"caps\">XPI</span> file) is really just a zip file with a different extension. You can open it up using your favourite archive manager and see the following structure:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">myextension.xpi/<tt>\n</tt> install.js<tt>\n</tt> install.rdf<tt>\n</tt> chrome/<tt>\n</tt> myextension.jar/<tt>\n</tt> ... myextension content ...<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Likewise, the <span class=\"caps\">JAR</span> file is also a zip file with an alternate extension. We can see that there are two major portions of the extension that need building, the <span class=\"caps\">JAR</span> and the <span class=\"caps\">XPI</span> (which contains the <span class=\"caps\">JAR</span>). As such, we will use a source structure that looks like this (<a href=\"http://rhnh.net/files/building-firefox-extensions.zip\">download the source code</a>):</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">myextension/<tt>\n</tt> Rantfile<tt>\n</tt> src/<tt>\n</tt> install/<tt>\n</tt> jar/<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Clearly, the install folder will only contain our <code>install.js</code> and <code>install.rdf</code> files, and the jar folder will contain the contents of our jar.</p>\n\n\n <h3 id=\"rant\">Rant</h3>\n\n\n <p>Enough introduction, let&#8217;s get started with Rant. Rant is a replacement for Rake. I won&#8217;t go into detail here, but one of the advantages for our purposes is portable zip creation without the need for external libraries. Rant is similar to Rake in that you define all your build tasks in a file in your root directory &#8211; the Rantfile. We will create 3 tasks &#8211; package, clean, and clobber. The first obviously packages up our extension into a zip file and gives it a <code>.xpi</code> extension. &#8220;clean&#8221; removes temporary files used to package the extension, and &#8220;clobber&#8221; removes all generated artefacts (basically the same as clean but also removes the <span class=\"caps\">XPI</span> file).</p>\n\n\n <h3 id=\"jar\">Making the <span class=\"caps\">JAR</span></h3>\n\n\n <p>Baby steps steps though &#8211; first of all we want to create the <span class=\"caps\">JAR</span> file for our extension. We can do this using the Archive::Zip generator provided by Rant:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">import <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">archive/zip</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>require <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">archive_rootdir_fix</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt>gen <span class=\"co\">Archive</span>::<span class=\"co\">Zip</span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build/helloworld</span><span class=\"dl\">&quot;</span></span>, <tt>\n</tt> <span class=\"sy\">:files</span> =&gt; sys[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">src/jar/**/*</span><span class=\"dl\">&quot;</span></span>],<tt>\n</tt> <span class=\"sy\">:rootdir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">src/jar</span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:extension</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">.jar</span><span class=\"dl\">&quot;</span></span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>This generator creates a task called &#8220;build/helloworld.jar&#8221; that creates exactly that archive, containing all the files from <code>src/jar</code>. &#8220;<code>**/*</code>&#8221; tells rant to recursively add all files. The <code>rootdir</code> parameter is necessary so that the generator knows where to start adding files. Without it, the created <span class=\"caps\">JAR</span> will have the &#8220;<code>src/jar</code>&#8221; folders inside it, which is undesirable.</p>\n\n\n <p>I draw your attention to the <code>archive_rootdir_fix</code> file that is being required. Support for the <code>rootdir</code> parameter is currently not in Rant. I&#8217;ve submitted a patch, but until it is accepted, you need this particular file. It is included in the <a href=\"http://rhnh.net/files/building-firefox-extensions.zip\">example source code</a> for you convenience.</p>\n\n\nThe generated task name is quite cumbersome, but it is quite trivial to create an alias to it using a blank task with a sole dependency. But what happens when we change our extension name or build directory? We also have to recode our alias task. Thankfully, the generator returns an object with information about the generated task, so that we can use it later in our Rantfile:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">import <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">archive/zip</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt>jar_t = gen <span class=\"co\">Archive</span>::<span class=\"co\">Zip</span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build/helloworld</span><span class=\"dl\">&quot;</span></span>, <tt>\n</tt> <span class=\"sy\">:files</span> =&gt; sys[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">src/jar/**/*</span><span class=\"dl\">&quot;</span></span>],<tt>\n</tt> <span class=\"sy\">:rootdir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">src/jar</span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:extension</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">.jar</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt>task <span class=\"sy\">:build_jar</span> =&gt; jar_t.path<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"cleaning\">Cleaning</h3>\n\n\n <p>Before we proceed, let us quickly set up our clean and clobber tasks, as they are required for the next section. Rant makes this trivially easy, so I&#8217;m just going to show you some code and move on.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">import <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">clean</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt>gen <span class=\"co\">Clean</span>, <span class=\"sy\">:clean</span><tt>\n</tt>var[<span class=\"sy\">:clean</span>] &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt>gen <span class=\"co\">Clean</span>, <span class=\"sy\">:clobber</span><tt>\n</tt>var[<span class=\"sy\">:clobber</span>] &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>var[<span class=\"sy\">:clobber</span>] &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">bin</span><span class=\"dl\">&quot;</span></span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"xpi\">Making the <span class=\"caps\">XPI</span></h3>\n\n\n <p>As you can imagine, the next step &#8211; packaging up the <span class=\"caps\">XPI</span> file &#8211; is more of the same. A small amount of trickery is required to get the <span class=\"caps\">JAR</span> file into the chrome directory &#8211; we actually move files around and prepare the <span class=\"caps\">XPI</span> file in the build directory, so that our zip task only has to zip the single directory. You can do this using methods of the <code>sys</code> object. Since it uses standard shell commands it is fairly self explanatory, as you&#8217;ll see in the following example. See that we can keep using the <code>jar_t</code> object through out build file.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">xpitask = gen <span class=\"co\">Archive</span>::<span class=\"co\">Zip</span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">bin/helloworld</span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:version</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">1.0.0</span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:files</span> =&gt; sys[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build/**/*</span><span class=\"dl\">&quot;</span></span>],<tt>\n</tt> <span class=\"sy\">:rootdir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build</span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:extension</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">.xpi</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>task <span class=\"sy\">:build_xpi</span> =&gt; xpitask.path <tt>\n</tt><tt>\n</tt>task <span class=\"sy\">:prepare</span> =&gt; [<span class=\"sy\">:build_jar</span>] <span class=\"r\">do</span> |t|<tt>\n</tt> sys.mkdir_p <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build/chrome</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> sys.mv jar_t.path, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build/chrome/helloworld.jar</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> sys.cp sys[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">src/install/**/*</span><span class=\"dl\">&quot;</span></span>], <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>task <span class=\"sy\">:package</span> =&gt; [<span class=\"sy\">:prepare</span>, <span class=\"sy\">:build_xpi</span>]<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Note that we&#8217;ve added a version parameter to the zip task &#8211; this automatically appends a version string to our output file.</p>\n\n\n <h3 id=\"final\">Final Touches</h3>\n\n\n <p>Now we just need to add the finishing touches to our build file. For maintainability, we will extract common names (such as the &#8220;helloworld&#8221; title and the &#8220;build&#8221; directory) into variables, so that changing them once will change them throughout the entire buildfile. You can use normal ruby variables for this, but it is preferable to use the &#8220;var&#8221; construct since it means you have the option of using them in <code>Command</code> generators later on (maybe I will cover it in another tutorial). It is more verbose, however, so you may choose not to use it in your own projects.</p>\n\n\n <p>Finally, we move our public tasks to the top of file for readability and give them descriptions so they are displayed when executing &#8220;<code>rant -T</code>&#8221;. And there you have it folks, an automated build script for firefox extensions. Please <a href=\"http://rhnh.net/files/building-firefox-extensions.zip\">download the source code</a> to peruse at your leisure.</p>\n\n\n <h3 id=\"completed\">The Completed Rantfile</h3>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt>31<tt>\n</tt>32<tt>\n</tt>33<tt>\n</tt>34<tt>\n</tt>35<tt>\n</tt>36<tt>\n</tt>37<tt>\n</tt>38<tt>\n</tt>39<tt>\n</tt>40<tt>\n</tt>41<tt>\n</tt>42<tt>\n</tt>43<tt>\n</tt>44<tt>\n</tt>45<tt>\n</tt>46<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\"># Rantfile for building Firefox Extension</span><tt>\n</tt><span class=\"c\"># Xavier Shay (xshay@rhnh.net), July 2006</span><tt>\n</tt><tt>\n</tt>import <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">archive/zip</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>require <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">archive_rootdir_fix</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>import <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">clean</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt><span class=\"c\"># Configuration</span><tt>\n</tt>var <span class=\"sy\">:title</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">helloworld</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>var <span class=\"sy\">:version</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">1.0.0</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>var <span class=\"sy\">:build_dir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>var <span class=\"sy\">:bin_dir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">bin</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>var <span class=\"sy\">:src_dir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">src</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt><span class=\"c\"># Primary tasks</span><tt>\n</tt>desc <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Package up the XPI file for release</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>task <span class=\"sy\">:package</span> =&gt; [<span class=\"sy\">:prepare</span>, <span class=\"sy\">:build_xpi</span>]<tt>\n</tt><tt>\n</tt>desc <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Cleanup temporary files</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>gen <span class=\"co\">Clean</span>, <span class=\"sy\">:clean</span><tt>\n</tt>var[<span class=\"sy\">:clean</span>] &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt>desc <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Cleanup all generated artifacts</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>gen <span class=\"co\">Clean</span>, <span class=\"sy\">:clobber</span><tt>\n</tt>var[<span class=\"sy\">:clobber</span>] &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">build</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>var[<span class=\"sy\">:clobber</span>] &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">bin</span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt><span class=\"c\"># Support tasks</span><tt>\n</tt>jar_t = gen <span class=\"co\">Archive</span>::<span class=\"co\">Zip</span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:build_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:title</span><span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span>, <tt>\n</tt> <span class=\"sy\">:files</span> =&gt; sys[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:src_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/jar/**/*</span><span class=\"dl\">&quot;</span></span>],<tt>\n</tt> <span class=\"sy\">:rootdir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:src_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/jar</span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:extension</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">.jar</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>task <span class=\"sy\">:build_jar</span> =&gt; jar_t.path<tt>\n</tt><tt>\n</tt>xpi_t = gen <span class=\"co\">Archive</span>::<span class=\"co\">Zip</span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:bin_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:title</span><span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:version</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:version</span><span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:files</span> =&gt; sys[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:build_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/**/*</span><span class=\"dl\">&quot;</span></span>],<tt>\n</tt> <span class=\"sy\">:rootdir</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:build_dir</span><span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span>,<tt>\n</tt> <span class=\"sy\">:extension</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">.xpi</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>task <span class=\"sy\">:build_xpi</span> =&gt; xpi_t.path <tt>\n</tt><tt>\n</tt>task <span class=\"sy\">:prepare</span> =&gt; [<span class=\"sy\">:clean</span>, <span class=\"sy\">:build_jar</span>] <span class=\"r\">do</span> |t|<tt>\n</tt> sys.mkdir_p <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:build_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/chrome</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> sys.mv jar_t.path, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:build_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/chrome/</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:title</span><span class=\"dl\">}</span></span><span class=\"k\">.jar</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> sys.cp sys[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:src_dir</span><span class=\"dl\">}</span></span><span class=\"k\">/install/**/*</span><span class=\"dl\">&quot;</span></span>], <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>var <span class=\"sy\">:build_dir</span><span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2006-07-23 11:02:00','2007-07-17 11:11:08','2007-07-17 11:41:21'),(122,'YAML Tutorial','yaml-tutorial','toc. \r\n# \"Basics\":#basics\r\n# \"Transience\":#transience\r\n# \"Conclusion\":#conclusion\r\n# \"Resources\":#resources\r\n# \"Bonus Material\":#bonus\r\n\r\nanchor. basics\r\n\r\nSo you\'ve got all these tasty ruby objects lying around in memory, and they\'re going to be lost when your program ends. Such a tragic end. What\'s a robot to do? Why, store them to disk in a language agnostic format, of course! Enter YAML, a language perfectly suited to the task, more so than it\'s heavier bretheren, XML. YAML support comes built in to the ruby language, and it couldn\'t be easier to use. Every object automagically gets a @to_yaml@ method that returns a string containing appropriate YAML markup when you include the right file.\r\n\r\n--- ruby\r\nrequire \'yaml\' # Assumed in future examples\r\n\r\nputs \"hello\".to_yaml\r\n---\r\n\r\nOf course this works for any object, using some of that oh-so-sweet reflection. @to_yaml@ recursively calls itself on all of your instance variables, and even knows how to handle complex structure like arrays and hashes. It even copes with cyclic references! How\'s _that_ for value?\r\n\r\n--- ruby\r\nclass Square\r\n def initialize width, height\r\n @width = width\r\n @height = height\r\n @bonus = [\'yo\', {:msg => \'YAML 4TW\'}]\r\n @me = self\r\n end\r\nend\r\n\r\nputs Square.new(2, 2).to_yaml\r\n---\r\n\r\nNow that you\'ve got a handy YAML string you can do whatever you like with it: write it to disk, store it in a database, email it to your cousin Benny. But Benny is going to spin out - how does he reproduce your shiny ruby objects? Thoughtfully, ruby makes it just about as easy to create an object from YAML markup - in other words to go the other way. The @YAML::load@ method takes either a string or an IO object and gives you back an object, ready to use. It\'s worth noting that the @initialize@ method is *not* called on the new object - a fact that will become pertinent later.\r\n\r\n--- ruby\r\nserialized = Square.new(2, 2).to_yaml\r\nnew_obj = YAML::load(serialized)\r\nputs new_obj.width\r\n---\r\n\r\nh3(#transience). Transience\r\n\r\nThe YAML serializer works in essentially the same manner as a sledgehammer. There\'s no finesse - it will serialize *all* of your instance variables. Always. This is generally not a problem, but every now and then for reasons of space, security, beauty or public health you will have a transient variable that you really just don\'t want to be serialized. There is no neat way in the supplied library to do this. You could override @to_yaml@ and blank out the transient fields before you call @super@, but then you need to restore them afterwards. And what if those fields were calculated on initialization - how do you restore them when the object is deserialized?\r\n\r\nNot to worry, our gallant hero (yours truly) has created a \"helper script\":/projects/yaml+helper that allows you to specify which fields are to be persisted in a declarative manner using a class attribute.\r\n\r\n--- ruby\r\nrequire \'rhnh/yaml_helper\' # Assumed in future examples\r\n\r\nclass Square\r\n persistent :width, :height\r\n \r\n def initialize width, height\r\n @width = width\r\n @height = height\r\n @me = self # @me will not be serialized\r\n end\r\nend\r\n---\r\n\r\nThe script also provides a @post_deserialize@ hook that is called, not surprisingly, after deserialization. It essentially acts as @initialize@ for deserialized objects. No setup is necessary to use this hook, it\'s mere presence will attract enough attention.\r\n\r\n--- ruby\r\nclass OnTheBall\r\n def post_deserialize\r\n puts \"I\'m awake!\"\r\n end\r\nend\r\n\r\nYAML::load(OnTheBall.new.to_yaml)\r\n---\r\n\r\nh3(#conclusion). In closing\r\n\r\nYAML is an excellent choice for serializing your Ruby objects. Its brevity and readability give it the edge over both XML and Marshal, and with the addition of \"YAML Helper\":/projects/yaml+helper it becomes more flexible as well.\r\n\r\nh3(#resources). Resources\r\n\r\n* \"YAML\":http://www.yaml.org/\r\n* \"yaml4r\":http://yaml4r.sourceforge.net/\r\n\r\n','toc. \n <ol>\n <li><a href=\"#basics\">Basics</a></li>\n <li><a href=\"#transience\">Transience</a></li>\n <li><a href=\"#conclusion\">Conclusion</a></li>\n <li><a href=\"#resources\">Resources</a></li>\n <li><a href=\"#bonus\">Bonus Material</a></li>\n </ol>\n\n\n <p>anchor. basics</p>\n\n\n <p>So you&#8217;ve got all these tasty ruby objects lying around in memory, and they&#8217;re going to be lost when your program ends. Such a tragic end. What&#8217;s a robot to do? Why, store them to disk in a language agnostic format, of course! Enter <span class=\"caps\">YAML</span>, a language perfectly suited to the task, more so than it&#8217;s heavier bretheren, <span class=\"caps\">XML</span>. YAML support comes built in to the ruby language, and it couldn&#8217;t be easier to use. Every object automagically gets a <code>to_yaml</code> method that returns a string containing appropriate <span class=\"caps\">YAML</span> markup when you include the right file.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">yaml</span><span class=\"dl\">\'</span></span> <span class=\"c\"># Assumed in future examples</span><tt>\n</tt><tt>\n</tt>puts <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">hello</span><span class=\"dl\">&quot;</span></span>.to_yaml<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Of course this works for any object, using some of that oh-so-sweet reflection. <code>to_yaml</code> recursively calls itself on all of your instance variables, and even knows how to handle complex structure like arrays and hashes. It even copes with cyclic references! How&#8217;s <em>that</em> for value?</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">Square</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">initialize</span> width, height<tt>\n</tt> <span class=\"iv\">@width</span> = width<tt>\n</tt> <span class=\"iv\">@height</span> = height<tt>\n</tt> <span class=\"iv\">@bonus</span> = [<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">yo</span><span class=\"dl\">\'</span></span>, {<span class=\"sy\">:msg</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">YAML 4TW</span><span class=\"dl\">\'</span></span>}]<tt>\n</tt> <span class=\"iv\">@me</span> = <span class=\"pc\">self</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>puts <span class=\"co\">Square</span>.new(<span class=\"i\">2</span>, <span class=\"i\">2</span>).to_yaml<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Now that you&#8217;ve got a handy <span class=\"caps\">YAML</span> string you can do whatever you like with it: write it to disk, store it in a database, email it to your cousin Benny. But Benny is going to spin out &#8211; how does he reproduce your shiny ruby objects? Thoughtfully, ruby makes it just about as easy to create an object from <span class=\"caps\">YAML</span> markup &#8211; in other words to go the other way. The <code>YAML::load</code> method takes either a string or an IO object and gives you back an object, ready to use. It&#8217;s worth noting that the <code>initialize</code> method is <strong>not</strong> called on the new object &#8211; a fact that will become pertinent later.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">serialized = <span class=\"co\">Square</span>.new(<span class=\"i\">2</span>, <span class=\"i\">2</span>).to_yaml<tt>\n</tt>new_obj = <span class=\"co\">YAML</span>::load(serialized)<tt>\n</tt>puts new_obj.width<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"transience\">Transience</h3>\n\n\n <p>The <span class=\"caps\">YAML</span> serializer works in essentially the same manner as a sledgehammer. There&#8217;s no finesse &#8211; it will serialize <strong>all</strong> of your instance variables. Always. This is generally not a problem, but every now and then for reasons of space, security, beauty or public health you will have a transient variable that you really just don&#8217;t want to be serialized. There is no neat way in the supplied library to do this. You could override <code>to_yaml</code> and blank out the transient fields before you call <code>super</code>, but then you need to restore them afterwards. And what if those fields were calculated on initialization &#8211; how do you restore them when the object is deserialized?</p>\n\n\n <p>Not to worry, our gallant hero (yours truly) has created a <a href=\"/projects/yaml+helper\">helper script</a> that allows you to specify which fields are to be persisted in a declarative manner using a class attribute.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">rhnh/yaml_helper</span><span class=\"dl\">\'</span></span> <span class=\"c\"># Assumed in future examples</span><tt>\n</tt><tt>\n</tt><span class=\"r\">class</span> <span class=\"cl\">Square</span><tt>\n</tt> persistent <span class=\"sy\">:width</span>, <span class=\"sy\">:height</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">initialize</span> width, height<tt>\n</tt> <span class=\"iv\">@width</span> = width<tt>\n</tt> <span class=\"iv\">@height</span> = height<tt>\n</tt> <span class=\"iv\">@me</span> = <span class=\"pc\">self</span> <span class=\"c\"># @me will not be serialized</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>The script also provides a <code>post_deserialize</code> hook that is called, not surprisingly, after deserialization. It essentially acts as <code>initialize</code> for deserialized objects. No setup is necessary to use this hook, it&#8217;s mere presence will attract enough attention.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">OnTheBall</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">post_deserialize</span><tt>\n</tt> puts <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">I\'m awake!</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt><span class=\"co\">YAML</span>::load(<span class=\"co\">OnTheBall</span>.new.to_yaml)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"conclusion\">In closing</h3>\n\n\n <p><span class=\"caps\">YAML</span> is an excellent choice for serializing your Ruby objects. Its brevity and readability give it the edge over both <span class=\"caps\">XML</span> and Marshal, and with the addition of <a href=\"/projects/yaml+helper\"><span class=\"caps\">YAML</span> Helper</a> it becomes more flexible as well.</p>\n\n\n <h3 id=\"resources\">Resources</h3>\n\n\n <ul>\n <li><a href=\"http://www.yaml.org/\"><span class=\"caps\">YAML</span></a></li>\n <li><a href=\"http://yaml4r.sourceforge.net/\">yaml4r</a></li>\n </ul>',1,0,NULL,'2006-06-25 11:15:00','2007-07-17 11:16:28','2007-07-17 11:22:47'),(123,'Straight Sailing with Magellan','straight-sailing-with-magellan','toc. \r\n# \"Introduction\":#intro\r\n# \"Using Magellan\":#using\r\n## \"Dynamic Links\":#dynamic_links\r\n## \"State\":#state\r\n## \"Testing\":#testing\r\n# \"Extra Morsels\":#extra\r\n# \"Conclusion\":#conclusion\r\n# \"Footnotes\":#footnotes\r\n# \"Bonus Material\":#bonus\r\n\r\nanchor. intro\r\n\r\n\"Magellan\":/projects/magellan is a Ruby on Rails plugin that provides a framework for abstracting navigation logic out of your views and controllers, allowing you to write neater, more reusable code.\r\n\r\nh3. Why should I use Magellan?\r\n\r\nThe short answer is you probably shouldn\'t. Sorry, thanks for stopping by, please visit the gift shop. To elaborate, many applications don\'t actually have complex navigational requirements. They are more generally of the type \"go from page A to page B, then from there to page C\", and that\'s that. While of course Magellan can neatly express these relationships, it adds a layer of complexity to your application for questionable benefit.\r\n\r\nWhere Magellan excels is in expressing more complex requirements: \"go from page A to page B, unless it\'s a Thursday, in which case go to page C. If we got to page C from page A, then go to page B, otherwise go to page A\". Urgh. Where do you put this logic in a traditional rails app? You don\'t want this kind of logic in your views, and if you put it in your controllers you\'ll end up duplicating code. You need a better solution. \r\n\r\n_You need Magellan._\r\n\r\nh3(#using). Using Magellan\r\n\r\nTo use Magellan you need to understand three concepts:\r\n# Pages\r\n# Links\r\n# State\r\n\r\nState is a more advanced topic, so we\'ll go over that bit later on. You covered the first two in Web Coding 101, so I\'ll go over them first. The only difference in Magellan\'s usage of the terms \"page\" and \"links\" is a level of abstraction. Simply, a Magellan page represents a URL (rails or otherwise). Drop the following code into your @environment.rb@:\r\n\r\n--- ruby\r\nRHNH::Magellan::Navigator.draw do |map|\r\n map.add_page :home, {:controller => \'home\', :action => \'list\'}\r\nend\r\n---\r\n\r\nEasy. To link to this page in a view, we use the @nav_link_to@ helper in our @.rhtml@ file instead of @link_to@. The first parameter is the name of the page we are currently on - in this case it is not strictly required and could be set to @nil@.\r\n\r\n--- ruby\r\nnav_link_to :current_page, :home\r\n---\r\n\r\nThat in of itself isn\'t particularly exciting. Where things get tasty is when we start using links. Now, in basic usage a link acts the same way as a page[1]. We can create a next link that is different depending on which page you are on.\r\n\r\n--- ruby\r\nRHNH::Magellan::Navigator.draw do |map|\r\n map.add_page :home1 do |p|\r\n p.url = { :controller => \'home1\' }\r\n p.add_link :next, :home2\r\n end\r\n \r\n map.add_page :home2 do |p|\r\n p.url = { controller => \'home2\' }\r\n p.add_link :next, :home1\r\n end\r\nend\r\n\r\n# Then in both home1.rhtml and home2.rhtml\r\n# @current_page is either :home1 or :home2\r\nnav_link_to @current_page, :next\r\n---\r\n\r\nAs you can see we have de-coupled our navigation from the page itself. If we wanted to we could change the next link for home2 to home3 without having to change any of the code associated with home2. This makes our pages more modular and reusable, which is generally a Good Thing.\r\n\r\nh3(#dynamic_links). Dynamic Links\r\n\r\nLet\'s go back to our original example. I want the next link on page A to go to page B except on Thursdays, where it should go page C. The trick here is that in addition to just accepting a symbol for the link name (a \"static link\"), it can also accept a @lambda@ block that is evaluated at runtime. This is a little bit more convoluted, the block needs to return not a link name, but the actual page we want to go to. While initially slightly unintuitive, it allows for more flexibility and less code than having to specify extra links.\r\n\r\n--- ruby\r\nRHNH::Magellan::Navigator.draw do |map|\r\n map.add_page :page_a do |p|\r\n p.add_link :back, lambda {|pages, state|\r\n # Thursday is the 4th day the of week\r\n Time.new.wday == 4 ? pages[:page_b] : pages[:page_c]\r\n }\r\n end\r\n\r\n map.add_page :page_b, { :controller => \'page_b\' }\r\n map.add_page :page_c, { :controller => \'page_c\' }\r\nend\r\n---\r\n\r\nh3(#state). State\r\n\r\nState is just like session storage for your navigation logic. In fact, it actually uses a subset of session storage[2]. The reason we differentiate it from normal session variables is simply to keep a neat separation between our navigation logic and other modules that may require the session. In typical usage, you modify the state in your controller (using @set_nav_state@, and then make a decision based on that state in your navigation logic (using the @state@ parameter). A simple example is to have a dynamic back link depending on the previous page.\r\n\r\n--- ruby\r\n# Both page A and B have a link to page C\r\ndef page_a; set_nav_state :back_page => \'page_a\'; end;\r\ndef page_b; set_nav_state :back_page => \'page_b\'; end;\r\n\r\n# Page C\r\nnav_link_to \'Back\', :page_c, :back\r\n\r\n# environment.rb\r\nRHNH::Magellan::Navigator.draw do |map|\r\n map.add_page :page_a, { :controller => \'page_b\' }\r\n map.add_page :page_b, { :controller => \'page_c\' }\r\n\r\n map.add_page :page_c, { do |p|\r\n p.add_link :back, lambda {|pages, state|\r\n pages[state[:back_page]]\r\n }\r\n end\r\nend\r\n---\r\n\r\nh3(#testing). Testing your navigation\r\n\r\nAs with any code, it is important to test your navigation logic. There are many ways to do this, depending on the requirements and complexity of your application. I recommend at least one class of unit tests for your logic, and also to add code to your functional tests to ensure your controllers are setting the correct state. Magellan provides one helper function here - @nav_state@ - which returns a hash of the current state.\r\n\r\n--- ruby\r\nclass UnitTest < Test::Unit::TestCase\r\n def setup\r\n @nav = RHNH::Magellan::Navigator.instance\r\n end\r\n \r\n def test_back_link\r\n state = { :homepage => :home1 }\r\n expected = { :controller => \'example\', :action => \'home1\' }\r\n \r\n assert_equal expected, @nav.get_url(:page1, :back, state)\r\n end\r\nend\r\n---\r\n\r\n--- ruby\r\nclass FunctionalTest < Test::Unit::TestCase\r\n # Standard functional test setup code...\r\n \r\n def test_index\r\n get \'index\'\r\n \r\n assert_equal :home1, nav_state[:homepage]\r\n end\r\nend\r\n---\r\n\r\nThe tests included with the example that comes with Magellan provide a more complex example of navigation testing. I highly recommend you look over them.\r\n\r\nh3(#extra). Extra morsels\r\n\r\nYou can specify a default link by adding a link to the map rather than a page. For instance, to specify a default @:back@ link:\r\n\r\n--- ruby\r\nRHNH::Magellan::Navigator.draw do |map|\r\n map.add_page :home, { controller => \'home\' }\r\n map.add_link :back, :home\r\nend\r\n---\r\n\r\nTo be extra fancy, you can return extra parameters from your navigation logic that are added to the @:params@ hash of the url. This is done by returning an array with both the page and the parameters in it.\r\n\r\n--- ruby\r\nRHNH::Magellan::Navigator.draw do |map|\r\n map.add_page :home, { controller => \'home\' }\r\n map.add_link :back, lambda { |pages, state|\r\n [pages[:home], {:message => \'You just hit a default link\'}]\r\n }\r\nend\r\n---\r\n\r\nh3(#conclusion). To conclude\r\n\r\nMagellan is a great way of managing the complexity of larger projects. By abstracting navigation logic out of your controllers and views you make your project much more modular and reusable. It can even be introduced incrementally - all your old @link_to@ calls will still work.\r\n\r\nh3(#footnotes). Footnotes\r\n\r\nfn1. To be technically correct, a page acts like a link. Magellan creates default links to pages with the same name as the page. For instance, unless you specify otherwise, @:home@ is actually a link to the page @:home@\r\n\r\nfn2. Magellan uses @session[:rhnh_navigator_state]@, so you may want to steer clear of that to avoid stepping on anyone\'s toes.\r\n','toc. \n <ol>\n <li><a href=\"#intro\">Introduction</a></li>\n <li><a href=\"#using\">Using Magellan</a>\n <ol>\n <li><a href=\"#dynamic_links\">Dynamic Links</a></li>\n <li><a href=\"#state\">State</a></li>\n <li><a href=\"#testing\">Testing</a></li>\n </ol>\n </li>\n <li><a href=\"#extra\">Extra Morsels</a></li>\n <li><a href=\"#conclusion\">Conclusion</a></li>\n <li><a href=\"#footnotes\">Footnotes</a></li>\n <li><a href=\"#bonus\">Bonus Material</a></li>\n </ol>\n\n\n <p>anchor. intro</p>\n\n\n <p><a href=\"/projects/magellan\">Magellan</a> is a Ruby on Rails plugin that provides a framework for abstracting navigation logic out of your views and controllers, allowing you to write neater, more reusable code.</p>\n\n\n <h3>Why should I use Magellan?</h3>\n\n\n <p>The short answer is you probably shouldn&#8217;t. Sorry, thanks for stopping by, please visit the gift shop. To elaborate, many applications don&#8217;t actually have complex navigational requirements. They are more generally of the type &#8220;go from page A to page B, then from there to page C&#8221;, and that&#8217;s that. While of course Magellan can neatly express these relationships, it adds a layer of complexity to your application for questionable benefit.</p>\n\n\n <p>Where Magellan excels is in expressing more complex requirements: &#8220;go from page A to page B, unless it&#8217;s a Thursday, in which case go to page C. If we got to page C from page A, then go to page B, otherwise go to page A&#8221;. Urgh. Where do you put this logic in a traditional rails app? You don&#8217;t want this kind of logic in your views, and if you put it in your controllers you&#8217;ll end up duplicating code. You need a better solution.</p>\n\n\n <p><em>You need Magellan.</em></p>\n\n\n <h3 id=\"using\">Using Magellan</h3>\n\n\nTo use Magellan you need to understand three concepts:\n <ol>\n <li>Pages</li>\n <li>Links</li>\n <li>State</li>\n </ol>\n\n\n <p>State is a more advanced topic, so we&#8217;ll go over that bit later on. You covered the first two in Web Coding 101, so I&#8217;ll go over them first. The only difference in Magellan&#8217;s usage of the terms &#8220;page&#8221; and &#8220;links&#8221; is a level of abstraction. Simply, a Magellan page represents a <span class=\"caps\">URL</span> (rails or otherwise). Drop the following code into your <code>environment.rb</code>:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"co\">RHNH</span>::<span class=\"co\">Magellan</span>::<span class=\"co\">Navigator</span>.draw <span class=\"r\">do</span> |map|<tt>\n</tt> map.add_page <span class=\"sy\">:home</span>, {<span class=\"sy\">:controller</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">home</span><span class=\"dl\">\'</span></span>, <span class=\"sy\">:action</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">list</span><span class=\"dl\">\'</span></span>}<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Easy. To link to this page in a view, we use the <code>nav_link_to</code> helper in our <code>.rhtml</code> file instead of <code>link_to</code>. The first parameter is the name of the page we are currently on &#8211; in this case it is not strictly required and could be set to <code>nil</code>.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">nav_link_to <span class=\"sy\">:current_page</span>, <span class=\"sy\">:home</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>That in of itself isn&#8217;t particularly exciting. Where things get tasty is when we start using links. Now, in basic usage a link acts the same way as a page<sup><a href=\"#fn1\">1</a></sup>. We can create a next link that is different depending on which page you are on.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"co\">RHNH</span>::<span class=\"co\">Magellan</span>::<span class=\"co\">Navigator</span>.draw <span class=\"r\">do</span> |map|<tt>\n</tt> map.add_page <span class=\"sy\">:home1</span> <span class=\"r\">do</span> |p|<tt>\n</tt> p.url = { <span class=\"sy\">:controller</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">home1</span><span class=\"dl\">\'</span></span> }<tt>\n</tt> p.add_link <span class=\"sy\">:next</span>, <span class=\"sy\">:home2</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <tt>\n</tt> map.add_page <span class=\"sy\">:home2</span> <span class=\"r\">do</span> |p|<tt>\n</tt> p.url = { controller =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">home2</span><span class=\"dl\">\'</span></span> }<tt>\n</tt> p.add_link <span class=\"sy\">:next</span>, <span class=\"sy\">:home1</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt><span class=\"c\"># Then in both home1.rhtml and home2.rhtml</span><tt>\n</tt><span class=\"c\"># @current_page is either :home1 or :home2</span><tt>\n</tt>nav_link_to <span class=\"iv\">@current_page</span>, <span class=\"sy\">:next</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>As you can see we have de-coupled our navigation from the page itself. If we wanted to we could change the next link for home2 to home3 without having to change any of the code associated with home2. This makes our pages more modular and reusable, which is generally a Good Thing.</p>\n\n\n <h3 id=\"dynamic_links\">Dynamic Links</h3>\n\n\n <p>Let&#8217;s go back to our original example. I want the next link on page A to go to page B except on Thursdays, where it should go page C. The trick here is that in addition to just accepting a symbol for the link name (a &#8220;static link&#8221;), it can also accept a <code>lambda</code> block that is evaluated at runtime. This is a little bit more convoluted, the block needs to return not a link name, but the actual page we want to go to. While initially slightly unintuitive, it allows for more flexibility and less code than having to specify extra links.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"co\">RHNH</span>::<span class=\"co\">Magellan</span>::<span class=\"co\">Navigator</span>.draw <span class=\"r\">do</span> |map|<tt>\n</tt> map.add_page <span class=\"sy\">:page_a</span> <span class=\"r\">do</span> |p|<tt>\n</tt> p.add_link <span class=\"sy\">:back</span>, lambda {|pages, state|<tt>\n</tt> <span class=\"c\"># Thursday is the 4th day the of week</span><tt>\n</tt> <span class=\"co\">Time</span>.new.wday == <span class=\"i\">4</span> ? pages[<span class=\"sy\">:page_b</span>] : pages[<span class=\"sy\">:page_c</span>]<tt>\n</tt> }<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> map.add_page <span class=\"sy\">:page_b</span>, { <span class=\"sy\">:controller</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">page_b</span><span class=\"dl\">\'</span></span> }<tt>\n</tt> map.add_page <span class=\"sy\">:page_c</span>, { <span class=\"sy\">:controller</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">page_c</span><span class=\"dl\">\'</span></span> }<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"state\">State</h3>\n\n\n <p>State is just like session storage for your navigation logic. In fact, it actually uses a subset of session storage<sup><a href=\"#fn2\">2</a></sup>. The reason we differentiate it from normal session variables is simply to keep a neat separation between our navigation logic and other modules that may require the session. In typical usage, you modify the state in your controller (using <code>set_nav_state</code>, and then make a decision based on that state in your navigation logic (using the <code>state</code> parameter). A simple example is to have a dynamic back link depending on the previous page.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\"># Both page A and B have a link to page C</span><tt>\n</tt><span class=\"r\">def</span> <span class=\"fu\">page_a</span>; set_nav_state <span class=\"sy\">:back_page</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">page_a</span><span class=\"dl\">\'</span></span>; <span class=\"r\">end</span>;<tt>\n</tt><span class=\"r\">def</span> <span class=\"fu\">page_b</span>; set_nav_state <span class=\"sy\">:back_page</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">page_b</span><span class=\"dl\">\'</span></span>; <span class=\"r\">end</span>;<tt>\n</tt><tt>\n</tt><span class=\"c\"># Page C</span><tt>\n</tt>nav_link_to <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">Back</span><span class=\"dl\">\'</span></span>, <span class=\"sy\">:page_c</span>, <span class=\"sy\">:back</span><tt>\n</tt><tt>\n</tt><span class=\"c\"># environment.rb</span><tt>\n</tt><span class=\"co\">RHNH</span>::<span class=\"co\">Magellan</span>::<span class=\"co\">Navigator</span>.draw <span class=\"r\">do</span> |map|<tt>\n</tt> map.add_page <span class=\"sy\">:page_a</span>, { <span class=\"sy\">:controller</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">page_b</span><span class=\"dl\">\'</span></span> }<tt>\n</tt> map.add_page <span class=\"sy\">:page_b</span>, { <span class=\"sy\">:controller</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">page_c</span><span class=\"dl\">\'</span></span> }<tt>\n</tt><tt>\n</tt> map.add_page <span class=\"sy\">:page_c</span>, { <span class=\"r\">do</span> |p|<tt>\n</tt> p.add_link <span class=\"sy\">:back</span>, lambda {|pages, state|<tt>\n</tt> pages[state[<span class=\"sy\">:back_page</span>]]<tt>\n</tt> }<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"testing\">Testing your navigation</h3>\n\n\n <p>As with any code, it is important to test your navigation logic. There are many ways to do this, depending on the requirements and complexity of your application. I recommend at least one class of unit tests for your logic, and also to add code to your functional tests to ensure your controllers are setting the correct state. Magellan provides one helper function here &#8211; <code>nav_state</code> &#8211; which returns a hash of the current state.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">UnitTest</span> &lt; <span class=\"co\">Test</span>::<span class=\"co\">Unit</span>::<span class=\"co\">TestCase</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">setup</span><tt>\n</tt> <span class=\"iv\">@nav</span> = <span class=\"co\">RHNH</span>::<span class=\"co\">Magellan</span>::<span class=\"co\">Navigator</span>.instance<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">test_back_link</span><tt>\n</tt> state = { <span class=\"sy\">:homepage</span> =&gt; <span class=\"sy\">:home1</span> }<tt>\n</tt> expected = { <span class=\"sy\">:controller</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">example</span><span class=\"dl\">\'</span></span>, <span class=\"sy\">:action</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">home1</span><span class=\"dl\">\'</span></span> }<tt>\n</tt> <tt>\n</tt> assert_equal expected, <span class=\"iv\">@nav</span>.get_url(<span class=\"sy\">:page1</span>, <span class=\"sy\">:back</span>, state)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">FunctionalTest</span> &lt; <span class=\"co\">Test</span>::<span class=\"co\">Unit</span>::<span class=\"co\">TestCase</span><tt>\n</tt> <span class=\"c\"># Standard functional test setup code...</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">test_index</span><tt>\n</tt> get <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">index</span><span class=\"dl\">\'</span></span><tt>\n</tt> <tt>\n</tt> assert_equal <span class=\"sy\">:home1</span>, nav_state[<span class=\"sy\">:homepage</span>]<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>The tests included with the example that comes with Magellan provide a more complex example of navigation testing. I highly recommend you look over them.</p>\n\n\n <h3 id=\"extra\">Extra morsels</h3>\n\n\n <p>You can specify a default link by adding a link to the map rather than a page. For instance, to specify a default <code>:back</code> link:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"co\">RHNH</span>::<span class=\"co\">Magellan</span>::<span class=\"co\">Navigator</span>.draw <span class=\"r\">do</span> |map|<tt>\n</tt> map.add_page <span class=\"sy\">:home</span>, { controller =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">home</span><span class=\"dl\">\'</span></span> }<tt>\n</tt> map.add_link <span class=\"sy\">:back</span>, <span class=\"sy\">:home</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>To be extra fancy, you can return extra parameters from your navigation logic that are added to the <code>:params</code> hash of the url. This is done by returning an array with both the page and the parameters in it.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"co\">RHNH</span>::<span class=\"co\">Magellan</span>::<span class=\"co\">Navigator</span>.draw <span class=\"r\">do</span> |map|<tt>\n</tt> map.add_page <span class=\"sy\">:home</span>, { controller =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">home</span><span class=\"dl\">\'</span></span> }<tt>\n</tt> map.add_link <span class=\"sy\">:back</span>, lambda { |pages, state|<tt>\n</tt> [pages[<span class=\"sy\">:home</span>], {<span class=\"sy\">:message</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">You just hit a default link</span><span class=\"dl\">\'</span></span>}]<tt>\n</tt> }<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3 id=\"conclusion\">To conclude</h3>\n\n\n <p>Magellan is a great way of managing the complexity of larger projects. By abstracting navigation logic out of your controllers and views you make your project much more modular and reusable. It can even be introduced incrementally &#8211; all your old <code>link_to</code> calls will still work.</p>\n\n\n <h3 id=\"footnotes\">Footnotes</h3>\n\n\n <p id=\"fn1\"><sup>1</sup> To be technically correct, a page acts like a link. Magellan creates default links to pages with the same name as the page. For instance, unless you specify otherwise, <code>:home</code> is actually a link to the page <code>:home</code></p>\n\n\n <p id=\"fn2\"><sup>2</sup> Magellan uses <code>session[:rhnh_navigator_state]</code>, so you may want to steer clear of that to avoid stepping on anyone&#8217;s toes.</p>',1,0,NULL,'2006-06-24 11:23:00','2007-07-17 11:25:49','2007-10-27 02:10:02'),(124,'New Site','new-site','Hey look, RHNH is shiny and new! I\'ve folded in blog.rhnh.net, articles and the old rhnh and put it all into a mephisto install. I will be migrating across most of my \"byteclub blog\":http://www.byteclub.net/blog/xavier in the fullness of time. There\'s still a few loose ends that I\'m working on, but on the whole everything should be pretty OK.\r\n\r\nEDIT: Looks like commenting is broken ... not 100% sure why. I\'m going to go and blame FastCGI coz I don\'t know anything about it.\r\n\r\nEDIT2: Ultra props to <a href=\"http://grantbissett.com\">Grant</a> for setting me up with an account where I can run mongrel. If you\'re seeing this the DNS changes have propagated through and everything should be working A-OK.','<p>Hey look, <span class=\"caps\">RHNH</span> is shiny and new! I&#8217;ve folded in blog.rhnh.net, articles and the old rhnh and put it all into a mephisto install. I will be migrating across most of my <a href=\"http://www.byteclub.net/blog/xavier\">byteclub blog</a> in the fullness of time. There&#8217;s still a few loose ends that I&#8217;m working on, but on the whole everything should be pretty OK.</p>\n\n\n <p><span class=\"caps\">EDIT</span>: Looks like commenting is broken &#8230; not 100% sure why. I&#8217;m going to go and blame FastCGI coz I don&#8217;t know anything about it.</p>\n\n\n <p><span class=\"caps\">EDIT2</span>: Ultra props to <a href=\"http://grantbissett.com\">Grant</a> for setting me up with an account where I can run mongrel. If you&#8217;re seeing this the <span class=\"caps\">DNS</span> changes have propagated through and everything should be working A-OK.</p>',1,0,NULL,'2007-08-14 10:22:00','2007-08-14 10:25:59','2007-08-16 12:06:07'),(125,'This is stupid: Hash#select vs reject','this-is-stupid-hash-select-vs-reject','A little consistency would be nice...\r\n\r\n--- ruby\r\n{1=>1, 2=>2, 3=>3}.reject {|key, value| key != 1 } # => {1=>1}\r\n{1=>1, 2=>2, 3=>3}.select {|key, value| key == 1 } # => [[1, 1]]\r\n---','<p>A little consistency would be nice&#8230;</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">{<span class=\"i\">1</span>=&gt;<span class=\"i\">1</span>, <span class=\"i\">2</span>=&gt;<span class=\"i\">2</span>, <span class=\"i\">3</span>=&gt;<span class=\"i\">3</span>}.reject {|key, value| key != <span class=\"i\">1</span> } <span class=\"c\"># =&gt; {1=&gt;1}</span><tt>\n</tt>{<span class=\"i\">1</span>=&gt;<span class=\"i\">1</span>, <span class=\"i\">2</span>=&gt;<span class=\"i\">2</span>, <span class=\"i\">3</span>=&gt;<span class=\"i\">3</span>}.select {|key, value| key == <span class=\"i\">1</span> } <span class=\"c\"># =&gt; [[1, 1]]</span><tt>\n</tt></pre></td>\n</tr></table>',1,2,NULL,'2007-07-26 07:17:00','2007-08-15 07:18:41','2007-08-15 07:26:16'),(126,'Go','go','Rather than actually doing any of the work I was supposed to do tonight, I started teaching myself <a href=\"http://playgo.to/interactive/\">how to play \"Go\"</a>. This game is all very zen and is one of the few that computers haven\'t been able to master (top level programs can\'t even beat amateurs...). It appeals to me more than chess (which I actually quite like) - maybe because it\'s new and exciting.\r\n\r\nIt is also a really good way to make you feel really stupid. There is a go proverb (one of many): \"Lose your first 50 games as fast as possible\". So I let <a href=\"http://www.gnu.org/software/gnugo/\">gnugo </a> hammer me a few times and already it\'s almost midnight. I suspect I have a way to go before I start hitting the internet for a challenge, though I will most likely do so soon in search of guidance.','<p>Rather than actually doing any of the work I was supposed to do tonight, I started teaching myself <a href=\"http://playgo.to/interactive/\">how to play &#8220;Go&#8221;</a>. This game is all very zen and is one of the few that computers haven&#8217;t been able to master (top level programs can&#8217;t even beat amateurs&#8230;). It appeals to me more than chess (which I actually quite like) &#8211; maybe because it&#8217;s new and exciting.</p>\n\n\n <p>It is also a really good way to make you feel really stupid. There is a go proverb (one of many): &#8220;Lose your first 50 games as fast as possible&#8221;. So I let <a href=\"http://www.gnu.org/software/gnugo/\">gnugo </a> hammer me a few times and already it&#8217;s almost midnight. I suspect I have a way to go before I start hitting the internet for a challenge, though I will most likely do so soon in search of guidance.</p>',1,1,NULL,'2007-07-20 07:22:00','2007-08-15 07:22:46','2007-08-15 07:22:47'),(127,'Convert M4A to WAV in Ubuntu','convert-m4a-to-wav-in-ubuntu','--- bash\r\nmplayer -ao pcm:file=targetfile.wav sourcefile.m4a\r\n---','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">mplayer -ao pcm:file=targetfile.wav sourcefile.m4a<tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-07-23 07:24:00','2007-08-15 07:24:49','2007-08-15 07:25:29'),(128,'The Switch to VIM','the-switch-to-vim','I\'m been meaning to try out Vim for a while, especially since I now use two different platforms/editors for development (mac/textmate at work, linux/jedit at home). Finally got some time to try it out this weekend, and initial reports are positive! The thing that strikes me the most is how quickly you can navigate/select things without using the mouse. Vim\'s navigation shortcuts are like CTRL-(LEFT|RIGHT) on crack. Regex search forward and back, move by multiple lines, seek to next/prev char. I haven\'t internalized this navigation yet and already I\'m loving it. Give me some experience and I\'ll become an absolute machine. It\'s a bit weird because I\'m using colemak, so the \"stick to the home row\" mantra doesn\'t really apply, but overall it\'s still quite bearable. I need to figure out how to replicate the Apple-T shortcut in textmate (quick swith to file) and I think I\'ll be sold. I\'ll use it at work for the week and see how things go. For reference, I found the <a href=\"http://www.vi-improved.org/tutorial.php\">tutorial on the vi-improved site</a> to be quite helpful.','<p>I&#8217;m been meaning to try out Vim for a while, especially since I now use two different platforms/editors for development (mac/textmate at work, linux/jedit at home). Finally got some time to try it out this weekend, and initial reports are positive! The thing that strikes me the most is how quickly you can navigate/select things without using the mouse. Vim&#8217;s navigation shortcuts are like <span class=\"caps\">CTRL</span>-(LEFT|RIGHT) on crack. Regex search forward and back, move by multiple lines, seek to next/prev char. I haven&#8217;t internalized this navigation yet and already I&#8217;m loving it. Give me some experience and I&#8217;ll become an absolute machine. It&#8217;s a bit weird because I&#8217;m using colemak, so the &#8220;stick to the home row&#8221; mantra doesn&#8217;t really apply, but overall it&#8217;s still quite bearable. I need to figure out how to replicate the Apple-T shortcut in textmate (quick swith to file) and I think I&#8217;ll be sold. I&#8217;ll use it at work for the week and see how things go. For reference, I found the <a href=\"http://www.vi-improved.org/tutorial.php\">tutorial on the vi-improved site</a> to be quite helpful.</p>',1,2,NULL,'2007-07-08 07:26:00','2007-08-15 07:27:33','2007-08-15 07:28:06'),(129,'Practical Hpricot: SVG','practical-hpricot-svg','Inkscape does a pretty good job of creating plain SVG files, but they could be nicer. A particular file I was working on had many path elements, all with the same style attribute that I wanted to move into a parent tag (or external style or whatever). What better way to strip them out than Hpricot?\r\n\r\n--- ruby\r\nrequire \'hpricot\'\r\n\r\ndoc = open(ARGV[0]) { |f| Hpricot.XML(f) }\r\n\r\n(doc/:path).each do |path|\r\n [:id, :style].each do |attr| \r\n path.remove_attribute(attr)\r\n end\r\nend\r\n\r\nputs doc\r\n---\r\n\r\nAnd you get the benefit of prettier formatting!','<p>Inkscape does a pretty good job of creating plain <span class=\"caps\">SVG</span> files, but they could be nicer. A particular file I was working on had many path elements, all with the same style attribute that I wanted to move into a parent tag (or external style or whatever). What better way to strip them out than Hpricot?</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">hpricot</span><span class=\"dl\">\'</span></span><tt>\n</tt><tt>\n</tt>doc = open(<span class=\"pc\">ARGV</span>[<span class=\"i\">0</span>]) { |f| <span class=\"co\">Hpricot</span>.XML(f) }<tt>\n</tt><tt>\n</tt>(doc/<span class=\"sy\">:path</span>).each <span class=\"r\">do</span> |path|<tt>\n</tt> [<span class=\"sy\">:id</span>, <span class=\"sy\">:style</span>].each <span class=\"r\">do</span> |attr| <tt>\n</tt> path.remove_attribute(attr)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>puts doc<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>And you get the benefit of prettier formatting!</p>',1,0,NULL,'2007-07-01 07:29:00','2007-08-15 07:29:55','2007-08-15 07:29:57'),(133,'SwinBrain Article Suggestion','swinbrain-article-suggestion','I\'d like to see <a href=\"http://mercury.it.swin.edu.au/swinbrain/index.php/Game_Physics\">Game Physics</a> at least mention using <a href=\"http://en.wikipedia.org/wiki/Verlet_integration\">Verlet integration</a> as opposed to Euler or RK. I have personally found it much easier to use, especially when you get to dealing with collisions. It\'s much easier to keep your system stable. They also use it in industry, so I think it\'s a worthwhile addition (well, at least Hitman - there\'s a gamasutra article about it somewhere).\r\n\r\nYou see, I don\'t have an account to add this stuff myself...','<p>I&#8217;d like to see <a href=\"http://mercury.it.swin.edu.au/swinbrain/index.php/Game_Physics\">Game Physics</a> at least mention using <a href=\"http://en.wikipedia.org/wiki/Verlet_integration\">Verlet integration</a> as opposed to Euler or RK. I have personally found it much easier to use, especially when you get to dealing with collisions. It&#8217;s much easier to keep your system stable. They also use it in industry, so I think it&#8217;s a worthwhile addition (well, at least Hitman &#8211; there&#8217;s a gamasutra article about it somewhere).</p>\n\n\n <p>You see, I don&#8217;t have an account to add this stuff myself&#8230;</p>',1,0,NULL,'2007-06-18 11:39:00','2007-08-16 11:40:21','2007-08-16 11:41:28'),(137,'Byteclub reader','byteclub-reader','Finally got around to setting up a <a href=\"http://reader.rhnh.net/byteclub/\">planet for byteclub</a>. Only \"Robert\'s Comments\" was removed because it had heaps of spam comments (Robert: Let me know when this problem is rectified). Also, this is not currently synced in any way, so no new people will show up.','<p>Finally got around to setting up a <a href=\"http://reader.rhnh.net/byteclub/\">planet for byteclub</a>. Only &#8220;Robert&#8217;s Comments&#8221; was removed because it had heaps of spam comments (Robert: Let me know when this problem is rectified). Also, this is not currently synced in any way, so no new people will show up.</p>',1,2,NULL,'2007-06-07 11:50:00','2007-08-16 11:50:44','2007-08-16 11:50:44'),(140,'Practical Hpricot: XML to INI','practical-hpricot-xml-to-ini','--- ruby\r\nrequire \'hpricot\'\r\nrequire \'open-uri\'\r\n\r\ndef ini_entry(url, name)\r\n buffer = \"[#{url}]\\\\n\"\r\n buffer += \"name = #{name}\\\\n\"\r\n buffer += \"\\\\n\"\r\n buffer\r\nend\r\n\r\ndoc = Hpricot(open(\"http://www.byteclub.net/testsite/getFeeds.php\"))\r\n\r\n(doc/\"blog\").each do |elem|\r\n url = (elem/\"url\")\r\n name = (elem/\"name\")\r\n comments = (elem/\"comments\")\r\n \r\n if name.length > 0\r\n puts ini_entry(url.inner_text, name.inner_text) if url.inner_text.length > 0\r\n puts ini_entry(comments.inner_text, name.inner_text + \" Comments\") if comments.inner_text.length > 0\r\n end\r\nend\r\n---\r\n\r\nPlanet coming soon!','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">hpricot</span><span class=\"dl\">\'</span></span><tt>\n</tt>require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">open-uri</span><span class=\"dl\">\'</span></span><tt>\n</tt><tt>\n</tt><span class=\"r\">def</span> <span class=\"fu\">ini_entry</span>(url, name)<tt>\n</tt> buffer = <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">[</span><span class=\"il\"><span class=\"dl\">#{</span>url<span class=\"dl\">}</span></span><span class=\"k\">]</span><span class=\"ch\">\\\\</span><span class=\"k\">n</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> buffer += <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">name = </span><span class=\"il\"><span class=\"dl\">#{</span>name<span class=\"dl\">}</span></span><span class=\"ch\">\\\\</span><span class=\"k\">n</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> buffer += <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"ch\">\\\\</span><span class=\"k\">n</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> buffer<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>doc = Hpricot(open(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://www.byteclub.net/testsite/getFeeds.php</span><span class=\"dl\">&quot;</span></span>))<tt>\n</tt><tt>\n</tt>(doc/<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">blog</span><span class=\"dl\">&quot;</span></span>).each <span class=\"r\">do</span> |elem|<tt>\n</tt> url = (elem/<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">url</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt> name = (elem/<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">name</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt> comments = (elem/<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">comments</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt> <tt>\n</tt> <span class=\"r\">if</span> name.length &gt; <span class=\"i\">0</span><tt>\n</tt> puts ini_entry(url.inner_text, name.inner_text) <span class=\"r\">if</span> url.inner_text.length &gt; <span class=\"i\">0</span><tt>\n</tt> puts ini_entry(comments.inner_text, name.inner_text + <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\"> Comments</span><span class=\"dl\">&quot;</span></span>) <span class=\"r\">if</span> comments.inner_text.length &gt; <span class=\"i\">0</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Planet coming soon!</p>',1,0,NULL,'2007-06-01 02:11:00','2007-08-18 02:12:32','2007-08-21 07:37:11'),(141,'Let\'s go bowling with OO','let-s-go-bowling-with-oo','To compare with my <a href=\"http://rhnh.net/2007/5/12/let-s-go-bowling\">previous post</a>: <a href=\"http://rhnh.svnrepository.com/svn/public/ruby/snippets/bowling_scorer_oo.rb\">bowling_scorer_oo.rb</a>\r\n\r\nI don\'t like this version as much.\r\n\r\nHow would YOU do it?','<p>To compare with my <a href=\"http://rhnh.net/2007/5/12/let-s-go-bowling\">previous post</a>: <a href=\"http://rhnh.svnrepository.com/svn/public/ruby/snippets/bowling_scorer_oo.rb\">bowling_scorer_oo.rb</a></p>\n\n\n <p>I don&#8217;t like this version as much.</p>\n\n\n <p>How would <span class=\"caps\">YOU</span> do it?</p>',1,0,NULL,'2007-05-13 02:14:00','2007-08-18 02:15:10','2007-08-18 02:17:56'),(142,'Let\'s go bowling','let-s-go-bowling','--- ruby\r\nclass BowlingScorer\r\n def score(balls, frames = 10)\r\n return frames == 0 ? 0 : score_function(balls[0], balls[1]).call(balls) + score(balls, frames - 1)\r\n end\r\n \r\nprotected\r\n Component = Struct.new(:condition, :number_to_score, :number_to_shift)\r\n ConditionIsTrue = lambda {|x| x[0].call }\r\n \r\n def score_function(s1, s2)\r\n p = Component.new *[\r\n [ lambda { s1 == 10}, 3, 1], # Strike\r\n [ lambda { s1 + s2 == 10}, 3, 2], # Spare\r\n [ lambda { true }, 2, 2] # Default\r\n ].find(&ConditionIsTrue)\r\n return join_return_first(score_frame(p.number_to_score), multi_shift(p.number_to_shift))\r\n end\r\n \r\n def score_frame(n)\r\n lambda {|balls| n ? balls[0..n-1].inject(0) {|a, g| a + g } : 0 }\r\n end\r\n \r\n def multi_shift(count)\r\n lambda {|x| count.times { x.shift } }\r\n end\r\nend\r\n\r\nscorer = BowlingScorer.new\r\nscorer.score([10] * 11) # => 300\r\nscorer.score([5] * 21) # => 150\r\n---\r\n\r\nFull source and tests - <a href=\"http://rhnh.svnrepository.com/svn/public/ruby/snippets/bowling_scorer.rb\">bowling_scorer.rb</a>\r\n\r\nEDIT: Refactored BowlingScorer#score_function','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">BowlingScorer</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">score</span>(balls, frames = <span class=\"i\">10</span>)<tt>\n</tt> <span class=\"r\">return</span> frames == <span class=\"i\">0</span> ? <span class=\"i\">0</span> : score_function(balls[<span class=\"i\">0</span>], balls[<span class=\"i\">1</span>]).call(balls) + score(balls, frames - <span class=\"i\">1</span>)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <tt>\n</tt>protected<tt>\n</tt> <span class=\"co\">Component</span> = <span class=\"co\">Struct</span>.new(<span class=\"sy\">:condition</span>, <span class=\"sy\">:number_to_score</span>, <span class=\"sy\">:number_to_shift</span>)<tt>\n</tt> <span class=\"co\">ConditionIsTrue</span> = lambda {|x| x[<span class=\"i\">0</span>].call }<tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">score_function</span>(s1, s2)<tt>\n</tt> p = <span class=\"co\">Component</span>.new *[<tt>\n</tt> [ lambda { s1 == <span class=\"i\">10</span>}, <span class=\"i\">3</span>, <span class=\"i\">1</span>], <span class=\"c\"># Strike</span><tt>\n</tt> [ lambda { s1 + s2 == <span class=\"i\">10</span>}, <span class=\"i\">3</span>, <span class=\"i\">2</span>], <span class=\"c\"># Spare</span><tt>\n</tt> [ lambda { <span class=\"pc\">true</span> }, <span class=\"i\">2</span>, <span class=\"i\">2</span>] <span class=\"c\"># Default</span><tt>\n</tt> ].find(&amp;<span class=\"co\">ConditionIsTrue</span>)<tt>\n</tt> <span class=\"r\">return</span> join_return_first(score_frame(p.number_to_score), multi_shift(p.number_to_shift))<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">score_frame</span>(n)<tt>\n</tt> lambda {|balls| n ? balls[<span class=\"i\">0</span>..n<span class=\"i\">-1</span>].inject(<span class=\"i\">0</span>) {|a, g| a + g } : <span class=\"i\">0</span> }<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">multi_shift</span>(count)<tt>\n</tt> lambda {|x| count.times { x.shift } }<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>scorer = <span class=\"co\">BowlingScorer</span>.new<tt>\n</tt>scorer.score([<span class=\"i\">10</span>] * <span class=\"i\">11</span>) <span class=\"c\"># =&gt; 300</span><tt>\n</tt>scorer.score([<span class=\"i\">5</span>] * <span class=\"i\">21</span>) <span class=\"c\"># =&gt; 150</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Full source and tests &#8211; <a href=\"http://rhnh.svnrepository.com/svn/public/ruby/snippets/bowling_scorer.rb\">bowling_scorer.rb</a></p>\n\n\n <p><span class=\"caps\">EDIT</span>: Refactored BowlingScorer#score_function</p>',1,0,NULL,'2007-05-12 02:15:00','2007-08-18 02:16:32','2007-08-18 02:16:32'),(143,'Eating with functions','eating-with-functions','--- ruby\r\n# 3 Tasty treats, all the same!\r\nedibles.each do |edible|\r\n edible.eat! if likes?(edible) || edible.is_healthy?\r\nend\r\n\r\ncondition = lambda {|edible| likes?(edible) || edible.is_healthy?}\r\nedibles.select(&condition).each(&:eat!)\r\n\r\nedibles.select(disjoin(&method(:likes?), &:is_healthy?)).each(&:eat!)\r\n---\r\n\r\nHelp: <a href=\"http://api.rubyonrails.org/classes/Symbol.html\">&:eat!</a>, <a href=\"http://www.randomhacks.net/articles/2007/02/01/some-useful-closures-in-ruby\">disjoin</a>\r\n','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\"># 3 Tasty treats, all the same!</span><tt>\n</tt>edibles.each <span class=\"r\">do</span> |edible|<tt>\n</tt> edible.eat! <span class=\"r\">if</span> likes?(edible) || edible.is_healthy?<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>condition = lambda {|edible| likes?(edible) || edible.is_healthy?}<tt>\n</tt>edibles.select(&amp;condition).each(&amp;<span class=\"sy\">:eat!</span>)<tt>\n</tt><tt>\n</tt>edibles.select(disjoin(&amp;method(<span class=\"sy\">:likes?</span>), &amp;<span class=\"sy\">:is_healthy?</span>)).each(&amp;<span class=\"sy\">:eat!</span>)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Help: <a href=\"http://api.rubyonrails.org/classes/Symbol.html\">&#38;:eat!</a>, <a href=\"http://www.randomhacks.net/articles/2007/02/01/some-useful-closures-in-ruby\">disjoin</a></p>',1,0,NULL,'2007-05-11 21:20:00','2007-08-18 02:21:02','2007-08-18 02:21:02'),(144,'Data is fun','data-is-fun','This is a story about a graph.\r\n\r\nInspiration struck just before sunrise one Sunday morning. 8 of us, too tired to sleep, decided to construct a relationship map of the local swing dancing scene. Naturally, the discussion turned to relationships on a micro level ... who dances with who, who asks who, and the like, a topic quickly abandoned since gossip is a much more readily available data at 5am in the morning. But the seed was sown and my mind was compelled to tend it. On Monday I borrowed a copy of Tufte\'s \"The Visual Display of Quantitative Information\":http://books.google.com.au/books?id=JNoBAAAACAAJ from work and, well, if you don\'t feel like drawing a graph after reading that book there is something wrong with you.\r\n\r\nh3. Collection\r\n\r\nOn the following Wednesday I packed up my laptop and set off to brat pack (my performance troupe) rehearsal. Innocuously planted in the line of other machines waiting to play music or show off videos, my iSight went unnoticed as it snapped a picture of the dance floor every second during social dancing, weaving them together into a little over 1 minute of footage.\r\n\r\nThat Friday after a few too many post work beers at the local, being in an appropriate data collection mood I reviewed the footage and created a two column table: lead on the left, follow on the right, one row per song. The low quality of the iSight made identifying couples towards the rear of the hall tricky, but the tendency of dancers to generally wear distinctly colored clothes made it possible.\r\n\r\nh3. Presentation\r\n\r\nA brief stint of research led me to \"Processing\":http://processing.org/, a Java environment for creating neat data visuals. I would have preferred something with ruby, but you take what you can get. My Java was a bit rusty, and the collection handling was downright clumsy to what I was used to in ruby, but after a Saturday of hacking I had something I\'m quite proud of. Behold, the \"dancing network of brat pack for the 15th August 2007\":/files/brats-graph-20070815.png.\r\n\r\nI tried to apply many of Tufte\'s ideas in the creation of this graph. It was initially presented vertically, but I rotated it so it was easier to compare the histograms. Chart-junk is kept to a minimum, only the horizontal lines representing each dancer are non-data carrying, and the connecting lines were deliberately thinned and lightened to make interpreting the myriad of partnerships easier. Labels use a serif font and also provide scale information and except for one are all presented horizontally.\r\n\r\nLooking forward, I\'d like to collect some richer data - both more of the same and also extra information like tempo of song - to incorporate into the graph. I suspect the best way to do this would be to record normal video rather than timelapse, to both grab the audio and also make identifying partnerships easier.','<p>This is a story about a graph.</p>\n\n\n <p>Inspiration struck just before sunrise one Sunday morning. 8 of us, too tired to sleep, decided to construct a relationship map of the local swing dancing scene. Naturally, the discussion turned to relationships on a micro level &#8230; who dances with who, who asks who, and the like, a topic quickly abandoned since gossip is a much more readily available data at 5am in the morning. But the seed was sown and my mind was compelled to tend it. On Monday I borrowed a copy of Tufte&#8217;s <a href=\"http://books.google.com.au/books?id=JNoBAAAACAAJ\">The Visual Display of Quantitative Information</a> from work and, well, if you don&#8217;t feel like drawing a graph after reading that book there is something wrong with you.</p>\n\n\n <h3>Collection</h3>\n\n\n <p>On the following Wednesday I packed up my laptop and set off to brat pack (my performance troupe) rehearsal. Innocuously planted in the line of other machines waiting to play music or show off videos, my iSight went unnoticed as it snapped a picture of the dance floor every second during social dancing, weaving them together into a little over 1 minute of footage.</p>\n\n\n <p>That Friday after a few too many post work beers at the local, being in an appropriate data collection mood I reviewed the footage and created a two column table: lead on the left, follow on the right, one row per song. The low quality of the iSight made identifying couples towards the rear of the hall tricky, but the tendency of dancers to generally wear distinctly colored clothes made it possible.</p>\n\n\n <h3>Presentation</h3>\n\n\n <p>A brief stint of research led me to <a href=\"http://processing.org/\">Processing</a>, a Java environment for creating neat data visuals. I would have preferred something with ruby, but you take what you can get. My Java was a bit rusty, and the collection handling was downright clumsy to what I was used to in ruby, but after a Saturday of hacking I had something I&#8217;m quite proud of. Behold, the <a href=\"/files/brats-graph-20070815.png\">dancing network of brat pack for the 15th August 2007</a>.</p>\n\n\n <p>I tried to apply many of Tufte&#8217;s ideas in the creation of this graph. It was initially presented vertically, but I rotated it so it was easier to compare the histograms. Chart-junk is kept to a minimum, only the horizontal lines representing each dancer are non-data carrying, and the connecting lines were deliberately thinned and lightened to make interpreting the myriad of partnerships easier. Labels use a serif font and also provide scale information and except for one are all presented horizontally.</p>\n\n\n <p>Looking forward, I&#8217;d like to collect some richer data &#8211; both more of the same and also extra information like tempo of song &#8211; to incorporate into the graph. I suspect the best way to do this would be to record normal video rather than timelapse, to both grab the audio and also make identifying partnerships easier.</p>',1,1,NULL,'2007-08-18 08:16:00','2007-08-18 08:18:00','2007-08-20 02:29:02'),(146,'Practical Hpricot: CruiseControl.rb results','practical-hpricot-cruisecontrol-rb-results','--- ruby\r\nrequire \'hpricot\'\r\nrequire \'open-uri\'\r\n\r\nurl = \"http://mydomain.com/builds/myapp/#{ARGV[0]}\"\r\ndoc = Hpricot(open(url))\r\n\r\nputs (doc/\"div#build_details h1\").first.inner_text.gsub(/^\\s*/, \'\')\r\n(doc/\"div.test-results\").each do |results|\r\n puts results.inner_html\r\nend\r\n---\r\n\r\nGrabs the current build status from CruiseControl.rb. Especially handy since our build server isn\'t sending emails at the moment.','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">hpricot</span><span class=\"dl\">\'</span></span><tt>\n</tt>require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">open-uri</span><span class=\"dl\">\'</span></span><tt>\n</tt><tt>\n</tt>url = <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://mydomain.com/builds/myapp/</span><span class=\"il\"><span class=\"dl\">#{</span><span class=\"pc\">ARGV</span>[<span class=\"i\">0</span>]<span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span><tt>\n</tt>doc = Hpricot(open(url))<tt>\n</tt><tt>\n</tt>puts (doc/<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">div#build_details h1</span><span class=\"dl\">&quot;</span></span>).first.inner_text.gsub(<span class=\"rx\"><span class=\"dl\">/</span><span class=\"k\">^</span><span class=\"ch\">\\s</span><span class=\"k\">*</span><span class=\"dl\">/</span></span>, <span class=\"s\"><span class=\"dl\">\'</span><span class=\"dl\">\'</span></span>)<tt>\n</tt>(doc/<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">div.test-results</span><span class=\"dl\">&quot;</span></span>).each <span class=\"r\">do</span> |results|<tt>\n</tt> puts results.inner_html<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Grabs the current build status from CruiseControl.rb. Especially handy since our build server isn&#8217;t sending emails at the moment.</p>',1,0,NULL,'2007-08-21 07:33:00','2007-08-21 07:35:47','2007-08-21 07:36:09'),(147,'Scrabble isn\'t geeky enough','scrabble-isn-t-geeky-enough','<a href=\"http://en.wikipedia.org/wiki/Arity\">Arity</a> isn\'t in the official scrabble dictionary :(','<p><a href=\"http://en.wikipedia.org/wiki/Arity\">Arity</a> isn&#8217;t in the official scrabble dictionary :(</p>',1,0,NULL,'2007-04-01 07:38:00','2007-08-21 07:39:03','2007-08-21 07:39:29'),(148,'Google Update','google-update','Hooray, the obscure oracle forum postings are now gone from my ego-google! rhnh.net has been pushed down to the bottom of the first page, having been overtaken by blog.rhnh.net, probably because I use the latter as my URL when commenting on most blogs. This blog has shot to the top, propelled (I presume) by a trackback link on Sam Ruby\'s site to my OpenID post. Interestingly my redbubble profile has already hit number #4 with absolutely no link love from me. Agreeably, the remainder of the first page is rails related.\r\n\r\nYay for personal branding.\r\n\r\nIn other news my new job is FREAKING AWESOME. I\'m still getting used to working in a team on a production site (you can\'t just go nuts with design decisions/refactoring near a release, no matter how awesome they are), but I\'m learning.','<p>Hooray, the obscure oracle forum postings are now gone from my ego-google! rhnh.net has been pushed down to the bottom of the first page, having been overtaken by blog.rhnh.net, probably because I use the latter as my <span class=\"caps\">URL</span> when commenting on most blogs. This blog has shot to the top, propelled (I presume) by a trackback link on Sam Ruby&#8217;s site to my OpenID post. Interestingly my redbubble profile has already hit number #4 with absolutely no link love from me. Agreeably, the remainder of the first page is rails related.</p>\n\n\n <p>Yay for personal branding.</p>\n\n\n <p>In other news my new job is <span class=\"caps\">FREAKING AWESOME</span>. I&#8217;m still getting used to working in a team on a production site (you can&#8217;t just go nuts with design decisions/refactoring near a release, no matter how awesome they are), but I&#8217;m learning.</p>',1,0,NULL,'2007-03-27 07:40:00','2007-08-21 07:41:20','2007-08-21 07:42:00'),(149,'Joining the fold','joining-the-fold','Walked away from signing my employment contract yesterday with a shiny new MacBook Pro...','<p>Walked away from signing my employment contract yesterday with a shiny new MacBook Pro&#8230;</p>',1,0,NULL,'2007-03-15 06:44:00','2007-08-21 07:45:25','2007-08-21 07:45:25'),(150,'New job','new-job','The plan was to stay on holiday until I ran out of money. Things changed when I got asked to apply for a job that looked really awesome.\r\n\r\nAs of Monday, I will be employed as a full time Ruby on Rails developer for <a href=\"http://www.redbubble.com.au\">RedBubble</a>. I\'m really excited about this. They are a small company - around 10 people - with 4 existing, very talented developers. I went out socialising with them all after my second interview last Thursday and everyone seems really cool. Their processes are top notch and their tools are excellent (Rails on Mac - hooray!). The company seems to realise what it needs to motivate their developers - to give you some examples, they\'ve had Koz (from the rails core team) in to help improve their stuff, and they\'re sending their head developer over to railsconf.\r\n\r\nSpeaking of awesome people - Martin Fowler came along to the last Melbourne Ruby User\'s Group omg omg omg * hero worship *.','<p>The plan was to stay on holiday until I ran out of money. Things changed when I got asked to apply for a job that looked really awesome.</p>\n\n\n <p>As of Monday, I will be employed as a full time Ruby on Rails developer for <a href=\"http://www.redbubble.com.au\">RedBubble</a>. I&#8217;m really excited about this. They are a small company &#8211; around 10 people &#8211; with 4 existing, very talented developers. I went out socialising with them all after my second interview last Thursday and everyone seems really cool. Their processes are top notch and their tools are excellent (Rails on Mac &#8211; hooray!). The company seems to realise what it needs to motivate their developers &#8211; to give you some examples, they&#8217;ve had Koz (from the rails core team) in to help improve their stuff, and they&#8217;re sending their head developer over to railsconf.</p>\n\n\n <p>Speaking of awesome people &#8211; Martin Fowler came along to the last Melbourne Ruby User&#8217;s Group omg omg omg * hero worship *.</p>',1,0,NULL,'2007-03-13 06:47:00','2007-08-21 07:47:56','2007-08-21 07:49:14'),(151,'Global Warming','global-warming','Not IT related, but I recall a few byteclubbers are interested in this.\r\n\r\nI have recently read two quite different views on global warming. The first is Peter Singer\'s analysis in \"One World: The Ethics of Globalization\" (highly recommended, although I haven\'t actually finished it yet...), which argues in support of the Kyoto protocol (in particular, why developing countries should be exempt). The second was linked to today by bbspot: <a href=\"http://www.johnstonsarchive.net/environment/index.html\">http://www.johnstonsarchive.net/environment/index.html</a>. I like extreme contrasts like this since they get you thinking. Ultimately, I find Peter Singer much more convincing. He presents a much more robust coverage of the issue (in particular considering the entire world rather than just the US), and tends to avoid loaded language (loaded language rings alarm bells for me - if it\'s a convincing argument you should be able to present it without hyperbole). In addition, he makes it much easier to follow his sources - both authors claim scientific studies to support their claims.\r\n\r\nIn the end, it for me it comes down to acceptable risk. Yes, there may not be a robust scientific proof that global warming is caused by man, but there is sufficient correlation, and the risks too great (for non-developed countries, not so much the developed ones that Johnston focuses on) that we cannot afford to take a chance and do nothing.','<p>Not IT related, but I recall a few byteclubbers are interested in this.</p>\n\n\n <p>I have recently read two quite different views on global warming. The first is Peter Singer&#8217;s analysis in &#8220;One World: The Ethics of Globalization&#8221; (highly recommended, although I haven&#8217;t actually finished it yet&#8230;), which argues in support of the Kyoto protocol (in particular, why developing countries should be exempt). The second was linked to today by bbspot: <a href=\"http://www.johnstonsarchive.net/environment/index.html\">http://www.johnstonsarchive.net/environment/index.html</a>. I like extreme contrasts like this since they get you thinking. Ultimately, I find Peter Singer much more convincing. He presents a much more robust coverage of the issue (in particular considering the entire world rather than just the US), and tends to avoid loaded language (loaded language rings alarm bells for me &#8211; if it&#8217;s a convincing argument you should be able to present it without hyperbole). In addition, he makes it much easier to follow his sources &#8211; both authors claim scientific studies to support their claims.</p>\n\n\n <p>In the end, it for me it comes down to acceptable risk. Yes, there may not be a robust scientific proof that global warming is caused by man, but there is sufficient correlation, and the risks too great (for non-developed countries, not so much the developed ones that Johnston focuses on) that we cannot afford to take a chance and do nothing.</p>',1,3,NULL,'2007-02-02 06:58:00','2007-08-31 07:59:19','2007-08-31 08:18:48'),(159,'RailsConf Europe','railsconf-europe','I\'m flying out today for RailsConf Europe 2007 in Berlin. If you are going to be there, won\'t you join me for a drink?','<p>I&#8217;m flying out today for RailsConf Europe 2007 in Berlin. If you are going to be there, won&#8217;t you join me for a drink?</p>',1,0,NULL,'2007-09-15 02:02:00','2007-09-15 02:03:56','2007-09-15 02:03:57'),(160,'The Road To Berlin','the-road-to-berlin','Saturday, Noon: Let\'s roll<br />\r\n1:30pm: Join the wrong check-in line<br />\r\n2:12pm: 1 litre kahlua, 1 litre vanilla absolut vodka: $47<br />\r\n2:31pm: Almost get arrested for having a butter knife in my carry on luggage - it\'s been there for months :(<br />\r\n3:42pm: Kevin in 41B says \"join the rest of the cattle\"<br />\r\n4:16pm: First ever tiger beer<br />\r\n4:25pm: I am totally going to to speed run Mario<br />\r\n4:27pm: Nintendo locks up, resisting even a total system reboot<br />\r\n5:20pm: Vege 4TW, get served first<br />\r\n5:54pm: Kevin in 41B says \"we have a convert\" (Tiger #3)<br />\r\n6:31pm: Dance geekery - you could sort out some partnered finger chorey<br />\r\n9:37pm: \"Excuse me sir, Johnny Walker for you?\" (black)<br />\r\n9:38pm: 1 lemon + JW, 1 JW black, 1 lemon + JW<br />\r\n9:46pm: Band leader at Singapore airport: \"I have a bet with the guy on keys, natural curls?\"<br />\r\n9:57pm: 1 JW black, 1 lemon + JW<br />\r\n10:17pm: \"Free internet\" ain\'t so free. Enjoying a pint instead.<br />\r\n11:45pm: Wake up infront of a horrible vege meal with a hangover<br />\r\nSunday, 4:00am: Lufthansa food is inedible<br />\r\n4:35am: I am figuring a jazz routine to a live version of Buble\'s Moondance<br />\r\n7:20am: Frankfurt to Berlin flight departure gate changed<br />\r\n7:40am: While on the tarmac, flight delayed 1 hour<br />\r\n9:45am (25 hours later): Yes, thank you, it\'s good to be here\r\n\r\nUPDATE: Do read the even more exciting sequel, \"The Road Home\":/2007/9/25/the-road-home','<p>Saturday, Noon: Let&#8217;s roll<br />\n1:30pm: Join the wrong check-in line<br />\n2:12pm: 1 litre kahlua, 1 litre vanilla absolut vodka: $47<br />\n2:31pm: Almost get arrested for having a butter knife in my carry on luggage &#8211; it&#8217;s been there for months :(<br />\n3:42pm: Kevin in 41B says &#8220;join the rest of the cattle&#8221;<br />\n4:16pm: First ever tiger beer<br />\n4:25pm: I am totally going to to speed run Mario<br />\n4:27pm: Nintendo locks up, resisting even a total system reboot<br />\n5:20pm: Vege 4TW, get served first<br />\n5:54pm: Kevin in 41B says &#8220;we have a convert&#8221; (Tiger #3)<br />\n6:31pm: Dance geekery &#8211; you could sort out some partnered finger chorey<br />\n9:37pm: &#8220;Excuse me sir, Johnny Walker for you?&#8221; (black)<br />\n9:38pm: 1 lemon + JW, 1 JW black, 1 lemon + JW<br />\n9:46pm: Band leader at Singapore airport: &#8220;I have a bet with the guy on keys, natural curls?&#8221;<br />\n9:57pm: 1 JW black, 1 lemon + JW<br />\n10:17pm: &#8220;Free internet&#8221; ain&#8217;t so free. Enjoying a pint instead.<br />\n11:45pm: Wake up infront of a horrible vege meal with a hangover<br />\nSunday, 4:00am: Lufthansa food is inedible<br />\n4:35am: I am figuring a jazz routine to a live version of Buble&#8217;s Moondance<br />\n7:20am: Frankfurt to Berlin flight departure gate changed<br />\n7:40am: While on the tarmac, flight delayed 1 hour<br />\n9:45am (25 hours later): Yes, thank you, it&#8217;s good to be here</p>\n\n\n <p><span class=\"caps\">UPDATE</span>: Do read the even more exciting sequel, <a href=\"/2007/9/25/the-road-home\">The Road Home</a></p>',1,3,NULL,'2007-09-17 06:44:00','2007-09-17 06:51:55','2007-09-25 11:15:37'),(162,'Conference Cuisine','conference-cuisine','Don\'t ever let it be said we were left hungry at RailsConf Berlin. \r\n\r\nWith an attendance nearing 1000, sating the herd could not have been a trivial feat. The staff at the Maritim stepped up to the challenge, and were clearly in their element serving such a large audience. An all-you-can-eat buffet each day for lunch maintained the air of a multi course delicacy. The salads and desserts in particular were exquisitely presented, with small serving platters (shot glasses, for example) placed alongside the main serving giving the impression that the dining party could be counted on one hand, rather than the illegal quantities that would in fact be required.\r\n\r\nAnd the variety! Servings of lentil, tomato, potato, cous cous, capsicum and cucumber salads, cheeses, chillies, olives, seeds, beetroot, broccoli, breads, fried potato, rices, eggs, pestos, coleslaws, mushrooms, lasagne, curry - all a feast for the eyes as well as the tongue. Desserts included divine fruit combinations - fig and casois cream, banana and cherry tart, cream and mango ring cake, berry jam - and a chocolate mousse as light as the clouds, lightly encased in a delicate chocolate sponge. Individual truffles littered among the main platters made for a decadent final cadence.\r\n\r\nMy only criticism, reluctantly, is the hot food really suffered from the excess of scale. Presentation was clearly below that of the cold dishes, and \"hot\" is probably too generous an adjective. The taste of the lasagne and the broccoli was bland in comparison to exciting array of salads, but it did provide a nice anchor to the dish. I can\'t comment on the uninteresting choices (meat), so maybe these redeemed the mains.\r\n\r\nA more general note to close: If anyone ever tells you it is difficult to be a vegetarian in Berlin they are lying, and you should probably consider all of their opinions suspect.\r\n\r\nEDIT: Day 3 was shit - cardboard potato, bland carrots and something so forgettable I\'ve done just that.','<p>Don&#8217;t ever let it be said we were left hungry at RailsConf Berlin.</p>\n\n\n <p>With an attendance nearing 1000, sating the herd could not have been a trivial feat. The staff at the Maritim stepped up to the challenge, and were clearly in their element serving such a large audience. An all-you-can-eat buffet each day for lunch maintained the air of a multi course delicacy. The salads and desserts in particular were exquisitely presented, with small serving platters (shot glasses, for example) placed alongside the main serving giving the impression that the dining party could be counted on one hand, rather than the illegal quantities that would in fact be required.</p>\n\n\n <p>And the variety! Servings of lentil, tomato, potato, cous cous, capsicum and cucumber salads, cheeses, chillies, olives, seeds, beetroot, broccoli, breads, fried potato, rices, eggs, pestos, coleslaws, mushrooms, lasagne, curry &#8211; all a feast for the eyes as well as the tongue. Desserts included divine fruit combinations &#8211; fig and casois cream, banana and cherry tart, cream and mango ring cake, berry jam &#8211; and a chocolate mousse as light as the clouds, lightly encased in a delicate chocolate sponge. Individual truffles littered among the main platters made for a decadent final cadence.</p>\n\n\n <p>My only criticism, reluctantly, is the hot food really suffered from the excess of scale. Presentation was clearly below that of the cold dishes, and &#8220;hot&#8221; is probably too generous an adjective. The taste of the lasagne and the broccoli was bland in comparison to exciting array of salads, but it did provide a nice anchor to the dish. I can&#8217;t comment on the uninteresting choices (meat), so maybe these redeemed the mains.</p>\n\n\n <p>A more general note to close: If anyone ever tells you it is difficult to be a vegetarian in Berlin they are lying, and you should probably consider all of their opinions suspect.</p>\n\n\n <p><span class=\"caps\">EDIT</span>: Day 3 was shit &#8211; cardboard potato, bland carrots and something so forgettable I&#8217;ve done just that.</p>',1,0,NULL,'2007-09-18 11:56:00','2007-09-18 11:59:24','2007-09-25 10:46:32'),(163,'The Road Home','the-road-home','The mirror to my previous misadventure, \"The Road To Berlin\":/2007/9/17/the-road-to-berlin\r\n\r\nSaturday, 5:30pm: And they\'re racing!<br />\r\n6:30pm: Arrive at airport<br />\r\n6:40pm: Shoes get wet by an erratic shower<br />\r\n6:55pm: \"I\'m sorry sir, check-in for Frankfurt just closed. Please come back tomorrow\"<br />\r\n8:41pm: Arrive at \"dress to impress\" party in t-shirt, fisherman\'s and sandals<br />\r\nSunday, 3:00am: Catch up on work emails<br />\r\n5:30am: Fall asleep on floor<br />\r\n9:00am: Morning, sunshine. More work.<br />\r\nNoon: Prost! Not eating chicken for lunch.<br />\r\n3:15pm: Admonished by the polezei for dancing<br />\r\n4:16pm: Pass out on the side of the road outside Oktoberfest<br />\r\n4:50pm: Scab 80c off French guy and spend last Euros on a train ticket<br />\r\n5:42pm: Arrive at airport, with a little help from my friends<br />\r\n6:20pm: Push to front of check-in line, wailing \"I\'m going to miss my flight!\" (lie)<br />\r\n10:00pm: \"I\'m sorry sir, we don\'t have a vegetarian meal for you. Have a bonus roll-with-cheese.\"<br />\r\n3:00pm: Skip breakfast by nodding off during serving<br />\r\n5:35pm: After taxiing to the terminal, faint<br />\r\n5:38pm: After exitting the aircraft, faint<br />\r\n5:46pm: Eating chocolate while lying on the floor of business class<br />\r\n8:05pm: \"I\'m sorry sir, we don\'t have your ticket for this flight\"<br />\r\n8:15pm: Aquire ticket (5 minutes prior to checkin closure)<br />\r\n8:22pm: \"Excuse me sir, Absolut Vodka for you?\" (pass)<br />\r\nTuesday, 1am: No vege meal booked, but the vege+fish one only has fish in the salad<br />\r\n1:16am: French lady in 59B gives me her icecream<br />\r\n5:36am: Touchdown in Melbourne, 1 hour ahead of schedule due to a roaring tail wind. Fantastic.<br />\r\n5:46am: Exit plane. Don\'t faint. Fantastic.<br />\r\n5:55am: Exit Melbourne airport, having passed through duty-free collection, passport control, baggage collection and quarantine in under 10 minutes. Fantastic.<br />\r\n5:58am: Walk straight on to a sunbus that departs instantly. Fantastic.<br />\r\n6:55am: Asleep in own bed. FANTASTIC.<br />\r\n9:00am: At work, on time. Only a day late.\r\n','<p>The mirror to my previous misadventure, <a href=\"/2007/9/17/the-road-to-berlin\">The Road To Berlin</a></p>\n\n\n <p>Saturday, 5:30pm: And they&#8217;re racing!<br />\n6:30pm: Arrive at airport<br />\n6:40pm: Shoes get wet by an erratic shower<br />\n6:55pm: &#8220;I&#8217;m sorry sir, check-in for Frankfurt just closed. Please come back tomorrow&#8221;<br />\n8:41pm: Arrive at &#8220;dress to impress&#8221; party in t-shirt, fisherman&#8217;s and sandals<br />\nSunday, 3:00am: Catch up on work emails<br />\n5:30am: Fall asleep on floor<br />\n9:00am: Morning, sunshine. More work.<br />\nNoon: Prost! Not eating chicken for lunch.<br />\n3:15pm: Admonished by the polezei for dancing<br />\n4:16pm: Pass out on the side of the road outside Oktoberfest<br />\n4:50pm: Scab 80c off French guy and spend last Euros on a train ticket<br />\n5:42pm: Arrive at airport, with a little help from my friends<br />\n6:20pm: Push to front of check-in line, wailing &#8220;I&#8217;m going to miss my flight!&#8221; (lie)<br />\n10:00pm: &#8220;I&#8217;m sorry sir, we don&#8217;t have a vegetarian meal for you. Have a bonus roll-with-cheese.&#8221;<br />\n3:00pm: Skip breakfast by nodding off during serving<br />\n5:35pm: After taxiing to the terminal, faint<br />\n5:38pm: After exitting the aircraft, faint<br />\n5:46pm: Eating chocolate while lying on the floor of business class<br />\n8:05pm: &#8220;I&#8217;m sorry sir, we don&#8217;t have your ticket for this flight&#8221;<br />\n8:15pm: Aquire ticket (5 minutes prior to checkin closure)<br />\n8:22pm: &#8220;Excuse me sir, Absolut Vodka for you?&#8221; (pass)<br />\nTuesday, 1am: No vege meal booked, but the vege+fish one only has fish in the salad<br />\n1:16am: French lady in 59B gives me her icecream<br />\n5:36am: Touchdown in Melbourne, 1 hour ahead of schedule due to a roaring tail wind. Fantastic.<br />\n5:46am: Exit plane. Don&#8217;t faint. Fantastic.<br />\n5:55am: Exit Melbourne airport, having passed through duty-free collection, passport control, baggage collection and quarantine in under 10 minutes. Fantastic.<br />\n5:58am: Walk straight on to a sunbus that departs instantly. Fantastic.<br />\n6:55am: Asleep in own bed. <span class=\"caps\">FANTASTIC</span>.<br />\n9:00am: At work, on time. Only a day late.</p>',1,0,NULL,'2007-09-25 11:11:00','2007-09-25 11:13:09','2007-09-25 11:20:37'),(165,'Counting ActiveRecord associations: count, size or length?','counting-activerecord-associations-count-size-or-length','Short answer: @size@. Here\'s why.\r\n\r\n@length@ will fall through to the underlying array, which will force a load of the association\r\n\r\n--- ruby\r\n>> user.posts.length\r\n Post Load (0.620579) SELECT * FROM posts WHERE (posts.user_id = 1321) \r\n=> 162\r\n---\r\n\r\nThis is bad. You loaded 162 objects into memory, just to count them. The DB can do this for us! That\'s what @count@ does.\r\n\r\n--- ruby\r\n>> user.posts.count\r\n SQL (0.060506) SELECT count(*) AS count_all FROM posts WHERE (posts.user_id = 1321) \r\n=> 162\r\n---\r\n\r\nNow we\'re on to something. The problem is, @count@ will _always_ issue a count to the DB, which is kind of redundant if you\'ve already loaded the association. That\'s were @size@ comes in. It\'s got smarts. Observe!\r\n\r\n--- ruby\r\n>> User.find(1321).posts.size\r\n User Load (0.003610) SELECT * FROM users WHERE (users.id = 1321) \r\n SQL (0.000544) SELECT count(*) AS count_all FROM posts WHERE (posts.user_id = 1321) \r\n=> 162\r\n>> User.find(1321, :include => :posts).posts.size \r\n User Load Including Associations (0.124950) SELECT ...\r\n=> 162\r\n---\r\n\r\nNotice it uses count, but if the association is already loaded (i.e. we already know how many objects there are), it uses length, for optimum DB usage.\r\n\r\nBut know that\'s not all. There\'s always more. If you also store the number of posts on the user object, as is common for performance reasons, @size@ will use that also. Just make sure the column is named _association__count (i.e. posts_count).\r\n\r\n--- ruby\r\n>> User.columns.collect(&:name).include?(\"posts_count\")\r\n=> true\r\n>> User.find(1321).posts.size\r\n User Load (0.003869) SELECT * FROM users WHERE (users.id = 1321) \r\n=> 162\r\n---\r\n\r\nh3. The bad news\r\n\r\nSo now you\'re all excited, I better tell you why this is only fantastic until you start using has_many :through.\r\n\r\nNow, the situation is slightly different between 1.2.x (r4605) and edge (r7639), so I\'ll start with stable. Now, they may look the same but a normal has_many association and one with the :through option are actually implememted by two entirely separate classes under the hood. And it so happens that the has_many :through version kind of, well, doesn\'t have quite the same smarts. It loads up the association just as length does (then falls through to @Array#size@). -Edge is sharp enough to use a @count@, but still doesn\'t know about any caches you may be using. This was commited in r7237, so it\'s pretty easy to patch in to stable. Or you can use this extension (on either branch - here is the \"trac ticket\":http://dev.rubyonrails.org/ticket/9734):- This patch was added to edge in \"7692\":http://dev.rubyonrails.org/changeset/7692\r\n\r\n--- ruby\r\nmodule CoreExtensions::HasManyThroughAssociation\r\n def size\r\n return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter?\r\n return @target.size if loaded?\r\n return count\r\n end\r\n\r\n def has_cached_counter?\r\n @owner.attribute_present?(cached_counter_attribute_name)\r\n end\r\n\r\n def cached_counter_attribute_name\r\n \"#{@reflection.name}_count\"\r\n end\r\nend\r\n\r\nActiveRecord::Associations::HasManyThroughAssociation.send(:include, CoreExtensions::HasManyThroughAssociation)\r\n---\r\n\r\nh3. How it doesn\'t work\r\n\r\n--- ruby\r\nuser.posts.find(:all, :conditions => [\"reply_count > ?\", 50]).size\r\n---\r\n\r\n@size@ normally works because assocations use a proxy - when I call @user.posts@ it won\'t actually load any posts until I call a method that requires them. So user.posts.size can work without ever loading the posts because they aren\'t required for the operation. The above code won\'t work well because find does not use a proxy - it will straight away load the requested posts from the DB, without @size@ getting a chance to send a COUNT instead. You may be better off moving this finder logic into an association so that @size@ will work as expected. This also has the benefit that if you decide to add a counter cache later on you won\'t have to change any code to use it.\r\n\r\n--- ruby\r\nhas_many :popular_posts, :class_name => \"Post\", :foreign_key => \"post_id\", :conditions => [\"reply_count > ?\", 50]\r\n---\r\n\r\nSo use @size@ when counting associations unless you have a good reason not to. Most importantly thought, ensure you\'re watching your development log so to be aware what SQL your app is generating.\r\n\r\nUPDATE: Added link to \"my patch on trac\":http://dev.rubyonrails.org/ticket/9734\r\n\r\nUPDATE 2: ... which is now closed, see \"r7692\":http://dev.rubyonrails.org/changeset/7692','<p>Short answer: <code>size</code>. Here&#8217;s why.</p>\n\n\n <p><code>length</code> will fall through to the underlying array, which will force a load of the association</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&gt;&gt; user.posts.length<tt>\n</tt> <span class=\"co\">Post</span> <span class=\"co\">Load</span> (<span class=\"fl\">0.620579</span>) <span class=\"co\">SELECT</span> * <span class=\"co\">FROM</span> posts <span class=\"co\">WHERE</span> (posts.user_id = <span class=\"i\">1321</span>) <tt>\n</tt>=&gt; <span class=\"i\">162</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>This is bad. You loaded 162 objects into memory, just to count them. The DB can do this for us! That&#8217;s what <code>count</code> does.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&gt;&gt; user.posts.count<tt>\n</tt> <span class=\"co\">SQL</span> (<span class=\"fl\">0.060506</span>) <span class=\"co\">SELECT</span> count(*) <span class=\"co\">AS</span> count_all <span class=\"co\">FROM</span> posts <span class=\"co\">WHERE</span> (posts.user_id = <span class=\"i\">1321</span>) <tt>\n</tt>=&gt; <span class=\"i\">162</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Now we&#8217;re on to something. The problem is, <code>count</code> will <em>always</em> issue a count to the DB, which is kind of redundant if you&#8217;ve already loaded the association. That&#8217;s were <code>size</code> comes in. It&#8217;s got smarts. Observe!</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&gt;&gt; <span class=\"co\">User</span>.find(<span class=\"i\">1321</span>).posts.size<tt>\n</tt> <span class=\"co\">User</span> <span class=\"co\">Load</span> (<span class=\"fl\">0.003610</span>) <span class=\"co\">SELECT</span> * <span class=\"co\">FROM</span> users <span class=\"co\">WHERE</span> (users.id = <span class=\"i\">1321</span>) <tt>\n</tt> <span class=\"co\">SQL</span> (<span class=\"fl\">0.000544</span>) <span class=\"co\">SELECT</span> count(*) <span class=\"co\">AS</span> count_all <span class=\"co\">FROM</span> posts <span class=\"co\">WHERE</span> (posts.user_id = <span class=\"i\">1321</span>) <tt>\n</tt>=&gt; <span class=\"i\">162</span><tt>\n</tt>&gt;&gt; <span class=\"co\">User</span>.find(<span class=\"i\">1321</span>, <span class=\"sy\">:include</span> =&gt; <span class=\"sy\">:posts</span>).posts.size <tt>\n</tt> <span class=\"co\">User</span> <span class=\"co\">Load</span> <span class=\"co\">Including</span> <span class=\"co\">Associations</span> (<span class=\"fl\">0.124950</span>) <span class=\"co\">SELECT</span> ...<tt>\n</tt>=&gt; <span class=\"i\">162</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Notice it uses count, but if the association is already loaded (i.e. we already know how many objects there are), it uses length, for optimum DB usage.</p>\n\n\n <p>But know that&#8217;s not all. There&#8217;s always more. If you also store the number of posts on the user object, as is common for performance reasons, <code>size</code> will use that also. Just make sure the column is named _association__count (i.e. posts_count).</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&gt;&gt; <span class=\"co\">User</span>.columns.collect(&amp;<span class=\"sy\">:name</span>).include?(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">posts_count</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt>=&gt; <span class=\"pc\">true</span><tt>\n</tt>&gt;&gt; <span class=\"co\">User</span>.find(<span class=\"i\">1321</span>).posts.size<tt>\n</tt> <span class=\"co\">User</span> <span class=\"co\">Load</span> (<span class=\"fl\">0.003869</span>) <span class=\"co\">SELECT</span> * <span class=\"co\">FROM</span> users <span class=\"co\">WHERE</span> (users.id = <span class=\"i\">1321</span>) <tt>\n</tt>=&gt; <span class=\"i\">162</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3>The bad news</h3>\n\n\n <p>So now you&#8217;re all excited, I better tell you why this is only fantastic until you start using has_many :through.</p>\n\n\n <p>Now, the situation is slightly different between 1.2.x (r4605) and edge (r7639), so I&#8217;ll start with stable. Now, they may look the same but a normal has_many association and one with the :through option are actually implememted by two entirely separate classes under the hood. And it so happens that the has_many :through version kind of, well, doesn&#8217;t have quite the same smarts. It loads up the association just as length does (then falls through to <code>Array#size</code>). <del>Edge is sharp enough to use a <code>count</code>, but still doesn&#8217;t know about any caches you may be using. This was commited in r7237, so it&#8217;s pretty easy to patch in to stable. Or you can use this extension (on either branch &#8211; here is the <a href=\"http://dev.rubyonrails.org/ticket/9734\">trac ticket</a>):</del> This patch was added to edge in <a href=\"http://dev.rubyonrails.org/changeset/7692\">7692</a></p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">module</span> <span class=\"cl\">CoreExtensions::HasManyThroughAssociation</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">size</span><tt>\n</tt> <span class=\"r\">return</span> <span class=\"iv\">@owner</span>.send(<span class=\"sy\">:read_attribute</span>, cached_counter_attribute_name) <span class=\"r\">if</span> has_cached_counter?<tt>\n</tt> <span class=\"r\">return</span> <span class=\"iv\">@target</span>.size <span class=\"r\">if</span> loaded?<tt>\n</tt> <span class=\"r\">return</span> count<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">has_cached_counter?</span><tt>\n</tt> <span class=\"iv\">@owner</span>.attribute_present?(cached_counter_attribute_name)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">cached_counter_attribute_name</span><tt>\n</tt> <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span><span class=\"iv\">@reflection</span>.name<span class=\"dl\">}</span></span><span class=\"k\">_count</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt><span class=\"co\">ActiveRecord</span>::<span class=\"co\">Associations</span>::<span class=\"co\">HasManyThroughAssociation</span>.send(<span class=\"sy\">:include</span>, <span class=\"co\">CoreExtensions</span>::<span class=\"co\">HasManyThroughAssociation</span>)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3>How it doesn&#8217;t work</h3>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">user.posts.find(<span class=\"sy\">:all</span>, <span class=\"sy\">:conditions</span> =&gt; [<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">reply_count &gt; ?</span><span class=\"dl\">&quot;</span></span>, <span class=\"i\">50</span>]).size<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p><code>size</code> normally works because assocations use a proxy &#8211; when I call <code>user.posts</code> it won&#8217;t actually load any posts until I call a method that requires them. So user.posts.size can work without ever loading the posts because they aren&#8217;t required for the operation. The above code won&#8217;t work well because find does not use a proxy &#8211; it will straight away load the requested posts from the DB, without <code>size</code> getting a chance to send a <span class=\"caps\">COUNT</span> instead. You may be better off moving this finder logic into an association so that <code>size</code> will work as expected. This also has the benefit that if you decide to add a counter cache later on you won&#8217;t have to change any code to use it.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">has_many <span class=\"sy\">:popular_posts</span>, <span class=\"sy\">:class_name</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Post</span><span class=\"dl\">&quot;</span></span>, <span class=\"sy\">:foreign_key</span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">post_id</span><span class=\"dl\">&quot;</span></span>, <span class=\"sy\">:conditions</span> =&gt; [<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">reply_count &gt; ?</span><span class=\"dl\">&quot;</span></span>, <span class=\"i\">50</span>]<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>So use <code>size</code> when counting associations unless you have a good reason not to. Most importantly thought, ensure you&#8217;re watching your development log so to be aware what <span class=\"caps\">SQL</span> your app is generating.</p>\n\n\n <p><span class=\"caps\">UPDATE</span>: Added link to <a href=\"http://dev.rubyonrails.org/ticket/9734\">my patch on trac</a></p>\n\n\n <p><span class=\"caps\">UPDATE 2</span>: ... which is now closed, see <a href=\"http://dev.rubyonrails.org/changeset/7692\">r7692</a></p>',1,0,NULL,'2007-09-26 23:16:00','2007-09-26 23:16:59','2007-10-01 10:14:31'),(167,'I\'m a rails contributor','i-m-a-rails-contributor','Allow me to gloat for a moment. Please turn your attention to \"changeset 7692\":http://dev.rubyonrails.org/changeset/7692 you\'ll notice my name in the credits. So it\'s not much, but there\'s a certain amount of geek cred there.','<p>Allow me to gloat for a moment. Please turn your attention to <a href=\"http://dev.rubyonrails.org/changeset/7692\">changeset 7692</a> you&#8217;ll notice my name in the credits. So it&#8217;s not much, but there&#8217;s a certain amount of geek cred there.</p>',1,0,NULL,'2007-10-01 10:05:00','2007-10-01 10:10:46','2007-10-01 10:10:47'),(168,'Object#send_with_default','object-send_with_default','Avoid those pesky whiny nils! @send_with_default@ won\'t complain.\r\n--- ruby\r\n\"hello\".send_with_default(:length, 0) # => 5\r\n nil.send_with_default(:length, 0) # => 0\r\n\"hello\".send_with_default(:index, -1, \'e\') # => 1\r\n---\r\nSo sending parameters is a little clunky, but I don\'t reckon\' you\'ll want to do that much. Here is the extension you want:\r\n--- ruby\r\nmodule CoreExtensions\r\n module Object\r\n def send_with_default(method, default, *args)\r\n !self.nil? && self.respond_to?(method) ? self.send(*args.unshift(method)) : default\r\n end\r\n end\r\nend\r\n\r\nObject.send(:include, CoreExtensions::Object)\r\n---','Avoid those pesky whiny nils! <code>send_with_default</code> won&#8217;t complain.\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">hello</span><span class=\"dl\">&quot;</span></span>.send_with_default(<span class=\"sy\">:length</span>, <span class=\"i\">0</span>) <span class=\"c\"># =&gt; 5</span><tt>\n</tt> <span class=\"pc\">nil</span>.send_with_default(<span class=\"sy\">:length</span>, <span class=\"i\">0</span>) <span class=\"c\"># =&gt; 0</span><tt>\n</tt><span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">hello</span><span class=\"dl\">&quot;</span></span>.send_with_default(<span class=\"sy\">:index</span>, <span class=\"i\">-1</span>, <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">e</span><span class=\"dl\">\'</span></span>) <span class=\"c\"># =&gt; 1</span><tt>\n</tt></pre></td>\n</tr></table>\n\nSo sending parameters is a little clunky, but I don&#8217;t reckon&#8217; you&#8217;ll want to do that much. Here is the extension you want:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">module</span> <span class=\"cl\">CoreExtensions</span><tt>\n</tt> <span class=\"r\">module</span> <span class=\"cl\">Object</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">send_with_default</span>(method, default, *args)<tt>\n</tt> !<span class=\"pc\">self</span>.nil? &amp;&amp; <span class=\"pc\">self</span>.respond_to?(method) ? <span class=\"pc\">self</span>.send(*args.unshift(method)) : default<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt><span class=\"co\">Object</span>.send(<span class=\"sy\">:include</span>, <span class=\"co\">CoreExtensions</span>::<span class=\"co\">Object</span>)<tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-10-02 07:37:00','2007-10-02 07:43:33','2007-10-02 07:45:06'),(171,'How we use the Presenter pattern','how-we-use-the-presenter-pattern','<em>FAKE EDIT: I wrote this article just after RailsConf but have just got around to publishing it. Jay has since \"written a follow up\":http://blog.jayfields.com/2007/10/rails-rise-fall-and-potential-rebirth.html which is worthwhile reading.</em>\r\n\r\nI may have been zoning out during Jay Fields talk at RailsConf - not sleeping for a few days will do that to you - but I think I got the gist of his presentation: \"Presenter\" isn\'t really a pattern because it\'s use is to specific and there isn\'t anything that be generalized from it. Now, I\'m not going to argue with Jay, but I thought it may be helpful to give an example of how we\'re using this \"pattern\" and how it is helpful for us at \"redbubble\":http://www.redbubble.com.\r\n\r\nUploading a piece of work to redbubble requires us to create two different models - a work and a storage, and associate them with each other. Initially, this logic was simply in the create method of one of our controllers. My problem with this was it obscured the intent of the controller. To my mind a controller is responsible for the flow of the application - the logic governing which page the user is directed to next - and kicking off any changes that need to happen at the model layer. In this case the controller was also dealing with the exact associations between the models, roll back conditions. Code that as we will see wasn\'t actually specific to the controller. In addition, passing validation errors through to the views was hard because errors could exist on one or more of the models. So we introduced a psuedo-model that handled the aggregation of the models for us, it looks something like this:\r\n\r\n--- ruby\r\nclass UploadWorkPresenter < Presenter\r\n include Validatable\r\n\r\n attr_reader :storage\r\n attr_reader :work\r\n\r\n delegate_attributes :storage, :attributes => [:file]\r\n delegate_attributes :work, :attributes => [:description]\r\n\r\n include_validations_for :storage\r\n include_validations_for :work\r\n\r\n def initialize(work_type, user, attributes = {})\r\n @work_type = work_type\r\n @work = work_type.new(:user => user, :publication_state => Work::PUBLISHED)\r\n @storage = work_type.storage_type.new\r\n\r\n initialize_from_hash(attributes)\r\n end\r\n\r\n def save\r\n return false if !self.valid?\r\n\r\n if @storage.save\r\n @work.storage = @storage\r\n if @work.save\r\n return true\r\n else\r\n @storage.destroy\r\n end\r\n end\r\n\r\n return false\r\n end\r\nend\r\n---\r\n\r\nWe have neatly encapsulated the logic of creating a work in a nice testable class that not only slims our controller, but can be reused. This came in handy when our UI guy thought it would be awesome if we could allow a user to signup and upload a work all on the same screen:\r\n\r\n--- ruby\r\nclass SignupWithImagePresenter < UploadWorkPresenter\r\n attr_reader :user\r\n\r\n delegate_attributes :user, :attributes => [:user_name, :email_address]\r\n\r\n include_validations_for :user\r\n\r\n def initialize(attributes)\r\n @user = User.new\r\n super(ImageWork, @user, attributes)\r\n end\r\n\r\n def save\r\n return false if !self.valid?\r\n\r\n begin\r\n User.transaction do\r\n raise(Validatable::RecordInvalid.new(self)) unless @user.save && super\r\n return true\r\n end\r\n rescue Validatable::RecordInvalid\r\n return false\r\n end\r\n end\r\nend\r\n---\r\n\r\nSo why does Jay think this is such a bad idea? I think it stems from a terminology issue. Presenters on Jay\'s project were cloudy with their responsibilties - handling aggregation, helper functions, and navigation. As you can see, the Presenters we use solely deal with aggregation, keeping their responsibility narrow.\r\n\r\nFor reference, here is our base Presenter class:\r\n--- ruby\r\nclass Presenter\r\n extend Forwardable\r\n \r\n def initialize_from_hash(params)\r\n params.each_pair do |attribute, value| \r\n self.send :\"#{attribute}=\", value\r\n end unless params.nil?\r\n end\r\n \r\n def self.protected_delegate_writer(delegate, attribute, options)\r\n define_method \"#{attribute}=\" do |value|\r\n self.send(delegate).send(\"#{attribute}=\", value) if self.send(options[:if])\r\n end\r\n end\r\n \r\n def self.delegate_attributes(*options)\r\n raise ArgumentError(\"Must specify both a delegate and an attribute list\") if options.size != 2\r\n delegate = options[0]\r\n options = options[1]\r\n prefix = options[:prefix].blank? ? \"\" : options[:prefix] + \"_\"\r\n options[:attributes].each do |attribute|\r\n def_delegator delegate, attribute, \"#{prefix}#{attribute}\"\r\n def_delegator delegate, \"#{attribute}=\".to_sym, \"#{prefix}#{attribute}=\".to_sym\r\n def_delegator delegate, \"#{attribute}?\".to_sym, \"#{prefix}#{attribute}?\".to_sym\r\n end\r\n end\r\nend\r\n---','<p><em><span class=\"caps\">FAKE EDIT</span>: I wrote this article just after RailsConf but have just got around to publishing it. Jay has since <a href=\"http://blog.jayfields.com/2007/10/rails-rise-fall-and-potential-rebirth.html\">written a follow up</a> which is worthwhile reading.</em></p>\n\n\n <p>I may have been zoning out during Jay Fields talk at RailsConf &#8211; not sleeping for a few days will do that to you &#8211; but I think I got the gist of his presentation: &#8220;Presenter&#8221; isn&#8217;t really a pattern because it&#8217;s use is to specific and there isn&#8217;t anything that be generalized from it. Now, I&#8217;m not going to argue with Jay, but I thought it may be helpful to give an example of how we&#8217;re using this &#8220;pattern&#8221; and how it is helpful for us at <a href=\"http://www.redbubble.com\">redbubble</a>.</p>\n\n\n <p>Uploading a piece of work to redbubble requires us to create two different models &#8211; a work and a storage, and associate them with each other. Initially, this logic was simply in the create method of one of our controllers. My problem with this was it obscured the intent of the controller. To my mind a controller is responsible for the flow of the application &#8211; the logic governing which page the user is directed to next &#8211; and kicking off any changes that need to happen at the model layer. In this case the controller was also dealing with the exact associations between the models, roll back conditions. Code that as we will see wasn&#8217;t actually specific to the controller. In addition, passing validation errors through to the views was hard because errors could exist on one or more of the models. So we introduced a psuedo-model that handled the aggregation of the models for us, it looks something like this:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt>31<tt>\n</tt>32<tt>\n</tt>33<tt>\n</tt>34<tt>\n</tt>35<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">UploadWorkPresenter</span> &lt; <span class=\"co\">Presenter</span><tt>\n</tt> include <span class=\"co\">Validatable</span><tt>\n</tt><tt>\n</tt> attr_reader <span class=\"sy\">:storage</span><tt>\n</tt> attr_reader <span class=\"sy\">:work</span><tt>\n</tt><tt>\n</tt> delegate_attributes <span class=\"sy\">:storage</span>, <span class=\"sy\">:attributes</span> =&gt; [<span class=\"sy\">:file</span>]<tt>\n</tt> delegate_attributes <span class=\"sy\">:work</span>, <span class=\"sy\">:attributes</span> =&gt; [<span class=\"sy\">:description</span>]<tt>\n</tt><tt>\n</tt> include_validations_for <span class=\"sy\">:storage</span><tt>\n</tt> include_validations_for <span class=\"sy\">:work</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">initialize</span>(work_type, user, attributes = {})<tt>\n</tt> <span class=\"iv\">@work_type</span> = work_type<tt>\n</tt> <span class=\"iv\">@work</span> = work_type.new(<span class=\"sy\">:user</span> =&gt; user, <span class=\"sy\">:publication_state</span> =&gt; <span class=\"co\">Work</span>::<span class=\"co\">PUBLISHED</span>)<tt>\n</tt> <span class=\"iv\">@storage</span> = work_type.storage_type.new<tt>\n</tt><tt>\n</tt> initialize_from_hash(attributes)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">save</span><tt>\n</tt> <span class=\"r\">return</span> <span class=\"pc\">false</span> <span class=\"r\">if</span> !<span class=\"pc\">self</span>.valid?<tt>\n</tt><tt>\n</tt> <span class=\"r\">if</span> <span class=\"iv\">@storage</span>.save<tt>\n</tt> <span class=\"iv\">@work</span>.storage = <span class=\"iv\">@storage</span><tt>\n</tt> <span class=\"r\">if</span> <span class=\"iv\">@work</span>.save<tt>\n</tt> <span class=\"r\">return</span> <span class=\"pc\">true</span><tt>\n</tt> <span class=\"r\">else</span><tt>\n</tt> <span class=\"iv\">@storage</span>.destroy<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">return</span> <span class=\"pc\">false</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>We have neatly encapsulated the logic of creating a work in a nice testable class that not only slims our controller, but can be reused. This came in handy when our UI guy thought it would be awesome if we could allow a user to signup and upload a work all on the same screen:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">SignupWithImagePresenter</span> &lt; <span class=\"co\">UploadWorkPresenter</span><tt>\n</tt> attr_reader <span class=\"sy\">:user</span><tt>\n</tt><tt>\n</tt> delegate_attributes <span class=\"sy\">:user</span>, <span class=\"sy\">:attributes</span> =&gt; [<span class=\"sy\">:user_name</span>, <span class=\"sy\">:email_address</span>]<tt>\n</tt><tt>\n</tt> include_validations_for <span class=\"sy\">:user</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">initialize</span>(attributes)<tt>\n</tt> <span class=\"iv\">@user</span> = <span class=\"co\">User</span>.new<tt>\n</tt> <span class=\"r\">super</span>(<span class=\"co\">ImageWork</span>, <span class=\"iv\">@user</span>, attributes)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">save</span><tt>\n</tt> <span class=\"r\">return</span> <span class=\"pc\">false</span> <span class=\"r\">if</span> !<span class=\"pc\">self</span>.valid?<tt>\n</tt><tt>\n</tt> <span class=\"r\">begin</span><tt>\n</tt> <span class=\"co\">User</span>.transaction <span class=\"r\">do</span><tt>\n</tt> raise(<span class=\"co\">Validatable</span>::<span class=\"co\">RecordInvalid</span>.new(<span class=\"pc\">self</span>)) <span class=\"r\">unless</span> <span class=\"iv\">@user</span>.save &amp;&amp; <span class=\"r\">super</span><tt>\n</tt> <span class=\"r\">return</span> <span class=\"pc\">true</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">rescue</span> <span class=\"co\">Validatable</span>::<span class=\"co\">RecordInvalid</span><tt>\n</tt> <span class=\"r\">return</span> <span class=\"pc\">false</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>So why does Jay think this is such a bad idea? I think it stems from a terminology issue. Presenters on Jay&#8217;s project were cloudy with their responsibilties &#8211; handling aggregation, helper functions, and navigation. As you can see, the Presenters we use solely deal with aggregation, keeping their responsibility narrow.</p>\n\n\nFor reference, here is our base Presenter class:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">Presenter</span><tt>\n</tt> extend <span class=\"co\">Forwardable</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">initialize_from_hash</span>(params)<tt>\n</tt> params.each_pair <span class=\"r\">do</span> |attribute, value| <tt>\n</tt> <span class=\"pc\">self</span>.send <span class=\"sy\"><span class=\"sy\">:</span><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"k\">=</span><span class=\"dl\">&quot;</span></span>, value<tt>\n</tt> <span class=\"r\">end</span> <span class=\"r\">unless</span> params.nil?<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"pc\">self</span>.protected_delegate_writer(delegate, attribute, options)<tt>\n</tt> define_method <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"k\">=</span><span class=\"dl\">&quot;</span></span> <span class=\"r\">do</span> |value|<tt>\n</tt> <span class=\"pc\">self</span>.send(delegate).send(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"k\">=</span><span class=\"dl\">&quot;</span></span>, value) <span class=\"r\">if</span> <span class=\"pc\">self</span>.send(options[<span class=\"sy\">:if</span>])<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"pc\">self</span>.delegate_attributes(*options)<tt>\n</tt> raise ArgumentError(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Must specify both a delegate and an attribute list</span><span class=\"dl\">&quot;</span></span>) <span class=\"r\">if</span> options.size != <span class=\"i\">2</span><tt>\n</tt> delegate = options[<span class=\"i\">0</span>]<tt>\n</tt> options = options[<span class=\"i\">1</span>]<tt>\n</tt> prefix = options[<span class=\"sy\">:prefix</span>].blank? ? <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"dl\">&quot;</span></span> : options[<span class=\"sy\">:prefix</span>] + <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">_</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> options[<span class=\"sy\">:attributes</span>].each <span class=\"r\">do</span> |attribute|<tt>\n</tt> def_delegator delegate, attribute, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>prefix<span class=\"dl\">}</span></span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span><tt>\n</tt> def_delegator delegate, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"k\">=</span><span class=\"dl\">&quot;</span></span>.to_sym, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>prefix<span class=\"dl\">}</span></span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"k\">=</span><span class=\"dl\">&quot;</span></span>.to_sym<tt>\n</tt> def_delegator delegate, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"k\">?</span><span class=\"dl\">&quot;</span></span>.to_sym, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>prefix<span class=\"dl\">}</span></span><span class=\"il\"><span class=\"dl\">#{</span>attribute<span class=\"dl\">}</span></span><span class=\"k\">?</span><span class=\"dl\">&quot;</span></span>.to_sym<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-10-19 07:01:00','2007-10-19 07:01:13','2007-10-19 07:04:07'),(175,'Extending Rails','extending-rails','Previously, I extended rails by monkey patching stuff in @lib/@. This was good because it kept @vendor/rails@ clean.\r\n\r\nI have changed my mind!\r\n\r\nI now just patch @vendor/rails@ directly with a comment prefixed by RBEXT explaining why. This means that when I @piston update@ rails, I get notified of any conflicts immediately, rather than having to remember what was in lib. It\'s also much easier and quicker than monkey patching. Theoretically, I could also run the rails tests to make sure everything is still kosher, but I must confess I haven\'t gotten around to patching the tests as well...\r\n\r\nAnd the comments are ace because I can use this sweet rake task to see what rb-rails currently looks like:\r\n--- ruby\r\ndesc \"Show all RB extensions in vendor/\"\r\ntask :core_extensions do\r\n FileList[\"vendor/**/*.rb\"].egrep(/RBEXT/)\r\nend\r\n---','<p>Previously, I extended rails by monkey patching stuff in <code>lib/</code>. This was good because it kept <code>vendor/rails</code> clean.</p>\n\n\n <p>I have changed my mind!</p>\n\n\n <p>I now just patch <code>vendor/rails</code> directly with a comment prefixed by <span class=\"caps\">RBEXT</span> explaining why. This means that when I <code>piston update</code> rails, I get notified of any conflicts immediately, rather than having to remember what was in lib. It&#8217;s also much easier and quicker than monkey patching. Theoretically, I could also run the rails tests to make sure everything is still kosher, but I must confess I haven&#8217;t gotten around to patching the tests as well&#8230;</p>\n\n\nAnd the comments are ace because I can use this sweet rake task to see what rb-rails currently looks like:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">desc <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Show all RB extensions in vendor/</span><span class=\"dl\">&quot;</span></span><tt>\n</tt>task <span class=\"sy\">:core_extensions</span> <span class=\"r\">do</span><tt>\n</tt> <span class=\"co\">FileList</span>[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">vendor/**/*.rb</span><span class=\"dl\">&quot;</span></span>].egrep(<span class=\"rx\"><span class=\"dl\">/</span><span class=\"k\">RBEXT</span><span class=\"dl\">/</span></span>)<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-10-22 07:31:00','2007-10-22 07:37:49','2007-10-22 07:40:00'),(180,'Enumerable#inject is my favourite method','inject-is-my-favourite-method','<em>Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. - \"RubyDoc\":http://www.ruby-doc.org/core/classes/Enumerable.html#M003171</em>\r\n\r\nIt just doesn\'t sound very helpful. I must confess, it isn\'t something I use everyday. But I love that when you do want to use it, it is oh so sweet. The canonical example is summing the elements in an array:\r\n\r\n--- ruby\r\n[1,2,3].inject(0) {|sum, n| sum + n} # => 6\r\n---\r\n\r\nProbably the most used pattern is converting an array to a hash:\r\n\r\n--- ruby\r\n[1,2,3].inject({}) {|a, v| a.update(v => v * 2)} # => {1 => 2, 2 => 4, 3 => 6}\r\n---\r\n\r\nSomeone in IRC today wanted a nested send, something like @\"string\".send(\"trim.downcase\")\r\n\r\n--- ruby\r\n\"trim.downcase\".split(\'.\').inject(\"HELLO \") {|obj, method| obj.send(method)} # => \"hello\"\r\n---\r\n\r\nWhat do you inject?','<p><em>Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. &#8211; <a href=\"http://www.ruby-doc.org/core/classes/Enumerable.html#M003171\">RubyDoc</a></em></p>\n\n\n <p>It just doesn&#8217;t sound very helpful. I must confess, it isn&#8217;t something I use everyday. But I love that when you do want to use it, it is oh so sweet. The canonical example is summing the elements in an array:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">[<span class=\"i\">1</span>,<span class=\"i\">2</span>,<span class=\"i\">3</span>].inject(<span class=\"i\">0</span>) {|sum, n| sum + n} <span class=\"c\"># =&gt; 6</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Probably the most used pattern is converting an array to a hash:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">[<span class=\"i\">1</span>,<span class=\"i\">2</span>,<span class=\"i\">3</span>].inject({}) {|a, v| a.update(v =&gt; v * <span class=\"i\">2</span>)} <span class=\"c\"># =&gt; {1 =&gt; 2, 2 =&gt; 4, 3 =&gt; 6}</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Someone in <span class=\"caps\">IRC</span> today wanted a nested send, something like @&#8221;string&#8221;.send(&#8220;trim.downcase&#8221;)</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">trim.downcase</span><span class=\"dl\">&quot;</span></span>.split(<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">.</span><span class=\"dl\">\'</span></span>).inject(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">HELLO </span><span class=\"dl\">&quot;</span></span>) {|obj, method| obj.send(method)} <span class=\"c\"># =&gt; &quot;hello&quot;</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>What do you inject?</p>',1,0,NULL,'2007-10-23 07:46:00','2007-10-23 07:41:39','2007-10-23 07:48:34'),(196,'Making an impact','making-an-impact','\"Poverty.com\":http://www.poverty.com/\r\n\r\nThe dynamic map in the header hits home hard, along with the real time death roll. Real people dying right now. This is a masterful example of boiling down your message and making it count. Even though they just make the names/photos up, it still works.\r\n\r\nFound via \"FreeRice\":http://www.freerice.com/ (partner site), a slick vocab game following in the footsteps of \"Ripple Search\":http://ripple.org/ - get advertising revenue, donate to charity.','<p><a href=\"http://www.poverty.com/\">Poverty.com</a></p>\n\n\n <p>The dynamic map in the header hits home hard, along with the real time death roll. Real people dying right now. This is a masterful example of boiling down your message and making it count. Even though they just make the names/photos up, it still works.</p>\n\n\n <p>Found via <a href=\"http://www.freerice.com/\">FreeRice</a> (partner site), a slick vocab game following in the footsteps of <a href=\"http://ripple.org/\">Ripple Search</a> &#8211; get advertising revenue, donate to charity.</p>',1,0,NULL,'2007-10-29 08:48:00','2007-10-29 08:55:49','2007-10-29 08:56:39'),(197,'Sinatra deserves an encore','sinatra-deserves-an-encore','I\'m putting together a small site for a dancing troupe I\'m involved with. Index page, bio pages, that\'s about it. I want basic templating so I can keep my HTML dry. Initially I tried rolling my own solution with ERB and rake to generate HTML, but that was shit, so I found \"Sinatra\":http://sinatra.rubyforge.org and found that a much tastier. It\'s kind of like \"camping\":http://code.whytheluckystiff.net/camping/ but without all the weird meta-fu. Also, it has a sweet name and sweet copy:\r\n\r\n--- bash\r\n$ ruby app.rb \r\n== Sinatra has taken the stage on port 4567!\r\nGET / | Status: 200 | Params: {:format=>\"html\"}\r\n== Sinatra has ended his set (crowd applauds)\r\n---\r\n\r\nMy app, sans views and data (use your imagination):\r\n\r\n--- ruby\r\n[\'sinatra\', \'yaml\'].each {|x| require x }\r\n\r\n# This complex bit just loads up a YAML file and indexes an array of hashes\r\n# by their name. Also, it symbolizes keys because strings are for losers\r\nsymbolize_keys = lambda {|a,v| a.update(v[0].intern => v[1]) }\r\nData = YAML.load(File.open(\'data/performers.yml\')).inject({}) {|a, v| a.update(v[\"name\"].downcase => v.inject({}, &symbolize_keys))}\r\n\r\nlayout do\r\n File.open(\'views/main.erb\').read\r\nend\r\n\r\nhelpers do\r\n def dancer\r\n data = Data[params[:id].downcase]\r\n data[:bio] = erb(:\"dancers/#{params[:id]}\")\r\n data\r\n end\r\nend\r\n\r\nget \'/\' do\r\n erb :index\r\nend\r\n\r\nget \'/dancers/:id\' do\r\n if dancer\r\n erb :dancer\r\n else\r\n status(404)\r\n end\r\nend\r\n\r\nstatic \'/static\', \'static\'\r\n---\r\n\r\nWhen I need to deploy to some cheap-cheap-we-support-nothing host I can just spider wget the whole site and FTP it up. For the complete integrated coding experience may I recommend Mr. Sinatra live with The Count Basie Band.','<p>I&#8217;m putting together a small site for a dancing troupe I&#8217;m involved with. Index page, bio pages, that&#8217;s about it. I want basic templating so I can keep my <span class=\"caps\">HTML</span> dry. Initially I tried rolling my own solution with <span class=\"caps\">ERB</span> and rake to generate <span class=\"caps\">HTML</span>, but that was shit, so I found <a href=\"http://sinatra.rubyforge.org\">Sinatra</a> and found that a much tastier. It&#8217;s kind of like <a href=\"http://code.whytheluckystiff.net/camping/\">camping</a> but without all the weird meta-fu. Also, it has a sweet name and sweet copy:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">$ ruby app.rb <tt>\n</tt>== Sinatra has taken the stage on port 4567!<tt>\n</tt>GET / | Status: 200 | Params: {:format=&gt;&quot;html&quot;}<tt>\n</tt>== Sinatra has ended his set (crowd applauds)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>My app, sans views and data (use your imagination):</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt>31<tt>\n</tt>32<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">[<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">sinatra</span><span class=\"dl\">\'</span></span>, <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">yaml</span><span class=\"dl\">\'</span></span>].each {|x| require x }<tt>\n</tt><tt>\n</tt><span class=\"c\"># This complex bit just loads up a YAML file and indexes an array of hashes</span><tt>\n</tt><span class=\"c\"># by their name. Also, it symbolizes keys because strings are for losers</span><tt>\n</tt>symbolize_keys = lambda {|a,v| a.update(v[<span class=\"i\">0</span>].intern =&gt; v[<span class=\"i\">1</span>]) }<tt>\n</tt><span class=\"co\">Data</span> = <span class=\"co\">YAML</span>.load(<span class=\"co\">File</span>.open(<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">data/performers.yml</span><span class=\"dl\">\'</span></span>)).inject({}) {|a, v| a.update(v[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">name</span><span class=\"dl\">&quot;</span></span>].downcase =&gt; v.inject({}, &amp;symbolize_keys))}<tt>\n</tt><tt>\n</tt>layout <span class=\"r\">do</span><tt>\n</tt> <span class=\"co\">File</span>.open(<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">views/main.erb</span><span class=\"dl\">\'</span></span>).read<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>helpers <span class=\"r\">do</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">dancer</span><tt>\n</tt> data = <span class=\"co\">Data</span>[params[<span class=\"sy\">:id</span>].downcase]<tt>\n</tt> data[<span class=\"sy\">:bio</span>] = erb(<span class=\"sy\"><span class=\"sy\">:</span><span class=\"dl\">&quot;</span><span class=\"k\">dancers/</span><span class=\"il\"><span class=\"dl\">#{</span>params[<span class=\"sy\">:id</span>]<span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt> data<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>get <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">/</span><span class=\"dl\">\'</span></span> <span class=\"r\">do</span><tt>\n</tt> erb <span class=\"sy\">:index</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>get <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">/dancers/:id</span><span class=\"dl\">\'</span></span> <span class=\"r\">do</span><tt>\n</tt> <span class=\"r\">if</span> dancer<tt>\n</tt> erb <span class=\"sy\">:dancer</span><tt>\n</tt> <span class=\"r\">else</span><tt>\n</tt> status(<span class=\"i\">404</span>)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>static <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">/static</span><span class=\"dl\">\'</span></span>, <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">static</span><span class=\"dl\">\'</span></span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>When I need to deploy to some cheap-cheap-we-support-nothing host I can just spider wget the whole site and <span class=\"caps\">FTP</span> it up. For the complete integrated coding experience may I recommend Mr. Sinatra live with The Count Basie Band.</p>',1,7,NULL,'2007-10-30 10:39:00','2007-10-30 10:51:41','2007-10-30 10:53:03'),(199,'Get with the times: IMAP','get-with-the-times-imap','Last night I bought a fastmail account that I can use to host my rhnh.net mail. It supports IMAP. I really should have found out about this a long time ago, I\'ve been living in the POP dark ages. I\'m not going to list the benefits - many have done so before. I\'m just going to say: If you\'re still using POP, stop kidding yourself and get on to IMAP. Your quality of life will improve.','<p>Last night I bought a fastmail account that I can use to host my rhnh.net mail. It supports <span class=\"caps\">IMAP</span>. I really should have found out about this a long time ago, I&#8217;ve been living in the <span class=\"caps\">POP</span> dark ages. I&#8217;m not going to list the benefits &#8211; many have done so before. I&#8217;m just going to say: If you&#8217;re still using <span class=\"caps\">POP</span>, stop kidding yourself and get on to <span class=\"caps\">IMAP</span>. Your quality of life will improve.</p>',1,2,NULL,'2007-10-31 01:45:00','2007-10-31 01:49:20','2007-10-31 01:49:20'),(203,'Shocking News From The Late News','shocking-news-from-the-late-news','A revolutionary new study reveals being obese increases your chance of cancer. Random fat member of public responds with \"Pfft science. Everything gives you cancer these days.\" The head of the meat industry claims we are eating nowhere near enough red meat and it absolutely essential that we get more steak in us.\r\n\r\nThis is why I don\'t watch TV. The problem is everyone else does.\r\n\r\nYou gotta laugh else you get really fucking depressed.','<p>A revolutionary new study reveals being obese increases your chance of cancer. Random fat member of public responds with &#8220;Pfft science. Everything gives you cancer these days.&#8221; The head of the meat industry claims we are eating nowhere near enough red meat and it absolutely essential that we get more steak in us.</p>\n\n\n <p>This is why I don&#8217;t watch TV. The problem is everyone else does.</p>\n\n\n <p>You gotta laugh else you get really fucking depressed.</p>',1,1,NULL,'2007-11-01 11:56:00','2007-11-01 12:01:11','2007-11-01 12:01:11'),(204,'Gutsy upgrade','gutsy-upgrade','Just upgraded Ubuntu from fiesty (7.04) to gusty (7.10). Was a bit touch and go for a moment ... got a filesystem check failure on reboot. Miamoto_musashi, my knight in shining armour from #roro, saved the day. @ls -l /dev/disk/by-id/@ revealed that my HDs had been remapped from @/dev/hd@ to @/dev/sd@. The gutsy upgrade had modified all of the standard partitions to use UUIDs, but had failed to update a custom mount I had (@/data -> /dev/hdc1@). Changed that over in @/etc/fstab@, reboot, hooray we have a winner.','<p>Just upgraded Ubuntu from fiesty (7.04) to gusty (7.10). Was a bit touch and go for a moment &#8230; got a filesystem check failure on reboot. Miamoto_musashi, my knight in shining armour from #roro, saved the day. <code>ls -l /dev/disk/by-id/</code> revealed that my HDs had been remapped from <code>/dev/hd</code> to <code>/dev/sd</code>. The gutsy upgrade had modified all of the standard partitions to use UUIDs, but had failed to update a custom mount I had (<code>/data -&gt; /dev/hdc1</code>). Changed that over in <code>/etc/fstab</code>, reboot, hooray we have a winner.</p>',1,0,NULL,'2007-11-02 23:52:00','2007-11-02 23:59:05','2007-11-02 23:59:35'),(205,'No Audio in Ubuntu','no-audio-in-ubuntu','Just a quick one - for some reason my sound stopped working in Ubuntu. To fix, right click volume icon (once you\'ve re added it to the panel if it\'s not usually there), select \"Open Volume Control\" and ensure that PCM is not muted.\r\n\r\nAlso, to allow sounds from multiple sources to play simultaneously, go to System -> Preferences -> Sound and select ESD for your output device and ensure \"Enable Software Sound Mixing (ESD)\" is selected. Not sure why this wasn\'t working as a default for me.','<p>Just a quick one &#8211; for some reason my sound stopped working in Ubuntu. To fix, right click volume icon (once you&#8217;ve re added it to the panel if it&#8217;s not usually there), select &#8220;Open Volume Control&#8221; and ensure that <span class=\"caps\">PCM</span> is not muted.</p>\n\n\n <p>Also, to allow sounds from multiple sources to play simultaneously, go to System -&gt; Preferences -&gt; Sound and select <span class=\"caps\">ESD</span> for your output device and ensure &#8220;Enable Software Sound Mixing (ESD)&#8221; is selected. Not sure why this wasn&#8217;t working as a default for me.</p>',1,0,NULL,'2007-01-08 00:58:00','2007-11-03 00:58:49','2007-11-03 00:58:49'),(206,'Mode Errors in Mobile Phones','mode-errors-in-mobile-phones','A <a href=\"http://www.humanized.com/weblog/2006/12/07/is_visual_feedback_enough_why_modes_kill/\">recent post</a> on the humanized weblog has got me thinking about mode errors in software I use often.\r\n\r\nOne particularly nasty one occurs on my Nokia 2100 phone when sending SMS messages. After selecting \"send\", a box is displayed to enter the destination number. To select a contact from your address book, you press button A. However, if a number is already present in the entry box (if you are replying to a message, and in other circumstances whose criteria I am uncertain of), the same button A sends the message!\r\n\r\nThe implication? My reflex action is to press button A immediately after selecting \"send\" to go to my address book. Twice in the past two days there has already (unexpectedly!) been a number there, causing me to send a message to the wrong person.\r\n\r\nWhat potentially embarrassing or disastrous mode errors do you deal with regularly?','<p>A <a href=\"http://www.humanized.com/weblog/2006/12/07/is_visual_feedback_enough_why_modes_kill/\">recent post</a> on the humanized weblog has got me thinking about mode errors in software I use often.</p>\n\n\n <p>One particularly nasty one occurs on my Nokia 2100 phone when sending <span class=\"caps\">SMS</span> messages. After selecting &#8220;send&#8221;, a box is displayed to enter the destination number. To select a contact from your address book, you press button A. However, if a number is already present in the entry box (if you are replying to a message, and in other circumstances whose criteria I am uncertain of), the same button A sends the message!</p>\n\n\n <p>The implication? My reflex action is to press button A immediately after selecting &#8220;send&#8221; to go to my address book. Twice in the past two days there has already (unexpectedly!) been a number there, causing me to send a message to the wrong person.</p>\n\n\n <p>What potentially embarrassing or disastrous mode errors do you deal with regularly?</p>',1,1,NULL,'2006-12-22 01:00:00','2007-11-03 01:01:19','2007-11-03 01:01:19'),(208,'Passwordless Login','passwordless-login','I\'ve been typing in SSH passwords for ever now. For some reason I just assumed it was a pain to setup passwordless login. Wrong! It took me about 10 minutes. Ubuntu already has all the tools you need.\r\n\r\n--- bash\r\nssh-keygen -t rsa -f ~/.ssh/id_rsa -C \"xavier@home\"\r\nssh-add ~/.ssh/id_rsa\r\ncat ~/.ssh/id_rsa.pub | ssh xavier@remote_host \'cat - >> ~/.ssh/authorized_keys\'\r\n---\r\n\r\nRepeats steps 3 and 4 for each remote host.\r\n\r\nIt just works. And you know how much I like that.\r\n\r\n<a href=\"http://mah.everybody.org/docs/ssh\">Reference</a>','<p>I&#8217;ve been typing in <span class=\"caps\">SSH</span> passwords for ever now. For some reason I just assumed it was a pain to setup passwordless login. Wrong! It took me about 10 minutes. Ubuntu already has all the tools you need.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">ssh-keygen -t rsa -f ~/.ssh/id_rsa -C &quot;xavier@home&quot;<tt>\n</tt>ssh-add ~/.ssh/id_rsa<tt>\n</tt>cat ~/.ssh/id_rsa.pub | ssh xavier@remote_host \'cat - &gt;&gt; ~/.ssh/authorized_keys\'<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Repeats steps 3 and 4 for each remote host.</p>\n\n\n <p>It just works. And you know how much I like that.</p>\n\n\n <p><a href=\"http://mah.everybody.org/docs/ssh\">Reference</a></p>',1,0,NULL,'2006-10-07 02:05:00','2007-11-03 01:07:29','2007-11-03 01:08:21'),(209,'Hack the Planet','hack-the-planet','<ol>\r\n<li>Vegetarian - battery farms lose!</li>\r\n<li>Buy organic, fairtrade and/or local where possible</li>\r\n<li>No car, use public transport and feet, except where not possible (Geelong)</li>\r\n<li>Refuse plastic bags, although I think perversely our excessive number of green bags at home is soon to become an environmental risk</li>\r\n<li>I plan to vote, haven\'t had the opportunity yet</li>\r\n<li>Spread the love. Bring politics into conversations. Getting people talking and thinking is the first step.</li>\r\n</ol>\r\n\r\nThe last one is important. Preaching at people will never work - global awareness must come from within. We must provide the support and encouragement. Lead by example. It can be tough sometimes. I almost hit intolerable despair last night. Startling, raw, realisations: The pope - the most important man in Christianity - is a political retard, the most powerful man in the world is widely regarded an idiot, and you couldn\'t have pulled the recent Naomi Robson story from Frontline... Politics, Religion, Media, the triple crown. The world is loco.\r\n\r\nIn other news, I\'ve just commited some C Sharp tools to <a href=\"http://make.rubyforge.org\">Ruby Rant</a> , if you\'re interested in a sweet build tool that lets you use ruby (XML loses!). I\'m using it for a fairly decent project at work (multiple projects, resources, unit tests, etc) and find it a pleasure to work with. Note I\'m talking about a replacement for the deprecated method described in the current documentation. I\'m going to get that updated, but for now check the mailing list for info.','<ol>\n<li>Vegetarian &#8211; battery farms lose!</li>\n<li>Buy organic, fairtrade and/or local where possible</li>\n<li>No car, use public transport and feet, except where not possible (Geelong)</li>\n<li>Refuse plastic bags, although I think perversely our excessive number of green bags at home is soon to become an environmental risk</li>\n<li>I plan to vote, haven&#8217;t had the opportunity yet</li>\n<li>Spread the love. Bring politics into conversations. Getting people talking and thinking is the first step.</li>\n</ol>\n\n <p>The last one is important. Preaching at people will never work &#8211; global awareness must come from within. We must provide the support and encouragement. Lead by example. It can be tough sometimes. I almost hit intolerable despair last night. Startling, raw, realisations: The pope &#8211; the most important man in Christianity &#8211; is a political retard, the most powerful man in the world is widely regarded an idiot, and you couldn&#8217;t have pulled the recent Naomi Robson story from Frontline&#8230; Politics, Religion, Media, the triple crown. The world is loco.</p>\n\n\n <p>In other news, I&#8217;ve just commited some C Sharp tools to <a href=\"http://make.rubyforge.org\">Ruby Rant</a> , if you&#8217;re interested in a sweet build tool that lets you use ruby (XML loses!). I&#8217;m using it for a fairly decent project at work (multiple projects, resources, unit tests, etc) and find it a pleasure to work with. Note I&#8217;m talking about a replacement for the deprecated method described in the current documentation. I&#8217;m going to get that updated, but for now check the mailing list for info.</p>',1,0,NULL,'2006-09-21 02:10:00','2007-11-03 01:12:36','2007-11-03 01:14:01'),(210,'Day of days','day-of-days','I shouldn\'t read <a href=\"http://www.joelonsoftware.com/items/2006/09/07.html\">Joel On Software</a>, it makes me covetous. Between no LCDs, a dodgy chair, Delphi <i>6</i>, PVCS(!!), motivation is waning at work. I must have raved about Subversion enough though - we have a meeting on Monday to discuss switching over. Nov 24 I finish. Can\'t wait until I\'m free of these commitments so I can start chasing some choice projects.\r\n\r\nJust had an amusing chat with mum about the internets, why people would be interested on the \"tripe\" on my website, why if you google my name you get \"private conversations\" I\'ve had.\r\n\r\nWhich made me realise I\'ve lost my #1 google spot to some posts I made on the Oracle forums while at Swinburne. Freaking Oracle.\r\n\r\nToday\'s awesome Ruby on Rails link: <a href=\"http://www.writertopia.com/developers/authorization\">Declarative Permissions</a>. Just added it to a project I\'m working on, and in less than an hour had a full authorization model going. Very pretty.','<p>I shouldn&#8217;t read <a href=\"http://www.joelonsoftware.com/items/2006/09/07.html\">Joel On Software</a>, it makes me covetous. Between no LCDs, a dodgy chair, Delphi <i>6</i>, <acronym title=\"!!\">PVCS</acronym>, motivation is waning at work. I must have raved about Subversion enough though &#8211; we have a meeting on Monday to discuss switching over. Nov 24 I finish. Can&#8217;t wait until I&#8217;m free of these commitments so I can start chasing some choice projects.</p>\n\n\n <p>Just had an amusing chat with mum about the internets, why people would be interested on the &#8220;tripe&#8221; on my website, why if you google my name you get &#8220;private conversations&#8221; I&#8217;ve had.</p>\n\n\n <p>Which made me realise I&#8217;ve lost my #1 google spot to some posts I made on the Oracle forums while at Swinburne. Freaking Oracle.</p>\n\n\n <p>Today&#8217;s awesome Ruby on Rails link: <a href=\"http://www.writertopia.com/developers/authorization\">Declarative Permissions</a>. Just added it to a project I&#8217;m working on, and in less than an hour had a full authorization model going. Very pretty.</p>',1,2,NULL,'2006-09-08 02:15:00','2007-11-03 01:15:49','2007-11-03 01:15:49'),(213,'LDAP Address Book with FreeBSD and SSL','howto-ldap-address-book-with-freebsd-and-ssl','First you need to install and configure the OpenLDAP server. Clearly you won\'t want to use rhnh.net - just substitute in your own domain.\r\n\r\n--- bash\r\nsudo pkg_add -r openldap24-server\r\nsudo pkg_add -r openssl\r\n\r\nsudo cp /usr/local/openssl/openssl.cnf.sample /usr/local/openssl/openssl.cnf \r\n# Generate a self signed certificate\r\nsudo openssl req -newkey rsa:1024 -x509 -nodes -out server.pem -keyout server.pem -days 3650\r\nsudo mkdir /usr/local/etc/ldap\r\nsudo mv server.pem /usr/local/etc/ldap\r\n\r\n# /etc/rc.conf\r\nslapd_enable=\"YES\"\r\nslapd_flags=\'-h \"ldaps://rhnh.net/\"\'\r\n\r\n# /usr/local/etc/openldap/ldap.conf\r\n# Add these same settings not just on the server but for each client\r\nBASE dc=rhnh, dc=net\r\nURI ldaps://rhnh.net/\r\nTLS_REQCERT allow\r\n\r\n# /usr/local/etc/openldap/slapd.conf:\r\n# Add\r\ninclude /usr/local/etc/openldap/schema/cosine.schema\r\ninclude /usr/local/etc/openldap/schema/inetorgperson.schema\r\n\r\nTLSCipherSuite HIGH:MEDIUM:-SSLv2\r\nTLSCACertificateFile /usr/local/etc/ldap/server.pem\r\nTLSCertificateFile /usr/local/etc/ldap/server.pem\r\nTLSCertificateKeyFile /usr/local/etc/ldap/server.pem\r\n\r\nrequire authc\r\n\r\n# Modify these properties from their defaults\r\nsuffix \"dc=rhnh,dc=net\"\r\nrootdn \"cn=xavier,dc=rhnh,dc=net\"\r\n# Use slappasswd to generate your own password\r\nrootpw {SSHA}Iogj+Awafoj9FP5IdLVy1DmFaASDw1P5 # secret\r\n---\r\n\r\nStart up the server to make sure everything is apples\r\n\r\n--- bash\r\nsudo /usr/local/etc/rc.d/slapd start\r\nopenssl s_client -connect rhnh.net:636 -showcerts\r\n---\r\n\r\nLoad up a schema to hold your address book entries, and here is also an example entry.\r\n\r\n--- bash\r\n# directory.ldif\r\ndn: dc=rhnh, dc=net\r\nobjectClass: top\r\nobjectClass: dcObject\r\nobjectClass: organization\r\ndc: rhnh\r\no: Robot Has No Heart\r\n\r\ndn: ou=people, dc=rhnh, dc=net\r\nobjectClass: top\r\nobjectClass: organizationalUnit\r\nou: people\r\n\r\n# contact.ldif\r\ndn: cn=Xavier Shay, ou=people, dc=rhnh, dc=net\r\nobjectClass: top\r\nobjectClass: person\r\nobjectClass: organizationalPerson\r\nobjectClass: inetOrgPerson\r\ncn: Xavier Shay\r\ngn: Xavier\r\nsn: Shay\r\nmail: contact@rhnh.net\r\nou: people\r\nmobile: 0400-123-456\r\n---\r\n--- bash\r\nldapadd -D \'cn=xavier,dc=rhnh,dc=net\' -f directory.ldif -W\r\nldapsearch -D \'cn=xavier,dc=rhnh,dc=net\' -w -x # Check everything worked\r\n---\r\n\r\nTo configure Thunderbird to use your address book, go to @Edit - Preferences... - Composition - Edit Directories...@ and follow the bouncing ball. Thunderbird can\'t write to the directory, which is kind of a pain. Maybe you could use Evolution, which I think works. Maybe you could write an app that monitors a drop box and updates your directory for you. Maybe you could assume I\'ve already done what I suggested and wait for me to release it in the very near future.\r\n\r\nTested on FreeBSD 6.2-stable\r\n\r\nh3. References\r\n\r\n * \"Building an Address Book with OpenLDAP\":http://www.onlamp.com/pub/a/onlamp/2003/03/27/ldap_ab.html\r\n * \"Installing Secure LDAP on Ubuntu using a self-signed certificate\":http://islandlinux.org/howto/installing-secure-ldap-openldap-ssl-ubuntu-using-self-signed-certificate\r\n * \"OpenLDAP Administrator\'s Guide\":http://www.openldap.org/doc/admin23/security.html','<p>First you need to install and configure the OpenLDAP server. Clearly you won&#8217;t want to use rhnh.net &#8211; just substitute in your own domain.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt>31<tt>\n</tt>32<tt>\n</tt>33<tt>\n</tt>34<tt>\n</tt>35<tt>\n</tt>36<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">sudo pkg_add -r openldap24-server<tt>\n</tt>sudo pkg_add -r openssl<tt>\n</tt><tt>\n</tt>sudo cp /usr/local/openssl/openssl.cnf.sample /usr/local/openssl/openssl.cnf <tt>\n</tt># Generate a self signed certificate<tt>\n</tt>sudo openssl req -newkey rsa:1024 -x509 -nodes -out server.pem -keyout server.pem -days 3650<tt>\n</tt>sudo mkdir /usr/local/etc/ldap<tt>\n</tt>sudo mv server.pem /usr/local/etc/ldap<tt>\n</tt><tt>\n</tt># /etc/rc.conf<tt>\n</tt>slapd_enable=&quot;YES&quot;<tt>\n</tt>slapd_flags=\'-h &quot;ldaps://rhnh.net/&quot;\'<tt>\n</tt><tt>\n</tt># /usr/local/etc/openldap/ldap.conf<tt>\n</tt># Add these same settings not just on the server but for each client<tt>\n</tt>BASE dc=rhnh, dc=net<tt>\n</tt>URI ldaps://rhnh.net/<tt>\n</tt>TLS_REQCERT allow<tt>\n</tt><tt>\n</tt># /usr/local/etc/openldap/slapd.conf:<tt>\n</tt># Add<tt>\n</tt>include /usr/local/etc/openldap/schema/cosine.schema<tt>\n</tt>include /usr/local/etc/openldap/schema/inetorgperson.schema<tt>\n</tt><tt>\n</tt>TLSCipherSuite HIGH:MEDIUM:-SSLv2<tt>\n</tt>TLSCACertificateFile /usr/local/etc/ldap/server.pem<tt>\n</tt>TLSCertificateFile /usr/local/etc/ldap/server.pem<tt>\n</tt>TLSCertificateKeyFile /usr/local/etc/ldap/server.pem<tt>\n</tt><tt>\n</tt>require authc<tt>\n</tt><tt>\n</tt># Modify these properties from their defaults<tt>\n</tt>suffix &quot;dc=rhnh,dc=net&quot;<tt>\n</tt>rootdn &quot;cn=xavier,dc=rhnh,dc=net&quot;<tt>\n</tt># Use slappasswd to generate your own password<tt>\n</tt>rootpw {SSHA}Iogj+Awafoj9FP5IdLVy1DmFaASDw1P5 # secret<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Start up the server to make sure everything is apples</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">sudo /usr/local/etc/rc.d/slapd start<tt>\n</tt>openssl s_client -connect rhnh.net:636 -showcerts<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Load up a schema to hold your address book entries, and here is also an example entry.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"># directory.ldif<tt>\n</tt>dn: dc=rhnh, dc=net<tt>\n</tt>objectClass: top<tt>\n</tt>objectClass: dcObject<tt>\n</tt>objectClass: organization<tt>\n</tt>dc: rhnh<tt>\n</tt>o: Robot Has No Heart<tt>\n</tt><tt>\n</tt>dn: ou=people, dc=rhnh, dc=net<tt>\n</tt>objectClass: top<tt>\n</tt>objectClass: organizationalUnit<tt>\n</tt>ou: people<tt>\n</tt><tt>\n</tt># contact.ldif<tt>\n</tt>dn: cn=Xavier Shay, ou=people, dc=rhnh, dc=net<tt>\n</tt>objectClass: top<tt>\n</tt>objectClass: person<tt>\n</tt>objectClass: organizationalPerson<tt>\n</tt>objectClass: inetOrgPerson<tt>\n</tt>cn: Xavier Shay<tt>\n</tt>gn: Xavier<tt>\n</tt>sn: Shay<tt>\n</tt>mail: contact@rhnh.net<tt>\n</tt>ou: people<tt>\n</tt>mobile: 0400-123-456<tt>\n</tt></pre></td>\n</tr></table>\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">ldapadd -D \'cn=xavier,dc=rhnh,dc=net\' -f directory.ldif -W<tt>\n</tt>ldapsearch -D \'cn=xavier,dc=rhnh,dc=net\' -w -x # Check everything worked<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>To configure Thunderbird to use your address book, go to <code>Edit - Preferences... - Composition - Edit Directories...</code> and follow the bouncing ball. Thunderbird can&#8217;t write to the directory, which is kind of a pain. Maybe you could use Evolution, which I think works. Maybe you could write an app that monitors a drop box and updates your directory for you. Maybe you could assume I&#8217;ve already done what I suggested and wait for me to release it in the very near future.</p>\n\n\n <p>Tested on FreeBSD 6.2-stable</p>\n\n\n <h3>References</h3>\n\n\n <ul>\n <li><a href=\"http://www.onlamp.com/pub/a/onlamp/2003/03/27/ldap_ab.html\">Building an Address Book with OpenLDAP</a></li>\n <li><a href=\"http://islandlinux.org/howto/installing-secure-ldap-openldap-ssl-ubuntu-using-self-signed-certificate\">Installing Secure <span class=\"caps\">LDAP</span> on Ubuntu using a self-signed certificate</a></li>\n <li><a href=\"http://www.openldap.org/doc/admin23/security.html\">OpenLDAP Administrator&#8217;s Guide</a></li>\n </ul>',1,2,NULL,'2007-11-03 06:28:00','2007-11-03 06:25:07','2007-11-04 00:00:43'),(214,'Introducing Clerk Simon','introducing-clerk-simon','Someone sends you an email and you want to add them to your LDAP address book, but your email client doesn\'t support it *cough*thunderbird*cough*. If you think the next best way would be to just forward that email somewhere and have someone else take care of it, then allow me to introduce \"Clerk Simon\":http://code.rhnh.net/clerk_simon/README. He\'s quite attentive when it comes to such matters, and fully certified to boot. Full details at that link, check it out.\r\n\r\n--- bash\r\nbzr co http://code.rhnh.net/clerk_simon/\r\ncd clerk_simon\r\ncp config.sample.yml config.yml # Edit to taste\r\nbin/clerk_simon config.yml\r\n---','<p>Someone sends you an email and you want to add them to your <span class=\"caps\">LDAP</span> address book, but your email client doesn&#8217;t support it <strong>cough</strong>thunderbird<strong>cough</strong>. If you think the next best way would be to just forward that email somewhere and have someone else take care of it, then allow me to introduce <a href=\"http://code.rhnh.net/clerk_simon/README\">Clerk Simon</a>. He&#8217;s quite attentive when it comes to such matters, and fully certified to boot. Full details at that link, check it out.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">bzr co http://code.rhnh.net/clerk_simon/<tt>\n</tt>cd clerk_simon<tt>\n</tt>cp config.sample.yml config.yml # Edit to taste<tt>\n</tt>bin/clerk_simon config.yml<tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-11-03 10:23:00','2007-11-03 10:29:31','2007-11-03 10:29:31'),(216,'Facets patch','facets-patch','--- bash\r\n$ svn log svn://rubyforge.org/var/svn/facets/trunk -r 383 -v\r\n------------------------------------------------------------------------\r\nr383 | transami | 2007-11-03 23:31:54 +1100 (Sat, 03 Nov 2007) | 2 lines\r\nChanged paths:\r\n M /trunk/lib/core/facets/hash/op.rb\r\n M /trunk/test/unit/hash/test_op.rb\r\n\r\nFixed bug in Hash#- Thanks to Xavier Shay.\r\n---\r\n--- ruby\r\nrequire \'facets/hash/op\'\r\n{:a => 1, :b => 2, :c => 3} - [:a, :b] # => {:c => 3}\r\n{:a => 1, :b => 2, :c => 3} - {:a => 1, :b => 99} # => {:b => 2, :c => 3}\r\n---\r\n\r\nIt may be small, but it\'s authentic. In the 2.0.5 gem.','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">$ svn log svn://rubyforge.org/var/svn/facets/trunk -r 383 -v<tt>\n</tt>------------------------------------------------------------------------<tt>\n</tt>r383 | transami | 2007-11-03 23:31:54 +1100 (Sat, 03 Nov 2007) | 2 lines<tt>\n</tt>Changed paths:<tt>\n</tt> M /trunk/lib/core/facets/hash/op.rb<tt>\n</tt> M /trunk/test/unit/hash/test_op.rb<tt>\n</tt><tt>\n</tt>Fixed bug in Hash#- Thanks to Xavier Shay.<tt>\n</tt></pre></td>\n</tr></table>\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">facets/hash/op</span><span class=\"dl\">\'</span></span><tt>\n</tt>{<span class=\"sy\">:a</span> =&gt; <span class=\"i\">1</span>, <span class=\"sy\">:b</span> =&gt; <span class=\"i\">2</span>, <span class=\"sy\">:c</span> =&gt; <span class=\"i\">3</span>} - [<span class=\"sy\">:a</span>, <span class=\"sy\">:b</span>] <span class=\"c\"># =&gt; {:c =&gt; 3}</span><tt>\n</tt>{<span class=\"sy\">:a</span> =&gt; <span class=\"i\">1</span>, <span class=\"sy\">:b</span> =&gt; <span class=\"i\">2</span>, <span class=\"sy\">:c</span> =&gt; <span class=\"i\">3</span>} - {<span class=\"sy\">:a</span> =&gt; <span class=\"i\">1</span>, <span class=\"sy\">:b</span> =&gt; <span class=\"i\">99</span>} <span class=\"c\"># =&gt; {:b =&gt; 2, :c =&gt; 3}</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>It may be small, but it&#8217;s authentic. In the 2.0.5 gem.</p>',1,0,NULL,'2007-11-05 23:21:00','2007-11-05 23:26:34','2007-11-05 23:36:36'),(217,'Maintaining a stable branch','maintaining-a-stable-branch','Part one of my VCS ninja skills program.\r\n\r\nA common scenario for a production application is to have a trunk for development, and a stable branch that is deployed to production. This is what we do at \"RedBubble\":http://www.redbubble.com, and here I share how to complete some common tasks with subversion.\r\n\r\nh3. Push out a new release\r\n\r\nIt might seem like a good idea to merge trunk into stable. Not so! Trunk is the code that you\'ve been working with and testing with, merging it into another branch introduces the risk of either hard conflicts (not so bad - you can fix them) or the scarier Bodgy Merge (technical term) where subversion thinks it has merged everything correctly but hasn\'t. We blow away our stable branch and just copy over trunk. Takes less time, and we\'re more confident in the result. Here\'s an example from our release notes:\r\n\r\n--- bash\r\nsvn delete -m \"Removed previous stable branch\" svn+ssh://example.com/home/svn/branches/stable\r\nsvn copy -m \"Ice T Release - Iteration 2 : trunk to stable (r1234)\" svn+ssh://example.com/home/svn/trunk svn+ssh://example.com/home/svn/branches/stable\r\n---\r\n\r\nWe also tag the release in @tags/@ (just another copy), but to this day we have never checked out one of the tags, so maybe that isn\'t worthwhile. You can always checkout a specific revision anyway.\r\n\r\nh3. Patch a bug fix into stable\r\n\r\nOh noes! Production is broken! Code Red! Hopefully you release often enough that trunk and stable are similar enough that you can apply the same patch to both of them. This is the case 99% of the time for us, so when something is broken we fix it in trunk, then merge the patch across to stable to release.\r\n\r\n--- bash\r\n# trunk fix was r100\r\ncd branches/stable\r\nsvn merge -r99:100 svn+ssh://example.com/home/svn/trunk .\r\nsvn st # Always check!\r\nsvn diff # Always check!\r\nsvn ci -m \"Merge r100 from trunk (my awesome bug fix)\"\r\n---\r\n\r\nThat\'ll get it done, but we don\'t want to be just competent. Ninjas aren\'t just \'competent\'.\r\n\r\n--- ruby\r\n#!/usr/bin/env ruby\r\nARGV.collect {|x| x.to_i }.each do |revision|\r\n cmd = \"svn merge -r#{revision-1}:#{revision} svn+ssh://example.com/home/svn/trunk .\"\r\n puts `#{cmd}`\r\nend\r\n---\r\n\r\nPut that in your bin folder - mine\'s called @rbm@ (RedBubble Merge - yay for obscure shortcuts) - and you can now patch with @rbm 100 105@. It\'s so quick, there have been reports of patches getting merged before they\'re even committed to trunk.\r\n\r\n*UPDATE:* Multi-param version of @rbm@','<p>Part one of my <span class=\"caps\">VCS</span> ninja skills program.</p>\n\n\n <p>A common scenario for a production application is to have a trunk for development, and a stable branch that is deployed to production. This is what we do at <a href=\"http://www.redbubble.com\">RedBubble</a>, and here I share how to complete some common tasks with subversion.</p>\n\n\n <h3>Push out a new release</h3>\n\n\n <p>It might seem like a good idea to merge trunk into stable. Not so! Trunk is the code that you&#8217;ve been working with and testing with, merging it into another branch introduces the risk of either hard conflicts (not so bad &#8211; you can fix them) or the scarier Bodgy Merge (technical term) where subversion thinks it has merged everything correctly but hasn&#8217;t. We blow away our stable branch and just copy over trunk. Takes less time, and we&#8217;re more confident in the result. Here&#8217;s an example from our release notes:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">svn delete -m &quot;Removed previous stable branch&quot; svn+ssh://example.com/home/svn/branches/stable<tt>\n</tt>svn copy -m &quot;Ice T Release - Iteration 2 : trunk to stable (r1234)&quot; svn+ssh://example.com/home/svn/trunk svn+ssh://example.com/home/svn/branches/stable<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>We also tag the release in <code>tags/</code> (just another copy), but to this day we have never checked out one of the tags, so maybe that isn&#8217;t worthwhile. You can always checkout a specific revision anyway.</p>\n\n\n <h3>Patch a bug fix into stable</h3>\n\n\n <p>Oh noes! Production is broken! Code Red! Hopefully you release often enough that trunk and stable are similar enough that you can apply the same patch to both of them. This is the case 99% of the time for us, so when something is broken we fix it in trunk, then merge the patch across to stable to release.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"># trunk fix was r100<tt>\n</tt>cd branches/stable<tt>\n</tt>svn merge -r99:100 svn+ssh://example.com/home/svn/trunk .<tt>\n</tt>svn st # Always check!<tt>\n</tt>svn diff # Always check!<tt>\n</tt>svn ci -m &quot;Merge r100 from trunk (my awesome bug fix)&quot;<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>That&#8217;ll get it done, but we don&#8217;t want to be just competent. Ninjas aren&#8217;t just &#8216;competent&#8217;.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\">#!/usr/bin/env ruby</span><tt>\n</tt><span class=\"pc\">ARGV</span>.collect {|x| x.to_i }.each <span class=\"r\">do</span> |revision|<tt>\n</tt> cmd = <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">svn merge -r</span><span class=\"il\"><span class=\"dl\">#{</span>revision<span class=\"i\">-1</span><span class=\"dl\">}</span></span><span class=\"k\">:</span><span class=\"il\"><span class=\"dl\">#{</span>revision<span class=\"dl\">}</span></span><span class=\"k\"> svn+ssh://example.com/home/svn/trunk .</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> puts <span class=\"sh\"><span class=\"dl\">`</span><span class=\"il\"><span class=\"dl\">#{</span>cmd<span class=\"dl\">}</span></span><span class=\"dl\">`</span></span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Put that in your bin folder &#8211; mine&#8217;s called <code>rbm</code> (RedBubble Merge &#8211; yay for obscure shortcuts) &#8211; and you can now patch with <code>rbm 100 105</code>. It&#8217;s so quick, there have been reports of patches getting merged before they&#8217;re even committed to trunk.</p>\n\n\n <p><strong><span class=\"caps\">UPDATE</span>:</strong> Multi-param version of <code>rbm</code></p>',1,0,NULL,'2007-11-06 00:47:00','2007-11-06 00:44:01','2007-12-21 02:27:56'),(224,'Array#collapse','array-collapse','--- ruby\r\nmodule CoreExtensions\r\n module Array\r\n def collapse\r\n self.inject([]) do |a, v|\r\n if existing = a.find {|o| o.eql?(v)}\r\n yield(existing, v)\r\n else\r\n a << v\r\n end\r\n a\r\n end\r\n end\r\n end\r\nend\r\n\r\nArray.send(:include, CoreExtensions::Array)\r\n---\r\n\r\nKind of handy for reporting, where you need to collapse line items into a summary. This example may make it clear:\r\n\r\n--- ruby\r\nclass Item < Struct.new(:code, :quantity)\r\n def eql?(b)\r\n code == b.code\r\n end\r\n\r\n alias_method :==, :eql?\r\n\r\n def hash\r\n code.hash\r\n end\r\n\r\n def to_s\r\n \"#{code} - #{quantity}\"\r\n end\r\nend \r\n\r\nsummary = [Item.new(\"a\", 1), Item.new(\"a\", 2), Item.new(\"b\", 5)].collapse {|a, b| a.quantity += b.quantity}\r\nsummary.collect(&:to_s) # => [\"a - 3\", \"b - 5\"]\r\n---','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">module</span> <span class=\"cl\">CoreExtensions</span><tt>\n</tt> <span class=\"r\">module</span> <span class=\"cl\">Array</span><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">collapse</span><tt>\n</tt> <span class=\"pc\">self</span>.inject([]) <span class=\"r\">do</span> |a, v|<tt>\n</tt> <span class=\"r\">if</span> existing = a.find {|o| o.eql?(v)}<tt>\n</tt> <span class=\"r\">yield</span>(existing, v)<tt>\n</tt> <span class=\"r\">else</span><tt>\n</tt> a &lt;&lt; v<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> a<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt><span class=\"co\">Array</span>.send(<span class=\"sy\">:include</span>, <span class=\"co\">CoreExtensions</span>::<span class=\"co\">Array</span>)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Kind of handy for reporting, where you need to collapse line items into a summary. This example may make it clear:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">class</span> <span class=\"cl\">Item</span> &lt; <span class=\"co\">Struct</span>.new(<span class=\"sy\">:code</span>, <span class=\"sy\">:quantity</span>)<tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">eql?</span>(b)<tt>\n</tt> code == b.code<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> alias_method <span class=\"sy\">:==</span>, <span class=\"sy\">:eql?</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">hash</span><tt>\n</tt> code.hash<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">to_s</span><tt>\n</tt> <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>code<span class=\"dl\">}</span></span><span class=\"k\"> - </span><span class=\"il\"><span class=\"dl\">#{</span>quantity<span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span> <tt>\n</tt><tt>\n</tt>summary = [<span class=\"co\">Item</span>.new(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">a</span><span class=\"dl\">&quot;</span></span>, <span class=\"i\">1</span>), <span class=\"co\">Item</span>.new(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">a</span><span class=\"dl\">&quot;</span></span>, <span class=\"i\">2</span>), <span class=\"co\">Item</span>.new(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">b</span><span class=\"dl\">&quot;</span></span>, <span class=\"i\">5</span>)].collapse {|a, b| a.quantity += b.quantity}<tt>\n</tt>summary.collect(&amp;<span class=\"sy\">:to_s</span>) <span class=\"c\"># =&gt; [&quot;a - 3&quot;, &quot;b - 5&quot;]</span><tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-11-13 06:10:00','2007-11-13 06:09:33','2007-11-15 06:01:00'),(234,'Hash#translate_keys_and_values','hash-translate_keys_and_values','--- ruby\r\nmodule CoreExtensions\r\n module Hash \r\n def translate_keys_and_values(&block)\r\n inject({}) {|a, (key, value)| a.update(block.call(key) => block.call(value))}\r\n end\r\n end\r\nend\r\n\r\nHash.send(:include, CoreExtensions::Hash)\r\n---\r\n\r\nIt\'s like @symbolize_keys@ but a bit more flexible. It calls the block for every key and value in the hash. Of course you could tune it just do keys or values if you wanted. I do not want!\r\n\r\n--- ruby\r\n{\"1\" => \"2\"}.translate_keys_and_values(&:to_i) # => {1 => 2}\r\n{1 => 2}.translate_keys_and_values {|x| x + 1 } # => {2 => 3}\r\n---','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">module</span> <span class=\"cl\">CoreExtensions</span><tt>\n</tt> <span class=\"r\">module</span> <span class=\"cl\">Hash</span> <tt>\n</tt> <span class=\"r\">def</span> <span class=\"fu\">translate_keys_and_values</span>(&amp;block)<tt>\n</tt> inject({}) {|a, (key, value)| a.update(block.call(key) =&gt; block.call(value))}<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt><span class=\"co\">Hash</span>.send(<span class=\"sy\">:include</span>, <span class=\"co\">CoreExtensions</span>::<span class=\"co\">Hash</span>)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>It&#8217;s like <code>symbolize_keys</code> but a bit more flexible. It calls the block for every key and value in the hash. Of course you could tune it just do keys or values if you wanted. I do not want!</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">{<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">1</span><span class=\"dl\">&quot;</span></span> =&gt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">2</span><span class=\"dl\">&quot;</span></span>}.translate_keys_and_values(&amp;<span class=\"sy\">:to_i</span>) <span class=\"c\"># =&gt; {1 =&gt; 2}</span><tt>\n</tt>{<span class=\"i\">1</span> =&gt; <span class=\"i\">2</span>}.translate_keys_and_values {|x| x + <span class=\"i\">1</span> } <span class=\"c\"># =&gt; {2 =&gt; 3}</span><tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-11-15 05:55:00','2007-11-15 05:59:49','2007-11-15 06:02:33'),(263,'Things that aren\'t subversion','things-that-aren-t-subversion','\"Here are the slides\":http://rhnh.net/assets/2007/11/27/not-subversion.pdf for the talk I gave at the Melbourne Ruby Meetup last Thursday night. It was a little bit rambling, but my basic point was: You should try using bazaar instead of subversion because it\'s more awesome. Number one question was \"so should I use bazaar or git?\", to which I unfortunately don\'t have a good answer. I personally haven\'t used either enough to give an unequivocal recommendation, and there are heavyweights in both corners (ubuntu, linux kernel). My initial impression is bazaar is easier, git more powerful. There are also other options such as darcs and mecurial.\r\n\r\nFor the curious, I\'d say start with bazaar because it has the smallest learning curve from svn - see the slides. It seem that most non-svn ruby projects are on git, so you\'ll get to know that eventually :)','<p><a href=\"http://rhnh.net/assets/2007/11/27/not-subversion.pdf\">Here are the slides</a> for the talk I gave at the Melbourne Ruby Meetup last Thursday night. It was a little bit rambling, but my basic point was: You should try using bazaar instead of subversion because it&#8217;s more awesome. Number one question was &#8220;so should I use bazaar or git?&#8221;, to which I unfortunately don&#8217;t have a good answer. I personally haven&#8217;t used either enough to give an unequivocal recommendation, and there are heavyweights in both corners (ubuntu, linux kernel). My initial impression is bazaar is easier, git more powerful. There are also other options such as darcs and mecurial.</p>\n\n\n <p>For the curious, I&#8217;d say start with bazaar because it has the smallest learning curve from svn &#8211; see the slides. It seem that most non-svn ruby projects are on git, so you&#8217;ll get to know that eventually :)</p>',1,1,NULL,'2007-11-27 06:29:00','2007-11-27 06:37:49','2007-11-27 06:37:59'),(283,'exception_notifiable and ruby 1.8.6 p110','exception_notifiable-and-ruby-1-8-6-p110','ruby 1.8.6 p110 has recently come out in ports. If you\'re using the exception_notifiable plugin to let you know about errors, make sure you update it to at least r8191, otherwise it will break when you update ruby. And you won\'t know about it, because it can\'t email you.','<p>ruby 1.8.6 p110 has recently come out in ports. If you&#8217;re using the exception_notifiable plugin to let you know about errors, make sure you update it to at least r8191, otherwise it will break when you update ruby. And you won&#8217;t know about it, because it can&#8217;t email you.</p>',1,0,NULL,'2007-12-07 06:20:00','2007-12-07 06:22:11','2007-12-07 06:22:39'),(298,'Tiny doc patch wins hearts','tiny-doc-patch-wins-hearts','Rails patch accepted after just 44 minutes: \"r8379\":http://dev.rubyonrails.org/changeset/8379\r\n\r\nA result of moving our app from preview 1-ish on to 2-stable this morning. Only other issues were a test that was expecting a ProtectedAttributeAssignmentError - now the attribute just doesn\'t get set (a good change), and some small changes where we were doing stupid things with view paths.','<p>Rails patch accepted after just 44 minutes: <a href=\"http://dev.rubyonrails.org/changeset/8379\">r8379</a></p>\n\n\n <p>A result of moving our app from preview 1-ish on to 2-stable this morning. Only other issues were a test that was expecting a ProtectedAttributeAssignmentError &#8211; now the attribute just doesn&#8217;t get set (a good change), and some small changes where we were doing stupid things with view paths.</p>',1,0,NULL,'2007-12-13 05:41:00','2007-12-13 05:44:41','2007-12-13 05:44:55'),(301,'Logging SQL statistics in rails','logging-sql-statistics-in-rails','When your sysadmin comes to you -whinging- with a valid concern that your app is reading 60 gazillion records from the DB, you kinda wish you had a bit more information than % time spent in the DB. So I wrote a plugin that counts both the number of selects/updates/inserts/deletes and also the number of records affected.\r\n\r\n--- bash\r\nbzr checkout http://code.rhnh.net/sql_counter/ vendor/plugins/sql_counter\r\n---\r\n\r\nThat does the counting, you need to decide how to log it. I am personally quite partial to adding it to the request log line, thus getting stats per request:\r\n\r\n--- ruby\r\n# vendor/rails/actionpack/lib/action_controller/benchmarking.rb:75\r\nlog_message << \" | Select Records: #{ActiveRecord::Base.connection.select_record_count}\"\r\nlog_message << \" | Selects: #{ActiveRecord::Base.connection.select_count}\"\r\n\r\nActiveRecord::Base.connection.reset_counters!\r\n---\r\n\r\nDon\'t forget the last line, otherwise you get culmulative numbers. That may be handy, but I doubt it. We\'re only logging selects because that\'s all we care about at the moment. I am sure this will change in time.','<p>When your sysadmin comes to you <del>whinging</del> with a valid concern that your app is reading 60 gazillion records from the DB, you kinda wish you had a bit more information than % time spent in the DB. So I wrote a plugin that counts both the number of selects/updates/inserts/deletes and also the number of records affected.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">bzr checkout http://code.rhnh.net/sql_counter/ vendor/plugins/sql_counter<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>That does the counting, you need to decide how to log it. I am personally quite partial to adding it to the request log line, thus getting stats per request:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\"># vendor/rails/actionpack/lib/action_controller/benchmarking.rb:75</span><tt>\n</tt>log_message &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\"> | Select Records: </span><span class=\"il\"><span class=\"dl\">#{</span><span class=\"co\">ActiveRecord</span>::<span class=\"co\">Base</span>.connection.select_record_count<span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span><tt>\n</tt>log_message &lt;&lt; <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\"> | Selects: </span><span class=\"il\"><span class=\"dl\">#{</span><span class=\"co\">ActiveRecord</span>::<span class=\"co\">Base</span>.connection.select_count<span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span><tt>\n</tt><tt>\n</tt><span class=\"co\">ActiveRecord</span>::<span class=\"co\">Base</span>.connection.reset_counters!<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Don&#8217;t forget the last line, otherwise you get culmulative numbers. That may be handy, but I doubt it. We&#8217;re only logging selects because that&#8217;s all we care about at the moment. I am sure this will change in time.</p>',1,0,NULL,'2007-12-13 09:40:00','2007-12-13 09:40:30','2007-12-13 09:41:57'),(303,'Formatting ruby hashes in VIM','formatting-ruby-hashes-in-vim','I\'ve been meaning to write this script for a while. If you\'re anal about your whitespace (like I), you\'ll often pretty up your ruby hashes to make them easy to read by adding a bit of whitespace to the keys before the =>. I wrote a ruby script to do this automatically!\r\n\r\n--- ruby\r\n#!/usr/bin/env ruby\r\n\r\n# format_hash.rb\r\n#\r\n# Formats ruby hashes\r\n# a => 1\r\n# ab => 2\r\n# abc => 3\r\n#\r\n# becomes\r\n# a => 1\r\n# ab => 2\r\n# abc => 3\r\n#\r\n# http://rhnh.net\r\n\r\nlines = []\r\nwhile line = gets\r\n lines &lt;&lt; line\r\nend\r\n\r\nindent = lines.first.index(/[^\\s]/)\r\n\r\n# Massage into an array of [key, value]\r\nlines.collect! {|line| \r\n line.split(\'=>\').collect {|line| \r\n line.gsub(/^\\s*/, \'\').gsub(/\\s*$/, \'\') \r\n }\r\n}\r\n\r\nmax_key_length = lines.collect {|line| line[0].length}.max\r\n\r\n# Pad each key with whitespace to match length of longest key\r\nlines.collect! {|line|\r\n line[0] = \"%#{indent}s%-#{max_key_length}s\" % [\'\', line[0]]\r\n line.join(\' => \')\r\n}\r\n\r\nprint lines.join(\"\\n\")\r\n---\r\n\r\nPut that in your path, then in VIM you can run the following command to format the current selection:\r\n\r\n<macro:code>\r\n:\'&lt;,\'>!format_hash.rb\r\n# Or map F2 to do it for you...\r\n:vmap &lt;F2> !format_hash.rb&lt;CR>\r\n---','<p>I&#8217;ve been meaning to write this script for a while. If you&#8217;re anal about your whitespace (like I), you&#8217;ll often pretty up your ruby hashes to make them easy to read by adding a bit of whitespace to the keys before the =&gt;. I wrote a ruby script to do this automatically!</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt>31<tt>\n</tt>32<tt>\n</tt>33<tt>\n</tt>34<tt>\n</tt>35<tt>\n</tt>36<tt>\n</tt>37<tt>\n</tt>38<tt>\n</tt>39<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\">#!/usr/bin/env ruby</span><tt>\n</tt><tt>\n</tt><span class=\"c\"># format_hash.rb</span><tt>\n</tt><span class=\"c\">#</span><tt>\n</tt><span class=\"c\"># Formats ruby hashes</span><tt>\n</tt><span class=\"c\"># a =&gt; 1</span><tt>\n</tt><span class=\"c\"># ab =&gt; 2</span><tt>\n</tt><span class=\"c\"># abc =&gt; 3</span><tt>\n</tt><span class=\"c\">#</span><tt>\n</tt><span class=\"c\"># becomes</span><tt>\n</tt><span class=\"c\"># a =&gt; 1</span><tt>\n</tt><span class=\"c\"># ab =&gt; 2</span><tt>\n</tt><span class=\"c\"># abc =&gt; 3</span><tt>\n</tt><span class=\"c\">#</span><tt>\n</tt><span class=\"c\"># http://rhnh.net</span><tt>\n</tt><tt>\n</tt>lines = []<tt>\n</tt><span class=\"r\">while</span> line = gets<tt>\n</tt> lines &amp;lt;&amp;lt; line<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>indent = lines.first.index(<span class=\"rx\"><span class=\"dl\">/</span><span class=\"k\">[^</span><span class=\"ch\">\\s</span><span class=\"k\">]</span><span class=\"dl\">/</span></span>)<tt>\n</tt><tt>\n</tt><span class=\"c\"># Massage into an array of [key, value]</span><tt>\n</tt>lines.collect! {|line| <tt>\n</tt> line.split(<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">=&gt;</span><span class=\"dl\">\'</span></span>).collect {|line| <tt>\n</tt> line.gsub(<span class=\"rx\"><span class=\"dl\">/</span><span class=\"k\">^</span><span class=\"ch\">\\s</span><span class=\"k\">*</span><span class=\"dl\">/</span></span>, <span class=\"s\"><span class=\"dl\">\'</span><span class=\"dl\">\'</span></span>).gsub(<span class=\"rx\"><span class=\"dl\">/</span><span class=\"ch\">\\s</span><span class=\"k\">*$</span><span class=\"dl\">/</span></span>, <span class=\"s\"><span class=\"dl\">\'</span><span class=\"dl\">\'</span></span>) <tt>\n</tt> }<tt>\n</tt>}<tt>\n</tt><tt>\n</tt>max_key_length = lines.collect {|line| line[<span class=\"i\">0</span>].length}.max<tt>\n</tt><tt>\n</tt><span class=\"c\"># Pad each key with whitespace to match length of longest key</span><tt>\n</tt>lines.collect! {|line|<tt>\n</tt> line[<span class=\"i\">0</span>] = <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">%</span><span class=\"il\"><span class=\"dl\">#{</span>indent<span class=\"dl\">}</span></span><span class=\"k\">s%-</span><span class=\"il\"><span class=\"dl\">#{</span>max_key_length<span class=\"dl\">}</span></span><span class=\"k\">s</span><span class=\"dl\">&quot;</span></span> % [<span class=\"s\"><span class=\"dl\">\'</span><span class=\"dl\">\'</span></span>, line[<span class=\"i\">0</span>]]<tt>\n</tt> line.join(<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\"> =&gt; </span><span class=\"dl\">\'</span></span>)<tt>\n</tt>}<tt>\n</tt><tt>\n</tt>print lines.join(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"ch\">\\n</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Put that in your path, then in <span class=\"caps\">VIM</span> you can run the following command to format the current selection:</p>\n\n\n<pre><code>:\'&lt;,\'>!format_hash.rb\n# Or map F2 to do it for you...\n:vmap &lt;F2> !format_hash.rb&lt;CR>\n</code></pre>',1,0,NULL,'2007-12-13 12:19:00','2007-12-13 12:25:29','2007-12-17 04:11:19'),(327,'Making cerberus more fun','making-cerberus-more-fun','!http://img263.imageshack.us/img263/4033/mc1842oj.jpg!\r\n\r\n_And throughout the lands of the Greek empire, he was known and feared as Cerberus, the original three-headed party dog from hell_\r\n\r\nHere is patch to the \"cerberus\":http://cerberus.rubyforge.org/ campfire publisher that enables it to prepend a funny image to its messages. Submitted to core, guess it depends on how much of a sense of humour the author has.\r\n\r\nSomeone let \"GIS\":http://images.google.com/ know it\'s about to be thrashed by queries for train wrecks and hi fives.\r\n\r\n--- diff\r\nIndex: lib/cerberus/config.example.yml\r\n===================================================================\r\n--- lib/cerberus/config.example.yml (revision 167)\r\n+++ lib/cerberus/config.example.yml (working copy)\r\n@@ -17,6 +17,11 @@\r\n # channel: cerberus\r\n # campfire:\r\n # url: http://someemail:password@cerberustool.campfirenow.com/room/51660\r\n+# preamble: \r\n+# # Posts content before the main message based on the build state. Perfect for amusing images.\r\n+# # Valid states are: setup, broken, failed, revival, successful\r\n+# broken: http://mydomain.com/broken.jpg\r\n+# revival: http://mydomain.com/fixed.jpg\r\n # rss:\r\n # file: /usr/www/rss.xml\r\n #builder:\r\n@@ -26,4 +31,4 @@\r\n #hook:\r\n # rcov:\r\n # on_event: successful, setup #by default - run hook for any state\r\n-# action: \'export CERBERUS_HOME=/home/anatol && sudo chown www-data -R /home/anatol/cerberus && rcov\' #Add here any hook you want\r\n\\ No newline at end of file\r\n+# action: \'export CERBERUS_HOME=/home/anatol && sudo chown www-data -R /home/anatol/cerberus && rcov\' #Add here any hook you want\r\nIndex: lib/cerberus/publisher/campfire.rb\r\n===================================================================\r\n--- lib/cerberus/publisher/campfire.rb (revision 167)\r\n+++ lib/cerberus/publisher/campfire.rb (working copy)\r\n@@ -3,8 +3,10 @@\r\n class Cerberus::Publisher::Campfire < Cerberus::Publisher::Base\r\n def self.publish(state, manager, options)\r\n url = options[:publisher, :campfire, :url]\r\n+ preamble = options[:publisher, :campfire, :preamble, state.current_state]\r\n \r\n subject,body = Cerberus::Publisher::Base.formatted_message(state, manager, options)\r\n+ Marshmallow.say(url, preamble) unless preamble.nil?\r\n Marshmallow.say(url, subject)\r\n Marshmallow.paste(url, body)\r\n end\r\n---\r\n\r\nProps to \"grant\":http://grantbissett.com/ for the inspiration and finding of the title photo','<p><img src=\"http://img263.imageshack.us/img263/4033/mc1842oj.jpg\" alt=\"\" /></p>\n\n\n <p><em>And throughout the lands of the Greek empire, he was known and feared as Cerberus, the original three-headed party dog from hell</em></p>\n\n\n <p>Here is patch to the <a href=\"http://cerberus.rubyforge.org/\">cerberus</a> campfire publisher that enables it to prepend a funny image to its messages. Submitted to core, guess it depends on how much of a sense of humour the author has.</p>\n\n\n <p>Someone let <a href=\"http://images.google.com/\"><span class=\"caps\">GIS</span></a> know it&#8217;s about to be thrashed by queries for train wrecks and hi fives.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt>31<tt>\n</tt>32<tt>\n</tt>33<tt>\n</tt>34<tt>\n</tt>35<tt>\n</tt>36<tt>\n</tt>37<tt>\n</tt>38<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">Index: lib/cerberus/config.example.yml<tt>\n</tt>===================================================================<tt>\n</tt>--- lib/cerberus/config.example.yml (revision 167)<tt>\n</tt>+++ lib/cerberus/config.example.yml (working copy)<tt>\n</tt>@@ -17,6 +17,11 @@<tt>\n</tt> # channel: cerberus<tt>\n</tt> # campfire:<tt>\n</tt> # url: http://someemail:password@cerberustool.campfirenow.com/room/51660<tt>\n</tt>+# preamble: <tt>\n</tt>+# # Posts content before the main message based on the build state. Perfect for amusing images.<tt>\n</tt>+# # Valid states are: setup, broken, failed, revival, successful<tt>\n</tt>+# broken: http://mydomain.com/broken.jpg<tt>\n</tt>+# revival: http://mydomain.com/fixed.jpg<tt>\n</tt> # rss:<tt>\n</tt> # file: /usr/www/rss.xml<tt>\n</tt> #builder:<tt>\n</tt>@@ -26,4 +31,4 @@<tt>\n</tt> #hook:<tt>\n</tt> # rcov:<tt>\n</tt> # on_event: successful, setup #by default - run hook for any state<tt>\n</tt>-# action: \'export CERBERUS_HOME=/home/anatol &amp;&amp; sudo chown www-data -R /home/anatol/cerberus &amp;&amp; rcov\' #Add here any hook you want<tt>\n</tt>\\ No newline at end of file<tt>\n</tt>+# action: \'export CERBERUS_HOME=/home/anatol &amp;&amp; sudo chown www-data -R /home/anatol/cerberus &amp;&amp; rcov\' #Add here any hook you want<tt>\n</tt>Index: lib/cerberus/publisher/campfire.rb<tt>\n</tt>===================================================================<tt>\n</tt>--- lib/cerberus/publisher/campfire.rb (revision 167)<tt>\n</tt>+++ lib/cerberus/publisher/campfire.rb (working copy)<tt>\n</tt>@@ -3,8 +3,10 @@<tt>\n</tt> class Cerberus::Publisher::Campfire &lt; Cerberus::Publisher::Base<tt>\n</tt> def self.publish(state, manager, options)<tt>\n</tt> url = options[:publisher, :campfire, :url]<tt>\n</tt>+ preamble = options[:publisher, :campfire, :preamble, state.current_state]<tt>\n</tt> <tt>\n</tt> subject,body = Cerberus::Publisher::Base.formatted_message(state, manager, options)<tt>\n</tt>+ Marshmallow.say(url, preamble) unless preamble.nil?<tt>\n</tt> Marshmallow.say(url, subject)<tt>\n</tt> Marshmallow.paste(url, body)<tt>\n</tt> end<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Props to <a href=\"http://grantbissett.com/\">grant</a> for the inspiration and finding of the title photo</p>',1,0,NULL,'2007-12-17 04:25:00','2007-12-17 04:24:44','2007-12-17 04:58:46'),(330,'Rails devs, reclaim your harddrive','rails-devs-reclaim-your-harddrive','--- bash\r\ncd code-dir\r\nfind . | egrep \"(development|test)\\\\.log\" | grep -v .svn | xargs rm\r\n---\r\n\r\nI\'d forgotten to clear out my logs for a long while. This found me 9.5Gb!','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">cd code-dir<tt>\n</tt>find . | egrep &quot;(development|test)\\\\.log&quot; | grep -v .svn | xargs rm<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>I&#8217;d forgotten to clear out my logs for a long while. This found me 9.5Gb!</p>',1,2,NULL,'2007-12-17 21:36:00','2007-12-17 21:37:39','2007-12-17 21:37:39'),(333,'Understanding the Y Combinator','understanding-the-y-combinator','Many people have written about this, it still took me a long while to figure it out. It\'s a bit of a mindfuck. So here is me rehashing what other people have said in a way that makes sense to me.\r\n\r\nh3. The Problem\r\n\r\nI\'ll start with the same example of hash autovivication (that\'s what perl calls it) used by \"Charles Duan in his article\":http://www.eecs.harvard.edu/~cduan/technical/ruby/ycombinator.shtml.\r\nWe want the following code to work:\r\n\r\n--- ruby\r\nhash = Hash.new {|h, k| h[k] = default } # We need to implement default later, read on!\r\nhash[1][2][3][4][5] = true\r\nhash # => {1=>{2=>{3=>{4=>{5=>true}}}}}\r\n---\r\n\r\nTo do this, we need to specify an appropriate default value for the hash. If we set the default to @{}@, we only get one level of autovivication.\r\n\r\n--- ruby\r\nhash = Hash.new {|h, k| h[k] = {} }\r\nhash[1] # => {} \r\nhash[1][2] # => nil\r\n---\r\n\r\nClearly we need a recursive function to support infinite depth, which we can do with a normal ruby method.\r\n\r\n--- ruby\r\ndef make_hash\r\n Hash.new {|h, k| h[k] = make_hash }\r\nend \r\n\r\nhash = make_hash\r\nhash[1][2][3][4][5] # => {}\r\n---\r\n\r\nThe problem here is we\'ve introduced a new method into the namespace (@make_hash@), which isn\'t really necessary. The Y Combinator allows us to achieve the same result, *without* a named method or variable.\r\n\r\nh3. The Solution\r\n\r\nWe can avoid the need for a named method by wrapping the Hash creation code in an anonymous lambda that passes in the callback as an argument.\r\n\r\n--- ruby\r\nlambda {|callback| Hash.new {|h, k| h[k] = callback.call }}.call(some_callback)\r\n---\r\n\r\nWe just need a way to pass in a callback function that is the same as the initial function. If you try to copy and paste in the hash maker code, you\'ll find it doesn\'t quite work because we then need a way to get a callback for that callback.\r\n\r\n--- ruby\r\nlambda {|callback| \r\n Hash.new {|h, k| h[k] = callback.call }\r\n}.call(\r\n lambda { \r\n Hash.new {|h, k| h[k] = callback.call }\r\n }\r\n}) # fails because the second callback isn\'t defined\r\n---\r\n\r\nBut we\'re getting closer. What if we pass in our initial callback function as a parameter to itself? Then it will know how to call itself over and over again. This is pretty tricky - the first example illustrates the concept using a named method for clarity, the second example is what we actually want.\r\n\r\n--- ruby\r\n# With named method\r\ndef make_hash(x) \r\n Hash.new {|h,k| h[k] = x.call(x)}\r\nend \r\nhash = make_hash(method(:make_hash))\r\n\r\n# With lambdas\r\nhash = lambda {|callback| \r\n Hash.new {|h, k| h[k] = callback.call(callback) }\r\n}.call(\r\n lambda {|callback| \r\n Hash.new {|h, k| h[k] = callback.call(callback) }\r\n })\r\nhash[1][2][3][4][5] # => {}, hooray!\r\n---\r\n\r\n*And that\'s really the guts of it.* If you understand that you\'ve pretty much got it. From here on in it\'s just extra credit.\r\n\r\nh3. Making it DRY\r\n\r\nThe previous code repeats itself somewhat - you copy and paste the hash maker function into two spots. Basically, the code is @hash = x.call(x)@. So let\'s use another lambda to express it as such.\r\n\r\n--- ruby\r\nlambda {|x| x.call(x) }.call(\r\n lambda {|callback| \r\n Hash.new {|h, k| h[k] = callback.call(callback) }\r\n })\r\n---\r\n\r\nh3. Making it work for callbacks with an arbitrary number of parameters\r\n\r\nBy passing in the callback to itself, we\'re restricting ourselves to a callback with no parameters. You\'ll notice we\'re not able to pass in any parameters to the hash maker above. As you may have guessed, we add another level of abstraction with a lambda that passes in a @callback_maker@ function.\r\n\r\n--- ruby\r\nhash = lambda {|x| x.call(x) }.call(lambda {|callback_maker| \r\n lambda {|*args| \r\n callback = callback_maker.call(callback_maker)\r\n Hash.new {|h, k| h[k] = callback.call(*args) }\r\n }\r\n}).call(\"an argument!\")\r\n---\r\n\r\nSo yes, that example is kind of useless because we don\'t use the arguments. Let\'s try something a bit meatier, say a factorial function.\r\n\r\n--- ruby\r\nlambda {|x| x.call(x) }.call(lambda {|callback_maker| \r\n lambda {|*args| \r\n callback = callback_maker.call(callback_maker)\r\n v = args.first\r\n return v == 1 ? 1 : v * callback.call(v - 1)\r\n }\r\n}).call(5) # => 120\r\n---\r\n\r\nh3. Making it generic and pretty\r\n\r\n--- ruby\r\ndef y_combinator(&generator)\r\n lambda {|x| x.call(x) }.call(lambda {|callback_maker| \r\n lambda {|*args| \r\n callback = callback_maker.call(callback_maker)\r\n generator.call(callback).call(*args)\r\n }\r\n })\r\nend\r\n\r\ny_combinator {|callback|\r\n lambda {|v|\r\n return v == 1 ? 1 : v * callback.call(v - 1)\r\n }\r\n}.call(5) # => 120\r\nend\r\n---\r\n\r\nAnd let\'s make it a bit less ugly by doing what \"Tom Mortel did\":http://weblog.raganwald.com/2007/02/guest-blogger-tom-moertel-derives-y.html and using [] instead of call (they\'re equivalent), and moving the callback_maker inline.\r\n\r\n--- ruby\r\ndef y_combinator(&f)\r\n lambda {|x| x[x] } [\r\n lambda {|maker| lambda {|*args| f[maker[maker]][*args] }}\r\n ]\r\nend\r\n---\r\n\r\nThus ends my exploration of the Y Combinator. Practically useless in any language you\'d be using today, but hey, don\'t you feel smarter?\r\n\r\n*UPDATE:* Added dmh\'s suggestion from the comments.','<p>Many people have written about this, it still took me a long while to figure it out. It&#8217;s a bit of a mindfuck. So here is me rehashing what other people have said in a way that makes sense to me.</p>\n\n\n <h3>The Problem</h3>\n\n\n <p>I&#8217;ll start with the same example of hash autovivication (that&#8217;s what perl calls it) used by <a href=\"http://www.eecs.harvard.edu/~cduan/technical/ruby/ycombinator.shtml\">Charles Duan in his article</a>.\nWe want the following code to work:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">hash = <span class=\"co\">Hash</span>.new {|h, k| h[k] = default } <span class=\"c\"># We need to implement default later, read on!</span><tt>\n</tt>hash[<span class=\"i\">1</span>][<span class=\"i\">2</span>][<span class=\"i\">3</span>][<span class=\"i\">4</span>][<span class=\"i\">5</span>] = <span class=\"pc\">true</span><tt>\n</tt>hash <span class=\"c\"># =&gt; {1=&gt;{2=&gt;{3=&gt;{4=&gt;{5=&gt;true}}}}}</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>To do this, we need to specify an appropriate default value for the hash. If we set the default to <code>{}</code>, we only get one level of autovivication.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">hash = <span class=\"co\">Hash</span>.new {|h, k| h[k] = {} }<tt>\n</tt>hash[<span class=\"i\">1</span>] <span class=\"c\"># =&gt; {} </span><tt>\n</tt>hash[<span class=\"i\">1</span>][<span class=\"i\">2</span>] <span class=\"c\"># =&gt; nil</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Clearly we need a recursive function to support infinite depth, which we can do with a normal ruby method.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">make_hash</span><tt>\n</tt> <span class=\"co\">Hash</span>.new {|h, k| h[k] = make_hash }<tt>\n</tt><span class=\"r\">end</span> <tt>\n</tt><tt>\n</tt>hash = make_hash<tt>\n</tt>hash[<span class=\"i\">1</span>][<span class=\"i\">2</span>][<span class=\"i\">3</span>][<span class=\"i\">4</span>][<span class=\"i\">5</span>] <span class=\"c\"># =&gt; {}</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>The problem here is we&#8217;ve introduced a new method into the namespace (<code>make_hash</code>), which isn&#8217;t really necessary. The Y Combinator allows us to achieve the same result, <strong>without</strong> a named method or variable.</p>\n\n\n <h3>The Solution</h3>\n\n\n <p>We can avoid the need for a named method by wrapping the Hash creation code in an anonymous lambda that passes in the callback as an argument.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">lambda {|callback| <span class=\"co\">Hash</span>.new {|h, k| h[k] = callback.call }}.call(some_callback)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>We just need a way to pass in a callback function that is the same as the initial function. If you try to copy and paste in the hash maker code, you&#8217;ll find it doesn&#8217;t quite work because we then need a way to get a callback for that callback.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">lambda {|callback| <tt>\n</tt> <span class=\"co\">Hash</span>.new {|h, k| h[k] = callback.call }<tt>\n</tt>}.call(<tt>\n</tt> lambda { <tt>\n</tt> <span class=\"co\">Hash</span>.new {|h, k| h[k] = callback.call }<tt>\n</tt> }<tt>\n</tt>}) <span class=\"c\"># fails because the second callback isn\'t defined</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>But we&#8217;re getting closer. What if we pass in our initial callback function as a parameter to itself? Then it will know how to call itself over and over again. This is pretty tricky &#8211; the first example illustrates the concept using a named method for clarity, the second example is what we actually want.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\"># With named method</span><tt>\n</tt><span class=\"r\">def</span> <span class=\"fu\">make_hash</span>(x) <tt>\n</tt> <span class=\"co\">Hash</span>.new {|h,k| h[k] = x.call(x)}<tt>\n</tt><span class=\"r\">end</span> <tt>\n</tt>hash = make_hash(method(<span class=\"sy\">:make_hash</span>))<tt>\n</tt><tt>\n</tt><span class=\"c\"># With lambdas</span><tt>\n</tt>hash = lambda {|callback| <tt>\n</tt> <span class=\"co\">Hash</span>.new {|h, k| h[k] = callback.call(callback) }<tt>\n</tt>}.call(<tt>\n</tt> lambda {|callback| <tt>\n</tt> <span class=\"co\">Hash</span>.new {|h, k| h[k] = callback.call(callback) }<tt>\n</tt> })<tt>\n</tt>hash[<span class=\"i\">1</span>][<span class=\"i\">2</span>][<span class=\"i\">3</span>][<span class=\"i\">4</span>][<span class=\"i\">5</span>] <span class=\"c\"># =&gt; {}, hooray!</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p><strong>And that&#8217;s really the guts of it.</strong> If you understand that you&#8217;ve pretty much got it. From here on in it&#8217;s just extra credit.</p>\n\n\n <h3>Making it <span class=\"caps\">DRY</span></h3>\n\n\n <p>The previous code repeats itself somewhat &#8211; you copy and paste the hash maker function into two spots. Basically, the code is <code>hash = x.call(x)</code>. So let&#8217;s use another lambda to express it as such.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">lambda {|x| x.call(x) }.call(<tt>\n</tt> lambda {|callback| <tt>\n</tt> <span class=\"co\">Hash</span>.new {|h, k| h[k] = callback.call(callback) }<tt>\n</tt> })<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3>Making it work for callbacks with an arbitrary number of parameters</h3>\n\n\n <p>By passing in the callback to itself, we&#8217;re restricting ourselves to a callback with no parameters. You&#8217;ll notice we&#8217;re not able to pass in any parameters to the hash maker above. As you may have guessed, we add another level of abstraction with a lambda that passes in a <code>callback_maker</code> function.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">hash = lambda {|x| x.call(x) }.call(lambda {|callback_maker| <tt>\n</tt> lambda {|*args| <tt>\n</tt> callback = callback_maker.call(callback_maker)<tt>\n</tt> <span class=\"co\">Hash</span>.new {|h, k| h[k] = callback.call(*args) }<tt>\n</tt> }<tt>\n</tt>}).call(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">an argument!</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>So yes, that example is kind of useless because we don&#8217;t use the arguments. Let&#8217;s try something a bit meatier, say a factorial function.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">lambda {|x| x.call(x) }.call(lambda {|callback_maker| <tt>\n</tt> lambda {|*args| <tt>\n</tt> callback = callback_maker.call(callback_maker)<tt>\n</tt> v = args.first<tt>\n</tt> <span class=\"r\">return</span> v == <span class=\"i\">1</span> ? <span class=\"i\">1</span> : v * callback.call(v - <span class=\"i\">1</span>)<tt>\n</tt> }<tt>\n</tt>}).call(<span class=\"i\">5</span>) <span class=\"c\"># =&gt; 120</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <h3>Making it generic and pretty</h3>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">y_combinator</span>(&amp;generator)<tt>\n</tt> lambda {|x| x.call(x) }.call(lambda {|callback_maker| <tt>\n</tt> lambda {|*args| <tt>\n</tt> callback = callback_maker.call(callback_maker)<tt>\n</tt> generator.call(callback).call(*args)<tt>\n</tt> }<tt>\n</tt> })<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt>y_combinator {|callback|<tt>\n</tt> lambda {|v|<tt>\n</tt> <span class=\"r\">return</span> v == <span class=\"i\">1</span> ? <span class=\"i\">1</span> : v * callback.call(v - <span class=\"i\">1</span>)<tt>\n</tt> }<tt>\n</tt>}.call(<span class=\"i\">5</span>) <span class=\"c\"># =&gt; 120</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>And let&#8217;s make it a bit less ugly by doing what <a href=\"http://weblog.raganwald.com/2007/02/guest-blogger-tom-moertel-derives-y.html\">Tom Mortel did</a> and using [] instead of call (they&#8217;re equivalent), and moving the callback_maker inline.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">y_combinator</span>(&amp;f)<tt>\n</tt> lambda {|x| x[x] } [<tt>\n</tt> lambda {|maker| lambda {|*args| f[maker[maker]][*args] }}<tt>\n</tt> ]<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Thus ends my exploration of the Y Combinator. Practically useless in any language you&#8217;d be using today, but hey, don&#8217;t you feel smarter?</p>\n\n\n <p><strong><span class=\"caps\">UPDATE</span>:</strong> Added dmh&#8217;s suggestion from the comments.</p>',1,4,NULL,'2007-12-20 08:29:00','2007-12-20 08:19:43','2007-12-20 21:20:39'),(336,'Test setup broken in Rails 2.0.2','test-setup-broken-in-rails-2-0-2','Some changes went into rails 2.0.2 that mean the @setup@ method in test subclasses won\'t get called. Here\'s how it went down:\r\n\r\n * \"8392\":http://dev.rubyonrails.org/changeset/8392 broke it\r\n * \"8430\":http://dev.rubyonrails.org/changeset/8430 tagged 2.0.2\r\n * \"8442\":http://dev.rubyonrails.org/changeset/8442 reverted 8392\r\n * \"8445\":http://dev.rubyonrails.org/changeset/8445 added a test so it doesn\'t break again\r\n\r\nYou can see some code illustrating the problem in 8445. This affects two plugins that we\'re using - \"helper_test\":http://nubyonrails.com/articles/test-your-helpers and \"activemessaging\":http://code.google.com/p/activemessaging/wiki/ActiveMessaging.\r\n\r\nFor the helper test, the work around is to rename your helper test setup methods to @setup_with_fixtures@.\r\n--- ruby\r\ndef setup_with_fixtures\r\n super\r\nend\r\n---\r\n\r\nFor activemessaging, add the following line to the setup of your functionals that are failing (\"from the mailing list\":http://groups.google.com/group/activemessaging-discuss/browse_frm/thread/d0c7f025b2855463):\r\n\r\n--- ruby\r\nActiveMessaging.reload_activemessaging\r\n---','<p>Some changes went into rails 2.0.2 that mean the <code>setup</code> method in test subclasses won&#8217;t get called. Here&#8217;s how it went down:</p>\n\n\n <ul>\n <li><a href=\"http://dev.rubyonrails.org/changeset/8392\">8392</a> broke it</li>\n <li><a href=\"http://dev.rubyonrails.org/changeset/8430\">8430</a> tagged 2.0.2</li>\n <li><a href=\"http://dev.rubyonrails.org/changeset/8442\">8442</a> reverted 8392</li>\n <li><a href=\"http://dev.rubyonrails.org/changeset/8445\">8445</a> added a test so it doesn&#8217;t break again</li>\n </ul>\n\n\n <p>You can see some code illustrating the problem in 8445. This affects two plugins that we&#8217;re using &#8211; <a href=\"http://nubyonrails.com/articles/test-your-helpers\">helper_test</a> and <a href=\"http://code.google.com/p/activemessaging/wiki/ActiveMessaging\">activemessaging</a>.</p>\n\n\nFor the helper test, the work around is to rename your helper test setup methods to <code>setup_with_fixtures</code>.\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">setup_with_fixtures</span><tt>\n</tt> <span class=\"r\">super</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>For activemessaging, add the following line to the setup of your functionals that are failing (<a href=\"http://groups.google.com/group/activemessaging-discuss/browse_frm/thread/d0c7f025b2855463\">from the mailing list</a>):</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"co\">ActiveMessaging</span>.reload_activemessaging<tt>\n</tt></pre></td>\n</tr></table>',1,3,NULL,'2007-12-20 22:18:00','2007-12-20 22:16:41','2007-12-20 22:18:46'),(342,'Tail call optimization in erlang','tail-call-optimization-in-erlang','--- erlang\r\nfact(1) -> 1;\r\nfact(N) -> N * fact(N - 1).\r\n---\r\n\r\nYou\'ve all seen the classic recursive factorial definition. Problem is, it\'s not really useable. 50000 factorial, anyone? The problem is it needs to create a new stack frame for each recursive call, very quickly blowing out your memory usage. Let\'s look at a classic erlang structure, a message processing loop:\r\n\r\n--- erlang\r\nloop() ->\r\n receive\r\n hello -> io:format(\"hello\")\r\n loop().\r\n end.\r\n---\r\n\r\nThat looks mighty recursive also - one would be inclined to think that saying hello a couple of thousand times would quickly chew through memory. Happily, this is not the case! The reason is tail call optimization.\r\n\r\nAs you can see, the above hello program is really just a loop. Note that when we call @loop()@, there\'s no reason to maintain the stack for the current call, because there is no more processing to be done. It just needs to pass on the return value. The erlang compiler recognises this, and so can optimize the above code by doing just that - throwing away the stack (or transforming it into a loop, whichever you prefer).\r\n\r\nWith the factorial example, optimization cannot be done because each call needs to wait for the return value of @fact(N-1)@ to multiply it by @N@ - extra processing that depends on the call\'s stack.\r\n\r\n*Tail call optimization can only be done when the recursive call is the last operation in the function.*\r\n\r\nWith this knowledge, we can rewrite our factorial function to include an accumulator parameter, allowing us to take advantage of the optimization.\r\n\r\n--- erlang\r\nfact(N) -> fact(N, 1).\r\nfact(1, T) -> T;\r\nfact(N, T) -> fact(N - 1, T * N).\r\n---\r\n\r\nOr since we recognise that you can redo this with a loop, you could always just write it that way yourself.\r\n\r\n--- erlang\r\nfact(N) -> lists:foldl(fun(X, T) -> X * T end, 1, lists:seq(1, N)).\r\n---\r\n\r\nI haven\'t used erlang enough to make a call as to which is nicer. Probably the first one. I\'m a ruby guy at heart, so for old time\'s sake here\'s a version you can use in ruby, which I think is quite pretty (be warned ruby doesn\'t do tail call optimization).\r\n\r\n--- ruby\r\ndef fact(n)\r\n (1..n).inject(1) {|t, n| t * n}\r\nend\r\n---','<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">fact(1) -&gt; 1;<tt>\n</tt>fact(N) -&gt; N * fact(N - 1).<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>You&#8217;ve all seen the classic recursive factorial definition. Problem is, it&#8217;s not really useable. 50000 factorial, anyone? The problem is it needs to create a new stack frame for each recursive call, very quickly blowing out your memory usage. Let&#8217;s look at a classic erlang structure, a message processing loop:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">loop() -&gt;<tt>\n</tt> receive<tt>\n</tt> hello -&gt; io:format(&quot;hello&quot;)<tt>\n</tt> loop().<tt>\n</tt> end.<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>That looks mighty recursive also &#8211; one would be inclined to think that saying hello a couple of thousand times would quickly chew through memory. Happily, this is not the case! The reason is tail call optimization.</p>\n\n\n <p>As you can see, the above hello program is really just a loop. Note that when we call <code>loop()</code>, there&#8217;s no reason to maintain the stack for the current call, because there is no more processing to be done. It just needs to pass on the return value. The erlang compiler recognises this, and so can optimize the above code by doing just that &#8211; throwing away the stack (or transforming it into a loop, whichever you prefer).</p>\n\n\n <p>With the factorial example, optimization cannot be done because each call needs to wait for the return value of <code>fact(N-1)</code> to multiply it by <code>N</code> &#8211; extra processing that depends on the call&#8217;s stack.</p>\n\n\n <p><strong>Tail call optimization can only be done when the recursive call is the last operation in the function.</strong></p>\n\n\n <p>With this knowledge, we can rewrite our factorial function to include an accumulator parameter, allowing us to take advantage of the optimization.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">fact(N) -&gt; fact(N, 1).<tt>\n</tt>fact(1, T) -&gt; T;<tt>\n</tt>fact(N, T) -&gt; fact(N - 1, T * N).<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Or since we recognise that you can redo this with a loop, you could always just write it that way yourself.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">fact(N) -&gt; lists:foldl(fun(X, T) -&gt; X * T end, 1, lists:seq(1, N)).<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>I haven&#8217;t used erlang enough to make a call as to which is nicer. Probably the first one. I&#8217;m a ruby guy at heart, so for old time&#8217;s sake here&#8217;s a version you can use in ruby, which I think is quite pretty (be warned ruby doesn&#8217;t do tail call optimization).</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">fact</span>(n)<tt>\n</tt> (<span class=\"i\">1</span>..n).inject(<span class=\"i\">1</span>) {|t, n| t * n}<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>',1,0,NULL,'2007-12-22 06:03:00','2007-12-22 06:01:28','2007-12-22 06:04:26'),(347,'Lesstile - A yuletide present','lesstile-a-yuletide-present','Textile is great for formatting articles. But comments aren\'t articles, and I have always felt that textile was overkill. Do you really need nested headings and subscript in comments? No.\r\n\r\nAlso! And more importantly, textile doesn\'t output valid XHTML. Consider the following textile code:\r\n\r\n--- none\r\n<b>\r\nHello\r\n\r\nThis is broken\r\n</b>\r\n---\r\n\r\nConverts to:\r\n\r\n--- html\r\n<p><b>\r\nHello</p>\r\n<p>This is broken</b></p>\r\n---\r\n\r\nThat sucks if your blog happens to be XHTML strict, because then your site is broken :( So I made an alternative. I offer it as a present to you: \"Lesstile\":http://lesstile.rubyforge.org\r\n\r\nTry it out, it\'s pretty neat:\r\n--- none\r\ngem install lesstile\r\n---\r\n--- ruby\r\nrequire \'lesstile\'\r\n\r\nLesstile.format_as_xhtml <<-EOS\r\nWow this is ace!\r\n\r\n--- Ruby\r\ndef some_code\r\n \"yay code\"\r\nend\r\n---\r\nEOS\r\n---\r\n\r\nIt supports code blocks, and that\'s it. You can easily pass it through CodeRay to get syntax highlighting if you want - see the docs. In the future it may also support hyperlinking. That\'s all I suppose commenters on this blog need, maybe you will tell me otherwise. Try it out on this post.\r\n\r\nAs a special extra treat, I added live preview to this blog, so you can see what your comment is going to look like as you write. It\'s just like the future!\r\n\r\nPlease comment with code to say hi.','<p>Textile is great for formatting articles. But comments aren&#8217;t articles, and I have always felt that textile was overkill. Do you really need nested headings and subscript in comments? No.</p>\n\n\n <p>Also! And more importantly, textile doesn&#8217;t output valid <span class=\"caps\">XHTML</span>. Consider the following textile code:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&lt;b&gt;<tt>\n</tt>Hello<tt>\n</tt><tt>\n</tt>This is broken<tt>\n</tt>&lt;/b&gt;<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Converts to:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"ta\">&lt;p&gt;</span><span class=\"ta\">&lt;b&gt;</span><tt>\n</tt>Hello<span class=\"ta\">&lt;/p&gt;</span><tt>\n</tt><span class=\"ta\">&lt;p&gt;</span>This is broken<span class=\"ta\">&lt;/b&gt;</span><span class=\"ta\">&lt;/p&gt;</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>That sucks if your blog happens to be <span class=\"caps\">XHTML</span> strict, because then your site is broken :( So I made an alternative. I offer it as a present to you: <a href=\"http://lesstile.rubyforge.org\">Lesstile</a></p>\n\n\nTry it out, it&#8217;s pretty neat:\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">gem install lesstile<tt>\n</tt></pre></td>\n</tr></table>\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">lesstile</span><span class=\"dl\">\'</span></span><tt>\n</tt><tt>\n</tt><span class=\"co\">Lesstile</span>.format_as_xhtml <span class=\"s\"><span class=\"dl\">&lt;&lt;-EOS</span></span><span class=\"s\"><span class=\"k\"><tt>\n</tt>Wow this is ace!<tt>\n</tt><tt>\n</tt>--- Ruby<tt>\n</tt>def some_code<tt>\n</tt> &quot;yay code&quot;<tt>\n</tt>end<tt>\n</tt>---</span><span class=\"dl\"><tt>\n</tt>EOS</span></span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>It supports code blocks, and that&#8217;s it. You can easily pass it through CodeRay to get syntax highlighting if you want &#8211; see the docs. In the future it may also support hyperlinking. That&#8217;s all I suppose commenters on this blog need, maybe you will tell me otherwise. Try it out on this post.</p>\n\n\n <p>As a special extra treat, I added live preview to this blog, so you can see what your comment is going to look like as you write. It&#8217;s just like the future!</p>\n\n\n <p>Please comment with code to say hi.</p>',1,2,NULL,'2007-12-27 06:19:00','2007-12-27 06:13:47','2007-12-27 06:19:40'),(354,'Exercises in holiday recreation','exercises-in-holiday-recreation','In days gone by, before even when I was a young lad, the kids engaged in wholesome entertainment on their holidays. Chasing a wheel down the road, playing with wooden trains, arts and crafts. None of these newfangled electronics. Their eyes were round, not square. Last night my brother and I recaptured this spirit by eschewing the computer, the TV - we built something with our bare hands. A maddening patchwork of skewers and string, cardboard and bluetack. We created the Cockney Monster:\r\n\r\n<object type=\"application/x-shockwave-flash\" width=\"425\" height=\"350\" data=\"http://www.youtube.com/v/gGGei42Fv6U&rel=1\"><param name=\"movie\" value=\"http://www.youtube.com/v/gGGei42Fv6U&rel=1\" /></object>\r\n\r\nAn abominable assemblage by any standards, it was begging for death towards the end. Nevertheless, we left it standing overnight for observation and discussion by the family over breakfast. I stabbed myself in the finger with a skewer while making this. \"Detail shots and tasty trivia on flickr\":http://www.flickr.com/photos/18919286@N08/sets/72157603569100240/.\r\n\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2145568212/\" title=\"Cockney Monster - Ramps by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2153/2145568212_3ac9b89e3a_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Ramps\" /></a>\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2145566648/\" title=\"Cockney Monster - Pulley System by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2311/2145566648_5c38eea5ba_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Pulley System\" /></a>\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2145563700/\" title=\"Cockney Monster - Marble Transfer by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2122/2145563700_cf7c259139_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Marble Transfer\" /></a>\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2145569124/\" title=\"Cockney Monster - Full shot by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2075/2145569124_8c03bd3bf0_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Full shot\" /></a>','<p>In days gone by, before even when I was a young lad, the kids engaged in wholesome entertainment on their holidays. Chasing a wheel down the road, playing with wooden trains, arts and crafts. None of these newfangled electronics. Their eyes were round, not square. Last night my brother and I recaptured this spirit by eschewing the computer, the TV &#8211; we built something with our bare hands. A maddening patchwork of skewers and string, cardboard and bluetack. We created the Cockney Monster:</p>\n\n\n<object type=\"application/x-shockwave-flash\" width=\"425\" height=\"350\" data=\"http://www.youtube.com/v/gGGei42Fv6U&#38;rel=1\"><param name=\"movie\" value=\"http://www.youtube.com/v/gGGei42Fv6U&#38;rel=1\" /></object>\n\n <p>An abominable assemblage by any standards, it was begging for death towards the end. Nevertheless, we left it standing overnight for observation and discussion by the family over breakfast. I stabbed myself in the finger with a skewer while making this. <a href=\"http://www.flickr.com/photos/18919286@N08/sets/72157603569100240/\">Detail shots and tasty trivia on flickr</a>.</p>\n\n\n <p><a href=\"http://www.flickr.com/photos/18919286@N08/2145568212/\" title=\"Cockney Monster - Ramps by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2153/2145568212_3ac9b89e3a_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Ramps\" /></a>\n<a href=\"http://www.flickr.com/photos/18919286@N08/2145566648/\" title=\"Cockney Monster - Pulley System by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2311/2145566648_5c38eea5ba_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Pulley System\" /></a>\n<a href=\"http://www.flickr.com/photos/18919286@N08/2145563700/\" title=\"Cockney Monster - Marble Transfer by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2122/2145563700_cf7c259139_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Marble Transfer\" /></a>\n<a href=\"http://www.flickr.com/photos/18919286@N08/2145569124/\" title=\"Cockney Monster - Full shot by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2075/2145569124_8c03bd3bf0_s.jpg\" width=\"75\" height=\"75\" alt=\"Cockney Monster - Full shot\" /></a></p>',1,2,NULL,'2007-12-29 03:23:00','2007-12-29 03:23:35','2007-12-29 05:01:12'),(355,'Youth receives cookbook for Christmas, makes dessert','youth-receives-cookbook-for-christmas-makes-dessert','<img src=\"http://farm3.static.flickr.com/2279/2144971219_739a2145e8_m.jpg\" alt=\"Recipe book\" class=\"inline\" />\r\n\r\nA coffee liqueur gateau, to be precise. A 3 star recipe (the highest!), I set aside the afternoon for preparation. It required 3 kinds of nut - hazel, brazil, and almond - all of which were readily available at the local nut shack. The idea was to roast for a while then \'rub vigourously\' in a tea towel to remove the skins from the hazelnuts. Alas, with all the vigour I could muster I could only remove about half of them, so I surreptisiously threw them in the food processor beside the others and destroyed the evidence.\r\n\r\nThe cake required 7 egg whites, tips of which extracting include doing eggs one by one into a cup so if you screw one up all is not lost, and pouring the yoke between the two shell halves after cracking, allowing the white to flow out.\r\n\r\nAfter cooking the cake needed to be cut into 3 layers. I probably didn\'t allow it to cool enough before doing so, and using a serrated knife as the recipe suggested I struggled somewhat, ending up with two layers, one with a small hole in it. In the future, I will use dental floss for a finer cut.\r\n\r\nContrary to the suggested chocolate button covering (it looks a bit naff, don\'t you think?), I used shaved chocolate for topping. You can use a grater and a peeler to get different textures, and it looks really nice. Added two fanned strawberries and a couple of blueberries for extra credit. Much more of a modern vogue feel, much trendier. Also, I did not serve on floral plates.\r\n\r\nThe result was a cake I would pay money for. Light, nutty, moist, very tasty served with cream. It was quite a lot of effort though, and requiring of implements only available at my parents\' house, so I won\'t be making this too often. I did take a photo but it got deleted in a cruel act of fate. Here\'s the example picture from the cookbook. Mine looked much more awesome.\r\n\r\n!http://farm3.static.flickr.com/2312/2144972711_f9282bd47a_m.jpg!\r\n\r\nTop kudos to Mum and my sister. I will never be able to cook half as well as them. Every suggestion in this post came from them.','<p><img src=\"http://farm3.static.flickr.com/2279/2144971219_739a2145e8_m.jpg\" alt=\"Recipe book\" class=\"inline\" /></p>\n\n\n <p>A coffee liqueur gateau, to be precise. <span class=\"caps\">A 3</span> star recipe (the highest!), I set aside the afternoon for preparation. It required 3 kinds of nut &#8211; hazel, brazil, and almond &#8211; all of which were readily available at the local nut shack. The idea was to roast for a while then &#8216;rub vigourously&#8217; in a tea towel to remove the skins from the hazelnuts. Alas, with all the vigour I could muster I could only remove about half of them, so I surreptisiously threw them in the food processor beside the others and destroyed the evidence.</p>\n\n\n <p>The cake required 7 egg whites, tips of which extracting include doing eggs one by one into a cup so if you screw one up all is not lost, and pouring the yoke between the two shell halves after cracking, allowing the white to flow out.</p>\n\n\n <p>After cooking the cake needed to be cut into 3 layers. I probably didn&#8217;t allow it to cool enough before doing so, and using a serrated knife as the recipe suggested I struggled somewhat, ending up with two layers, one with a small hole in it. In the future, I will use dental floss for a finer cut.</p>\n\n\n <p>Contrary to the suggested chocolate button covering (it looks a bit naff, don&#8217;t you think?), I used shaved chocolate for topping. You can use a grater and a peeler to get different textures, and it looks really nice. Added two fanned strawberries and a couple of blueberries for extra credit. Much more of a modern vogue feel, much trendier. Also, I did not serve on floral plates.</p>\n\n\n <p>The result was a cake I would pay money for. Light, nutty, moist, very tasty served with cream. It was quite a lot of effort though, and requiring of implements only available at my parents&#8217; house, so I won&#8217;t be making this too often. I did take a photo but it got deleted in a cruel act of fate. Here&#8217;s the example picture from the cookbook. Mine looked much more awesome.</p>\n\n\n <p><img src=\"http://farm3.static.flickr.com/2312/2144972711_f9282bd47a_m.jpg\" alt=\"\" /></p>\n\n\n <p>Top kudos to Mum and my sister. I will never be able to cook half as well as them. Every suggestion in this post came from them.</p>',1,1,NULL,'2007-12-29 05:25:00','2007-12-29 05:19:04','2007-12-29 08:50:44'),(356,'Rails XHTML Validation with LibXML/HTML Tidy','rails-xhtml-validation-with-libxml-html-tidy','I improved upon the XHTML validation technique I showed yesterday to add nicer error messages, and also support for local testing via HTML Tidy. HTML Tidy isn\'t quite as good as W3C - for example it missed a label that was pointing to an invalid ID, but it runs hell fast. For W3C testing I\'m now using libXML to parse the response to actually list the errors rather than just tell you they exist.\r\n\r\nAnd it\'s all customizable by setting the MARKUP_VALIDATOR environment variables. Options are: w3c, tidy, tidy_no_warnings. Tidy is the default.\r\n\r\n--- ruby\r\ndef assert_valid_markup(markup=@response.body)\r\n ENV[\'MARKUP_VALIDATOR\'] ||= \'tidy\'\r\n case ENV[\'MARKUP_VALIDATOR\']\r\n when \'w3c\'\r\n # Thanks http://scottraymond.net/articles/2005/09/20/rails-xhtml-validation\r\n require \'net/http\'\r\n response = Net::HTTP.start(\'validator.w3.org\') do |w3c|\r\n query = \'fragment=\' + CGI.escape(markup) + \'&output=xml\'\r\n w3c.post2(\'/check\', query)\r\n end\r\n if response[\'x-w3c-validator-status\'] != \'Valid\'\r\n error_str = \"XHTML Validation Failed:\\n\"\r\n parser = XML::Parser.new\r\n parser.string = response.body\r\n doc = parser.parse\r\n\r\n doc.find(\"//result/messages/msg\").each do |msg|\r\n error_str += \" Line %i: %s\\n\" % [msg[\"line\"], msg]\r\n end\r\n\r\n flunk error_str\r\n end\r\n\r\n when \'tidy\', \'tidy_no_warnings\'\r\n require \'tidy\'\r\n errors = []\r\n Tidy.open(:input_xml => true) do |tidy|\r\n tidy.clean(markup)\r\n errors.concat(tidy.errors)\r\n end\r\n Tidy.open(:show_warnings=> (ENV[\'MARKUP_VALIDATOR\'] != \'tidy_no_warnings\')) do |tidy|\r\n tidy.clean(markup)\r\n errors.concat(tidy.errors)\r\n end\r\n if errors.length > 0\r\n error_str = \'\'\r\n errors.each do |e|\r\n error_str += e.gsub(/\\n/, \"\\n \")\r\n end\r\n error_str = \"XHTML Validation Failed:\\n #{error_str}\"\r\n \r\n assert_block(error_str) { false }\r\n end \r\n end\r\nend\r\n---\r\n\r\nGetting Tidy to work was an ordeal, the ruby documentation is rather lacking. It also behaves in weird ways - the call to <code>errors</code> returns a one element array, with all the errors bundled together in the one string.\r\n\r\nLibXML was a little tricky - there\'s no obvious way to parse an XML document in memory. You\'d think <code>XML::Document.new(xml)</code> would do the trick, since there\'s a <code>XML::Document.file(filename)</code> method, but that actually uses the entire XML document as the version string. Not so handy. Turns out you need to create an XML::Parser object instead, as I\'ve done above. The docs don\'t mention this (anywhere obvious, that is), I found a thread in the LibXML mailing list.','<p>I improved upon the <span class=\"caps\">XHTML</span> validation technique I showed yesterday to add nicer error messages, and also support for local testing via <span class=\"caps\">HTML</span> Tidy. <span class=\"caps\">HTML</span> Tidy isn&#8217;t quite as good as <span class=\"caps\">W3C</span> &#8211; for example it missed a label that was pointing to an invalid ID, but it runs hell fast. For <span class=\"caps\">W3C</span> testing I&#8217;m now using libXML to parse the response to actually list the errors rather than just tell you they exist.</p>\n\n\n <p>And it&#8217;s all customizable by setting the <span class=\"caps\">MARKUP</span>_VALIDATOR environment variables. Options are: w3c, tidy, tidy_no_warnings. Tidy is the default.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt>29<tt>\n</tt>30<tt>\n</tt>31<tt>\n</tt>32<tt>\n</tt>33<tt>\n</tt>34<tt>\n</tt>35<tt>\n</tt>36<tt>\n</tt>37<tt>\n</tt>38<tt>\n</tt>39<tt>\n</tt>40<tt>\n</tt>41<tt>\n</tt>42<tt>\n</tt>43<tt>\n</tt>44<tt>\n</tt>45<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">assert_valid_markup</span>(markup=<span class=\"iv\">@response</span>.body)<tt>\n</tt> <span class=\"co\">ENV</span>[<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">MARKUP_VALIDATOR</span><span class=\"dl\">\'</span></span>] ||= <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">tidy</span><span class=\"dl\">\'</span></span><tt>\n</tt> <span class=\"r\">case</span> <span class=\"co\">ENV</span>[<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">MARKUP_VALIDATOR</span><span class=\"dl\">\'</span></span>]<tt>\n</tt> <span class=\"r\">when</span> <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">w3c</span><span class=\"dl\">\'</span></span><tt>\n</tt> <span class=\"c\"># Thanks http://scottraymond.net/articles/2005/09/20/rails-xhtml-validation</span><tt>\n</tt> require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">net/http</span><span class=\"dl\">\'</span></span><tt>\n</tt> response = <span class=\"co\">Net</span>::<span class=\"co\">HTTP</span>.start(<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">validator.w3.org</span><span class=\"dl\">\'</span></span>) <span class=\"r\">do</span> |w3c|<tt>\n</tt> query = <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">fragment=</span><span class=\"dl\">\'</span></span> + <span class=\"co\">CGI</span>.escape(markup) + <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">&amp;output=xml</span><span class=\"dl\">\'</span></span><tt>\n</tt> w3c.post2(<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">/check</span><span class=\"dl\">\'</span></span>, query)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">if</span> response[<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">x-w3c-validator-status</span><span class=\"dl\">\'</span></span>] != <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">Valid</span><span class=\"dl\">\'</span></span><tt>\n</tt> error_str = <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">XHTML Validation Failed:</span><span class=\"ch\">\\n</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> parser = <span class=\"co\">XML</span>::<span class=\"co\">Parser</span>.new<tt>\n</tt> parser.string = response.body<tt>\n</tt> doc = parser.parse<tt>\n</tt><tt>\n</tt> doc.find(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">//result/messages/msg</span><span class=\"dl\">&quot;</span></span>).each <span class=\"r\">do</span> |msg|<tt>\n</tt> error_str += <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\"> Line %i: %s</span><span class=\"ch\">\\n</span><span class=\"dl\">&quot;</span></span> % [msg[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">line</span><span class=\"dl\">&quot;</span></span>], msg]<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> flunk error_str<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><tt>\n</tt> <span class=\"r\">when</span> <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">tidy</span><span class=\"dl\">\'</span></span>, <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">tidy_no_warnings</span><span class=\"dl\">\'</span></span><tt>\n</tt> require <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">tidy</span><span class=\"dl\">\'</span></span><tt>\n</tt> errors = []<tt>\n</tt> <span class=\"co\">Tidy</span>.open(<span class=\"sy\">:input_xml</span> =&gt; <span class=\"pc\">true</span>) <span class=\"r\">do</span> |tidy|<tt>\n</tt> tidy.clean(markup)<tt>\n</tt> errors.concat(tidy.errors)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"co\">Tidy</span>.open(<span class=\"sy\">:show_warnings</span>=&gt; (<span class=\"co\">ENV</span>[<span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">MARKUP_VALIDATOR</span><span class=\"dl\">\'</span></span>] != <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">tidy_no_warnings</span><span class=\"dl\">\'</span></span>)) <span class=\"r\">do</span> |tidy|<tt>\n</tt> tidy.clean(markup)<tt>\n</tt> errors.concat(tidy.errors)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">if</span> errors.length &gt; <span class=\"i\">0</span><tt>\n</tt> error_str = <span class=\"s\"><span class=\"dl\">\'</span><span class=\"dl\">\'</span></span><tt>\n</tt> errors.each <span class=\"r\">do</span> |e|<tt>\n</tt> error_str += e.gsub(<span class=\"rx\"><span class=\"dl\">/</span><span class=\"ch\">\\n</span><span class=\"dl\">/</span></span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"ch\">\\n</span><span class=\"k\"> </span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> error_str = <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">XHTML Validation Failed:</span><span class=\"ch\">\\n</span><span class=\"k\"> </span><span class=\"il\"><span class=\"dl\">#{</span>error_str<span class=\"dl\">}</span></span><span class=\"dl\">&quot;</span></span><tt>\n</tt> <tt>\n</tt> assert_block(error_str) { <span class=\"pc\">false</span> }<tt>\n</tt> <span class=\"r\">end</span> <tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Getting Tidy to work was an ordeal, the ruby documentation is rather lacking. It also behaves in weird ways &#8211; the call to <code>errors</code> returns a one element array, with all the errors bundled together in the one string.</p>\n\n\n <p>LibXML was a little tricky &#8211; there&#8217;s no obvious way to parse an <span class=\"caps\">XML</span> document in memory. You&#8217;d think <code>XML::Document.new(xml)</code> would do the trick, since there&#8217;s a <code>XML::Document.file(filename)</code> method, but that actually uses the entire <span class=\"caps\">XML</span> document as the version string. Not so handy. Turns out you need to create an <span class=\"caps\">XML</span>::Parser object instead, as I&#8217;ve done above. The docs don&#8217;t mention this (anywhere obvious, that is), I found a thread in the LibXML mailing list.</p>',1,0,NULL,'2006-06-11 06:41:00','2007-12-29 05:42:32','2007-12-29 05:43:37'),(357,'Testing rails','testing-rails','I was working on creating functional tests for some of my code today, a task made ridiculously easy by rails. To add extra value, I added an assertion (from <a href=\"http://scottraymond.net/articles/2005/09/20/rails-xhtml-validation\">Scott Raymond</a>) to validate my markup against the w3c online validator:\r\n\r\n--- ruby\r\ndef assert_valid_markup(markup=@response.body)\r\n if ENV[\"TEST_MARKUP\"]\r\n require \"net/http\"\r\n response = Net::HTTP.start(\"validator.w3.org\") do |w3c|\r\n query = \"fragment=\" + CGI.escape(markup) + \"&output=xml\"\r\n w3c.post2(\"/check\", query)\r\n end\r\n assert_equal \"Valid\", response[\"x-w3c-validator-status\"]\r\n end\r\nend\r\n---\r\n\r\nThe ENV test means it isn\'t run by default since it slows down my tests considerably, but I don\'t want to move markup checks out of the functional tests because that\'s where they belong. Next step is to validate locally, which I\'ve heard you can do with HTML Tidy.\r\n\r\nAnother problem is testing code that relies on <code>DateTime.now</code>, since this is a singleton call and not easily mockable.\r\n\r\n--- ruby\r\ndef pin_time\r\n time = DateTime.now\r\n DateTime.class_eval <<-EOS\r\n def self.now\r\n DateTime.parse(\"#{time}\")\r\n end\r\n EOS\r\n yield time\r\nend\r\n\r\n# Usage\r\npin_time do |test_time|\r\n assert_equal test_time, DateTime.now\r\n sleep 2\r\n assert_equal test_time, DateTime.now\r\nend\r\n---\r\n\r\nI haven\'t found a neat way of resetting the behaviour of <code>now</code>. Using <code>load \'date.rb\'</code> works but produces warnings for redefined constants. I couldn\'t get either aliasing the original method, undefining the new one, or even just calling <code>Date.now</code> to work.\r\n\r\n*UPDATE:* Ah, how young I was. A better way to do this is to use a library like \"mocha\":http://mocha.rubyforge.org','<p>I was working on creating functional tests for some of my code today, a task made ridiculously easy by rails. To add extra value, I added an assertion (from <a href=\"http://scottraymond.net/articles/2005/09/20/rails-xhtml-validation\">Scott Raymond</a>) to validate my markup against the w3c online validator:</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">assert_valid_markup</span>(markup=<span class=\"iv\">@response</span>.body)<tt>\n</tt> <span class=\"r\">if</span> <span class=\"co\">ENV</span>[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">TEST_MARKUP</span><span class=\"dl\">&quot;</span></span>]<tt>\n</tt> require <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">net/http</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> response = <span class=\"co\">Net</span>::<span class=\"co\">HTTP</span>.start(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">validator.w3.org</span><span class=\"dl\">&quot;</span></span>) <span class=\"r\">do</span> |w3c|<tt>\n</tt> query = <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">fragment=</span><span class=\"dl\">&quot;</span></span> + <span class=\"co\">CGI</span>.escape(markup) + <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">&amp;output=xml</span><span class=\"dl\">&quot;</span></span><tt>\n</tt> w3c.post2(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">/check</span><span class=\"dl\">&quot;</span></span>, query)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> assert_equal <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">Valid</span><span class=\"dl\">&quot;</span></span>, response[<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">x-w3c-validator-status</span><span class=\"dl\">&quot;</span></span>]<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>The <span class=\"caps\">ENV</span> test means it isn&#8217;t run by default since it slows down my tests considerably, but I don&#8217;t want to move markup checks out of the functional tests because that&#8217;s where they belong. Next step is to validate locally, which I&#8217;ve heard you can do with <span class=\"caps\">HTML</span> Tidy.</p>\n\n\n <p>Another problem is testing code that relies on <code>DateTime.now</code>, since this is a singleton call and not easily mockable.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">pin_time</span><tt>\n</tt> time = <span class=\"co\">DateTime</span>.now<tt>\n</tt> <span class=\"co\">DateTime</span>.class_eval <span class=\"s\"><span class=\"dl\">&lt;&lt;-EOS</span></span><span class=\"s\"><span class=\"k\"><tt>\n</tt> def self.now<tt>\n</tt> DateTime.parse(&quot;</span><span class=\"il\"><span class=\"dl\">#{</span>time<span class=\"dl\">}</span></span><span class=\"k\">&quot;)<tt>\n</tt> end</span><span class=\"dl\"><tt>\n</tt> EOS</span></span><tt>\n</tt> <span class=\"r\">yield</span> time<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt><tt>\n</tt><span class=\"c\"># Usage</span><tt>\n</tt>pin_time <span class=\"r\">do</span> |test_time|<tt>\n</tt> assert_equal test_time, <span class=\"co\">DateTime</span>.now<tt>\n</tt> sleep <span class=\"i\">2</span><tt>\n</tt> assert_equal test_time, <span class=\"co\">DateTime</span>.now<tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>I haven&#8217;t found a neat way of resetting the behaviour of <code>now</code>. Using <code>load \'date.rb\'</code> works but produces warnings for redefined constants. I couldn&#8217;t get either aliasing the original method, undefining the new one, or even just calling <code>Date.now</code> to work.</p>\n\n\n <p><strong><span class=\"caps\">UPDATE</span>:</strong> Ah, how young I was. A better way to do this is to use a library like <a href=\"http://mocha.rubyforge.org\">mocha</a></p>',1,0,NULL,'2006-06-10 06:45:00','2007-12-29 05:47:16','2007-12-29 05:54:28'),(358,'Make your resume interesting','make-your-resume-interesting','I\'ve just finished up all my IBL interviews. #1 preference is Advatel, followed by Fenwick Software. Both development roles - the other two not so much.\r\n\r\nAt every interview I got an excited question about my experience at \"The World of Robotics\", since everyone knows robots are cool. Also, I\'ve listed \"Swing Dancing\" under other interests. In every single interview, right at the very end I always got a \"now I just have to ask...\" question about it. It was fantastic, the interview always ended on a high note, which has to be a good thing for your job prospects.\r\n\r\nThe point I\'m trying to make is - Make your resume interesting and worth talking about. You can then predict many of the questions you\'ll be asked and can be well prepared to sell yourself.','<p>I&#8217;ve just finished up all my <span class=\"caps\">IBL</span> interviews. #1 preference is Advatel, followed by Fenwick Software. Both development roles &#8211; the other two not so much.</p>\n\n\n <p>At every interview I got an excited question about my experience at &#8220;The World of Robotics&#8221;, since everyone knows robots are cool. Also, I&#8217;ve listed &#8220;Swing Dancing&#8221; under other interests. In every single interview, right at the very end I always got a &#8220;now I just have to ask&#8230;&#8221; question about it. It was fantastic, the interview always ended on a high note, which has to be a good thing for your job prospects.</p>\n\n\n <p>The point I&#8217;m trying to make is &#8211; Make your resume interesting and worth talking about. You can then predict many of the questions you&#8217;ll be asked and can be well prepared to sell yourself.</p>',1,0,NULL,'2006-06-07 06:48:00','2007-12-29 05:50:06','2007-12-29 05:50:06'),(359,'Don\'t use pagination on your blog','don-t-use-pagination-on-your-blog','What problem are you trying to solve? In my case, I don\'t want the bottom of the page to be a dead end. Paging would appear to be a good solution - click next page, get more content. Alas, it has issues:\r\n\r\n * When you post a new article, it changes the content of all your pages. Google doesn\'t like this - search traffic to your blog will suffer since people will click through expecting an older version of the page.\r\n * Invalidates your entire cache when you post something new. Admittedly not a problem for most of us, but worth considering.\r\n\r\nArchives solve my problem - not wanting a dead end, while avoiding the two problems with pagination mentioned above. It is harder to get your window size right though (you don\'t want 2 or 200 articles per page).\r\n\r\nFor bonus points, add something like the \"Humanized Reader\":http://www.humanized.com/weblog/2006/04/28/reading_humanized/. Javascript fetches the next article when you\'re near the bottom of the page, seamlessly adding it to the bottom of the page so the user can just keep on reading.\r\n\r\nI\'ve just added archives to this site - an interim fix to tide me over until I do it right.\r\n\r\nThanks to \"Rick Olson\":http://techno-weenie.net/ for telling me I didn\'t need paging.','<p>What problem are you trying to solve? In my case, I don&#8217;t want the bottom of the page to be a dead end. Paging would appear to be a good solution &#8211; click next page, get more content. Alas, it has issues:</p>\n\n\n <ul>\n <li>When you post a new article, it changes the content of all your pages. Google doesn&#8217;t like this &#8211; search traffic to your blog will suffer since people will click through expecting an older version of the page.</li>\n <li>Invalidates your entire cache when you post something new. Admittedly not a problem for most of us, but worth considering.</li>\n </ul>\n\n\n <p>Archives solve my problem &#8211; not wanting a dead end, while avoiding the two problems with pagination mentioned above. It is harder to get your window size right though (you don&#8217;t want 2 or 200 articles per page).</p>\n\n\n <p>For bonus points, add something like the <a href=\"http://www.humanized.com/weblog/2006/04/28/reading_humanized/\">Humanized Reader</a>. Javascript fetches the next article when you&#8217;re near the bottom of the page, seamlessly adding it to the bottom of the page so the user can just keep on reading.</p>\n\n\n <p>I&#8217;ve just added archives to this site &#8211; an interim fix to tide me over until I do it right.</p>\n\n\n <p>Thanks to <a href=\"http://techno-weenie.net/\">Rick Olson</a> for telling me I didn&#8217;t need paging.</p>',1,0,NULL,'2007-12-30 08:24:00','2007-12-29 08:35:47','2007-12-29 08:35:47'),(360,'Bad UI ruins Christmas','bad-ui-ruins-christmas','On my Mum\'s digital camera, when you look at a movie, you get a still from the movie and two options: delete this frame, delete all frames. Delete all frames does not delete all frames of the movie. It deletes everything on the camera. We lost all our Christmas photos, and also a photo of \"my cake\":http://rhnh.net/2007/12/29/youth-receives-cookbook-for-christmas-makes-dessert which is kind of depressing.\r\n\r\nDon\'t use ambiguous or unclear terminology in your UI. \"Frame\" in my mind refers to a frame of a movie, but the camera used it to refer to a photo - this is likely a case of a system term seeping through to the user interface (or bad translation - it\'s a Japanese camera I think).\r\n\r\nMy brother was impressed by the feature: \"Hey cool, you can delete individual frames from your movie\".\r\n\r\nRephrase important decisions in the confirmation, and provide important information. \"Are you sure you want to delete all frames?\" is useless. Try \"This will irrevocably delete all 324 photos and movies on your camera. Are you sure you want to continue?\". And always provide \'undo\'. It\'s not that hard to have a \'trash\' area, only permanently delete the files when you really need the space.\r\n\r\nIf anyone has any spare Christmas shots they\'re not using, please link them up. Mum\'s pretty distraught.','<p>On my Mum&#8217;s digital camera, when you look at a movie, you get a still from the movie and two options: delete this frame, delete all frames. Delete all frames does not delete all frames of the movie. It deletes everything on the camera. We lost all our Christmas photos, and also a photo of <a href=\"http://rhnh.net/2007/12/29/youth-receives-cookbook-for-christmas-makes-dessert\">my cake</a> which is kind of depressing.</p>\n\n\n <p>Don&#8217;t use ambiguous or unclear terminology in your UI. &#8220;Frame&#8221; in my mind refers to a frame of a movie, but the camera used it to refer to a photo &#8211; this is likely a case of a system term seeping through to the user interface (or bad translation &#8211; it&#8217;s a Japanese camera I think).</p>\n\n\n <p>My brother was impressed by the feature: &#8220;Hey cool, you can delete individual frames from your movie&#8221;.</p>\n\n\n <p>Rephrase important decisions in the confirmation, and provide important information. &#8220;Are you sure you want to delete all frames?&#8221; is useless. Try &#8220;This will irrevocably delete all 324 photos and movies on your camera. Are you sure you want to continue?&#8221;. And always provide &#8216;undo&#8217;. It&#8217;s not that hard to have a &#8216;trash&#8217; area, only permanently delete the files when you really need the space.</p>\n\n\n <p>If anyone has any spare Christmas shots they&#8217;re not using, please link them up. Mum&#8217;s pretty distraught.</p>',1,0,NULL,'2007-12-31 08:36:00','2007-12-29 08:46:44','2007-12-29 08:46:44'),(366,'I don\'t want preferences','i-don-t-want-preferences','Or why I\'m writing another blog engine for ruby\r\n\r\nI\'ve been running this site on Mephisto for a number of months now. It is fantastic at what it does, but I\'ve just recently realised it\'s not what I want.\r\n\r\n*I want to configure my blog by hacking code*\r\n\r\nI don\'t want preferences or theme support - I want to edit code. Mephisto isn\'t great for this - it uses non standard routing (everything goes through dispatch), it uses liquid templates. I feel like I have to learn Mephisto to hack it.\r\nSimpleLog is another rails option, but it sucks because it reads like a PHP app, and I don\'t want to be hacking that. It\'s built to be configured, not to be hacked.\r\n\r\nSo here is my grand plan.\r\n\r\nAn opinionated blog engine that does things my way. OpenID login, XHTML valid default template, RESTful stuff, code highlighting in comments, etc...\r\nTo install, you branch my master git repo and customize away. You can just keep rebasing to get all the trunk updates. You can publish a \'theme\' in the form of a patch against trunk. The code is going to be lean since I don\'t need to accommodate for 5, 10 or 15 articles per page, so it will be easy to comprehend.\r\n\r\nBasically, it\'s so you can write your own blog without having to worry about boring stuff like admin, defensio integration, and OpenID auth.\r\n\r\nI wonder what I\'ll call it.\r\n\r\n*UPDATE:* Look I made it - \"Enki\":http://www.enkiblog.com','<p>Or why I&#8217;m writing another blog engine for ruby</p>\n\n\n <p>I&#8217;ve been running this site on Mephisto for a number of months now. It is fantastic at what it does, but I&#8217;ve just recently realised it&#8217;s not what I want.</p>\n\n\n <p><strong>I want to configure my blog by hacking code</strong></p>\n\n\n <p>I don&#8217;t want preferences or theme support &#8211; I want to edit code. Mephisto isn&#8217;t great for this &#8211; it uses non standard routing (everything goes through dispatch), it uses liquid templates. I feel like I have to learn Mephisto to hack it.\nSimpleLog is another rails option, but it sucks because it reads like a <span class=\"caps\">PHP</span> app, and I don&#8217;t want to be hacking that. It&#8217;s built to be configured, not to be hacked.</p>\n\n\n <p>So here is my grand plan.</p>\n\n\n <p>An opinionated blog engine that does things my way. OpenID login, <span class=\"caps\">XHTML</span> valid default template, RESTful stuff, code highlighting in comments, etc&#8230;\nTo install, you branch my master git repo and customize away. You can just keep rebasing to get all the trunk updates. You can publish a &#8216;theme&#8217; in the form of a patch against trunk. The code is going to be lean since I don&#8217;t need to accommodate for 5, 10 or 15 articles per page, so it will be easy to comprehend.</p>\n\n\n <p>Basically, it&#8217;s so you can write your own blog without having to worry about boring stuff like admin, defensio integration, and OpenID auth.</p>\n\n\n <p>I wonder what I&#8217;ll call it.</p>\n\n\n <p><strong><span class=\"caps\">UPDATE</span>:</strong> Look I made it &#8211; <a href=\"http://www.enkiblog.com\">Enki</a></p>',1,4,NULL,'2008-01-02 07:39:00','2008-01-02 07:39:00','2008-01-19 08:19:00'),(367,'AtomFeedHelper produces invalid feeds','atomfeedhelper-produces-invalid-feeds','Summary: \"@atom_feed@ is broken until changeset 8529\":http://dev.rubyonrails.org/changeset/8529\r\n\r\n--- ruby\r\n# http://api.rubyonrails.org/classes/ActionView/Helpers/AtomFeedHelper.html#M000931\r\natom_feed do |feed|\r\n feed.title(\"My great blog!\")\r\n feed.updated((@posts.first.created_at))\r\n\r\n for post in @posts\r\n feed.entry(post) do |entry|\r\n entry.title(post.title)\r\n entry.content(post.body, :type => \'html\')\r\n\r\n entry.author do |author|\r\n author.name(\"DHH\")\r\n end\r\n end\r\n end\r\nend\r\n---\r\n\r\nProduces the following feed (rails 2.0.2)\r\n\r\n--- xml\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<feed xml:lang=\"en-US\" xmlns=\"http://www.w3.org/2005/Atom\">\r\n <id>tag:localhost:posts</id>\r\n <link type=\"text/html\" rel=\"alternate\" href=\"http://localhost:3000\"/>\r\n <title>My great blog!</title>\r\n <updated>2007-12-23T04:23:07+11:00</updated>\r\n <entry>\r\n <id>tag:localhost:3000:Post1</id>\r\n <published>2007-12-23T04:23:07+11:00</published>\r\n <updated>2007-12-30T15:29:55+11:00</updated>\r\n <link type=\"text/html\" rel=\"alternate\" href=\"http://localhost:3000/posts/1\"/>\r\n <title>First post</title>\r\n <content type=\"html\">Check out the first post</content>\r\n <author>\r\n <name>DHH</name>\r\n </author>\r\n </entry>\r\n</feed>\r\n---\r\n\r\nLet\'s run that through the \"feed validator\":http://feedvalidator.org/\r\n\r\n--- none\r\nline 3, column 25: id is not a valid TAG\r\nline 2, column 0: Missing atom:link with rel=\"self\"\r\nline 8, column 32: id is not a valid TAG\r\n---\r\n\r\nOh dear. Not a happy result. Let\'s fix it.\r\n\r\nProblem the first is the feed ID tag. It doesn\'t include a date, as per the \"Tag URI specification\":http://tools.ietf.org/html/rfc4151#section-2.1. This is a little bit tricky - you can\'t just add @Time.now.year@ as a default because that will change every year, and we need IDs to stay the same. We will provide an option to the user to specify the schema date, and produce a warning if they do not (as much as I\'d like to just break it, the pragmatic side of me keeps backwards compatibility in).\r\n\r\nThe entry tag has the same problem, but you\'ll also note it concatenates the class and the ID with no separator to create the ID. While it\'s an edge case, this will break if you have a class name ending in a number, so we need to add in a separator. I vote for a slash. Also, the port in the tag URI is inconsistent with the feed URI (no port), so remove it.\r\n\r\nFor further reading, I recommend \"How to make a good ID in Atom\":http://diveintomark.org/archives/2004/05/28/howto-atom-id.\r\n\r\nThe missing self link is just your garden variety bug - the documentation says it should be provided by default, but the code does not.\r\n\r\nI went ahead and fixed these problems. \"Changeset 8529\":http://dev.rubyonrails.org/changeset/8529. The example above, when you change the call to @atom_feed(:schema_date => 2008)@, looks like this.\r\n\r\n--- xml\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<feed xml:lang=\"en-US\" xmlns=\"http://www.w3.org/2005/Atom\">\r\n <id>tag:localhost:/posts</id>\r\n <link type=\"text/html\" rel=\"alternate\" href=\"http://localhost:3000\"/>\r\n <link type=\"application/atom+xml\" rel=\"self\" href=\"http://localhost:3000/posts.atom\"/>\r\n <title>My great blog!</title>\r\n <updated>2007-12-23T04:23:07+11:00</updated>\r\n <entry>\r\n <id>tag:localhost:Post/1</id>\r\n <published>2007-12-23T04:23:07+11:00</published>\r\n <updated>2007-12-30T15:29:55+11:00</updated>\r\n <link type=\"text/html\" rel=\"alternate\" href=\"http://localhost:3000/posts/1\"/>\r\n <title>First post</title>\r\n <content type=\"html\">HOORAY. About ruby.</content>\r\n <author>\r\n <name>DHH</name>\r\n </author>\r\n </entry>\r\n</feed>\r\n---\r\n\r\nmmm, semantic goodness','<p>Summary: <a href=\"http://dev.rubyonrails.org/changeset/8529\"><code>atom_feed</code> is broken until changeset 8529</a></p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"c\"># http://api.rubyonrails.org/classes/ActionView/Helpers/AtomFeedHelper.html#M000931</span><tt>\n</tt>atom_feed <span class=\"r\">do</span> |feed|<tt>\n</tt> feed.title(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">My great blog!</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt> feed.updated((<span class=\"iv\">@posts</span>.first.created_at))<tt>\n</tt><tt>\n</tt> <span class=\"r\">for</span> post <span class=\"r\">in</span> <span class=\"iv\">@posts</span><tt>\n</tt> feed.entry(post) <span class=\"r\">do</span> |entry|<tt>\n</tt> entry.title(post.title)<tt>\n</tt> entry.content(post.body, <span class=\"sy\">:type</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">html</span><span class=\"dl\">\'</span></span>)<tt>\n</tt><tt>\n</tt> entry.author <span class=\"r\">do</span> |author|<tt>\n</tt> author.name(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">DHH</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Produces the following feed (rails 2.0.2)</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"pp\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><tt>\n</tt><span class=\"ta\">&lt;feed</span> <span class=\"an\">xml:lang</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">en-US</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">xmlns</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://www.w3.org/2005/Atom</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;id&gt;</span>tag:localhost:posts<span class=\"ta\">&lt;/id&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;link</span> <span class=\"an\">type</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">text/html</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">rel</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">alternate</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">href</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://localhost:3000</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">/&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;title&gt;</span>My great blog!<span class=\"ta\">&lt;/title&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;updated&gt;</span>2007-12-23T04:23:07+11:00<span class=\"ta\">&lt;/updated&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;entry&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;id&gt;</span>tag:localhost:3000:Post1<span class=\"ta\">&lt;/id&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;published&gt;</span>2007-12-23T04:23:07+11:00<span class=\"ta\">&lt;/published&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;updated&gt;</span>2007-12-30T15:29:55+11:00<span class=\"ta\">&lt;/updated&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;link</span> <span class=\"an\">type</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">text/html</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">rel</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">alternate</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">href</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://localhost:3000/posts/1</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">/&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;title&gt;</span>First post<span class=\"ta\">&lt;/title&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;content</span> <span class=\"an\">type</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">html</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">&gt;</span>Check out the first post<span class=\"ta\">&lt;/content&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;author&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;name&gt;</span>DHH<span class=\"ta\">&lt;/name&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;/author&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;/entry&gt;</span><tt>\n</tt><span class=\"ta\">&lt;/feed&gt;</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Let&#8217;s run that through the <a href=\"http://feedvalidator.org/\">feed validator</a></p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">line 3, column 25: id is not a valid TAG<tt>\n</tt>line 2, column 0: Missing atom:link with rel=&quot;self&quot;<tt>\n</tt>line 8, column 32: id is not a valid TAG<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Oh dear. Not a happy result. Let&#8217;s fix it.</p>\n\n\n <p>Problem the first is the feed ID tag. It doesn&#8217;t include a date, as per the <a href=\"http://tools.ietf.org/html/rfc4151#section-2.1\">Tag <span class=\"caps\">URI</span> specification</a>. This is a little bit tricky &#8211; you can&#8217;t just add <code>Time.now.year</code> as a default because that will change every year, and we need IDs to stay the same. We will provide an option to the user to specify the schema date, and produce a warning if they do not (as much as I&#8217;d like to just break it, the pragmatic side of me keeps backwards compatibility in).</p>\n\n\n <p>The entry tag has the same problem, but you&#8217;ll also note it concatenates the class and the ID with no separator to create the ID. While it&#8217;s an edge case, this will break if you have a class name ending in a number, so we need to add in a separator. I vote for a slash. Also, the port in the tag <span class=\"caps\">URI</span> is inconsistent with the feed <span class=\"caps\">URI</span> (no port), so remove it.</p>\n\n\n <p>For further reading, I recommend <a href=\"http://diveintomark.org/archives/2004/05/28/howto-atom-id\">How to make a good ID in Atom</a>.</p>\n\n\n <p>The missing self link is just your garden variety bug &#8211; the documentation says it should be provided by default, but the code does not.</p>\n\n\n <p>I went ahead and fixed these problems. <a href=\"http://dev.rubyonrails.org/changeset/8529\">Changeset 8529</a>. The example above, when you change the call to <code>atom_feed(:schema_date =&gt; 2008)</code>, looks like this.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"pp\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><tt>\n</tt><span class=\"ta\">&lt;feed</span> <span class=\"an\">xml:lang</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">en-US</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">xmlns</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://www.w3.org/2005/Atom</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;id&gt;</span>tag:localhost:/posts<span class=\"ta\">&lt;/id&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;link</span> <span class=\"an\">type</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">text/html</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">rel</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">alternate</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">href</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://localhost:3000</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">/&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;link</span> <span class=\"an\">type</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">application/atom+xml</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">rel</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">self</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">href</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://localhost:3000/posts.atom</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">/&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;title&gt;</span>My great blog!<span class=\"ta\">&lt;/title&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;updated&gt;</span>2007-12-23T04:23:07+11:00<span class=\"ta\">&lt;/updated&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;entry&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;id&gt;</span>tag:localhost:Post/1<span class=\"ta\">&lt;/id&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;published&gt;</span>2007-12-23T04:23:07+11:00<span class=\"ta\">&lt;/published&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;updated&gt;</span>2007-12-30T15:29:55+11:00<span class=\"ta\">&lt;/updated&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;link</span> <span class=\"an\">type</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">text/html</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">rel</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">alternate</span><span class=\"dl\">&quot;</span></span> <span class=\"an\">href</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">http://localhost:3000/posts/1</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">/&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;title&gt;</span>First post<span class=\"ta\">&lt;/title&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;content</span> <span class=\"an\">type</span>=<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">html</span><span class=\"dl\">&quot;</span></span><span class=\"ta\">&gt;</span>HOORAY. About ruby.<span class=\"ta\">&lt;/content&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;author&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;name&gt;</span>DHH<span class=\"ta\">&lt;/name&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;/author&gt;</span><tt>\n</tt> <span class=\"ta\">&lt;/entry&gt;</span><tt>\n</tt><span class=\"ta\">&lt;/feed&gt;</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>mmm, semantic goodness</p>',1,2,NULL,'2008-01-02 21:15:00','2008-01-02 11:42:45','2008-01-02 21:20:47'),(374,'Unobtrusive live comment preview with jQuery','unobtrusive-live-comment-preview-with-jquery','Live preview is shiny. First get your self a URL that renders a comment. In rails maybe something like the following.\r\n\r\n--- ruby\r\ndef new\r\n @comment = Comment.build_for_preview(params[:comment])\r\n\r\n respond_to do |format|\r\n format.js do\r\n render :partial => \'comment.html.erb\'\r\n end\r\n end\r\nend\r\n---\r\n\r\nNow you should have a form or div with an ID something like \"new_comment\". Just drop in the following JS (you may need to customize the @submit_url@).\r\n\r\n--- javascript\r\n$(function() { // onload\r\n var comment_form = $(\'#new_comment\')\r\n var input_elements = comment_form.find(\':text, textarea\')\r\n var submit_url = \'/comments/new\' \r\n \r\n var fetch_comment_preview = function() {\r\n jQuery.ajax({\r\n data: comment_form.serialize(),\r\n url: submit_url,\r\n timeout: 2000,\r\n error: function() {\r\n console.log(\"Failed to submit\");\r\n },\r\n success: function(r) { \r\n if ($(\'#comment-preview\').length == 0) {\r\n comment_form.after(\'<h2>Your comment will look like this:</h2><div id=\"comment-preview\"></div>\')\r\n }\r\n $(\'#comment-preview\').html(r)\r\n }\r\n })\r\n }\r\n\r\n input_elements.keyup(function () {\r\n fetch_comment_preview.only_every(1000);\r\n })\r\n if (input_elements.any(function() { return $(this).val().length > 0 }))\r\n fetch_comment_preview();\r\n})\r\n---\r\n\r\nThe @only_every@ function is they key to this piece - it ensures that an AJAX request will be sent at most only once a second so you don\'t overload your server or your client\'s connection.\r\n\r\nObviously you\'ll need \"jQuery\":http://jquery.com, less obviously you\'ll also need these support functions\r\n\r\n--- javascript\r\n// Based on http://www.germanforblack.com/javascript-sleeping-keypress-delays-and-bashing-bad-articles\r\nFunction.prototype.only_every = function (millisecond_delay) {\r\n if (!window.only_every_func)\r\n {\r\n var function_object = this;\r\n window.only_every_func = setTimeout(function() { function_object(); window.only_every_func = null}, millisecond_delay);\r\n }\r\n};\r\n\r\n// jQuery extensions\r\njQuery.prototype.any = function(callback) { \r\n return (this.filter(callback).length > 0)\r\n}\r\n---\r\n\r\nViola, now you\'re shimmering in awesomeness.\r\nDemo up soon, but it\'s similar to what you see on this blog (though this blog is done with inline prototype).','<p>Live preview is shiny. First get your self a <span class=\"caps\">URL</span> that renders a comment. In rails maybe something like the following.</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\"><span class=\"r\">def</span> <span class=\"fu\">new</span><tt>\n</tt> <span class=\"iv\">@comment</span> = <span class=\"co\">Comment</span>.build_for_preview(params[<span class=\"sy\">:comment</span>])<tt>\n</tt><tt>\n</tt> respond_to <span class=\"r\">do</span> |format|<tt>\n</tt> format.js <span class=\"r\">do</span><tt>\n</tt> render <span class=\"sy\">:partial</span> =&gt; <span class=\"s\"><span class=\"dl\">\'</span><span class=\"k\">comment.html.erb</span><span class=\"dl\">\'</span></span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt> <span class=\"r\">end</span><tt>\n</tt><span class=\"r\">end</span><tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Now you should have a form or div with an ID something like &#8220;new_comment&#8221;. Just drop in the following JS (you may need to customize the <code>submit_url</code>).</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt>14<tt>\n</tt>15<tt>\n</tt>16<tt>\n</tt>17<tt>\n</tt>18<tt>\n</tt>19<tt>\n</tt>20<tt>\n</tt>21<tt>\n</tt>22<tt>\n</tt>23<tt>\n</tt>24<tt>\n</tt>25<tt>\n</tt>26<tt>\n</tt>27<tt>\n</tt>28<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">$(function() { // onload<tt>\n</tt> var comment_form = $(\'#new_comment\')<tt>\n</tt> var input_elements = comment_form.find(\':text, textarea\')<tt>\n</tt> var submit_url = \'/comments/new\' <tt>\n</tt> <tt>\n</tt> var fetch_comment_preview = function() {<tt>\n</tt> jQuery.ajax({<tt>\n</tt> data: comment_form.serialize(),<tt>\n</tt> url: submit_url,<tt>\n</tt> timeout: 2000,<tt>\n</tt> error: function() {<tt>\n</tt> console.log(&quot;Failed to submit&quot;);<tt>\n</tt> },<tt>\n</tt> success: function(r) { <tt>\n</tt> if ($(\'#comment-preview\').length == 0) {<tt>\n</tt> comment_form.after(\'&lt;h2&gt;Your comment will look like this:&lt;/h2&gt;&lt;div id=&quot;comment-preview&quot;&gt;&lt;/div&gt;\')<tt>\n</tt> }<tt>\n</tt> $(\'#comment-preview\').html(r)<tt>\n</tt> }<tt>\n</tt> })<tt>\n</tt> }<tt>\n</tt><tt>\n</tt> input_elements.keyup(function () {<tt>\n</tt> fetch_comment_preview.only_every(1000);<tt>\n</tt> })<tt>\n</tt> if (input_elements.any(function() { return $(this).val().length &gt; 0 }))<tt>\n</tt> fetch_comment_preview();<tt>\n</tt>})<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>The <code>only_every</code> function is they key to this piece &#8211; it ensures that an <span class=\"caps\">AJAX</span> request will be sent at most only once a second so you don&#8217;t overload your server or your client&#8217;s connection.</p>\n\n\n <p>Obviously you&#8217;ll need <a href=\"http://jquery.com\">jQuery</a>, less obviously you&#8217;ll also need these support functions</p>\n\n\n<table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt>5<tt>\n</tt>6<tt>\n</tt>7<tt>\n</tt>8<tt>\n</tt>9<tt>\n</tt>10<tt>\n</tt>11<tt>\n</tt>12<tt>\n</tt>13<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">// Based on http://www.germanforblack.com/javascript-sleeping-keypress-delays-and-bashing-bad-articles<tt>\n</tt>Function.prototype.only_every = function (millisecond_delay) {<tt>\n</tt> if (!window.only_every_func)<tt>\n</tt> {<tt>\n</tt> var function_object = this;<tt>\n</tt> window.only_every_func = setTimeout(function() { function_object(); window.only_every_func = null}, millisecond_delay);<tt>\n</tt> }<tt>\n</tt>};<tt>\n</tt><tt>\n</tt>// jQuery extensions<tt>\n</tt>jQuery.prototype.any = function(callback) { <tt>\n</tt> return (this.filter(callback).length &gt; 0)<tt>\n</tt>}<tt>\n</tt></pre></td>\n</tr></table>\n\n\n <p>Viola, now you&#8217;re shimmering in awesomeness.\nDemo up soon, but it&#8217;s similar to what you see on this blog (though this blog is done with inline prototype).</p>',1,2,NULL,'2008-01-09 12:35:00','2008-01-09 12:35:05','2008-01-09 13:46:47'),(390,'Absence, with suitable recompense','absence-with-suitable-recompense','I\'m going on holidays until the end of January. The off line kind of holiday where I don\'t see a computer. So sad.\r\n\r\nSo here is a tasty treat for you to devour until I return. A sneak preview of a Fashionable New Blogging App (tm) named \"Enki\":http://www.enkiblog.com. It is an alternative to Mephisto and SimpleLog that is built on the principles espoused in my \"prior writings\":http://rhnh.net/2008/1/2/i-don-t-want-preferences. The website is built using Enki itself, and the port of this site from mephisto is just about finished, so you know you\'re getting code that\'s got a real life application. There\'s still a few rough edges, but it\'s ready enough to start building something with if you don\'t mind getting your hands a little dirty. I\'ve set up a mailing list for it which I\'ll be catching up on once I get back.','<p>I&#8217;m going on holidays until the end of January. The off line kind of holiday where I don&#8217;t see a computer. So sad.</p>\n\n\n <p>So here is a tasty treat for you to devour until I return. A sneak preview of a Fashionable New Blogging App&#8482; named <a href=\"http://www.enkiblog.com\">Enki</a>. It is an alternative to Mephisto and SimpleLog that is built on the principles espoused in my <a href=\"http://rhnh.net/2008/1/2/i-don-t-want-preferences\">prior writings</a>. The website is built using Enki itself, and the port of this site from mephisto is just about finished, so you know you&#8217;re getting code that&#8217;s got a real life application. There&#8217;s still a few rough edges, but it&#8217;s ready enough to start building something with if you don&#8217;t mind getting your hands a little dirty. I&#8217;ve set up a mailing list for it which I&#8217;ll be catching up on once I get back.</p>',1,0,NULL,'2008-01-19 08:17:00','2008-01-19 08:11:42','2008-01-19 08:17:40'),(429,'Seagate 500Gb FreeAgent Pro external drive - first impressions','seagate-500gb-freeagent-pro-external-drive-first-impressions','It has a stupid name. The title is the first and last time I will refer to it as anything other than a \"Seagate 500Gb external drive\". What is not stupid is the packaging. It\'s clear, concise, fun, and most importantly makes me feel like Seagate actually cares about the people who use its products. Observe the following shots of the static packaging and the instruction booklet:\r\n\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2235621041/\" title=\"Packaging by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2171/2235621041_754b605d2a_m.jpg\" width=\"240\" height=\"218\" alt=\"Packaging\" /></a>\r\n\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2236410986/\" title=\"Instructions #1 by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2224/2236410986_b40c4851fa.jpg\" width=\"500\" height=\"166\" alt=\"Instructions #1\" /></a>\r\n\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2235620975/\" title=\"Instructions #2 by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2210/2235620975_b37849eb27.jpg\" width=\"500\" height=\"167\" alt=\"Instructions #2\" /></a>\r\n\r\nText on the last frame says: _\"Note: Times may vary depending on how excited you are about using your new FreeAgent Pro data mover.\"_ Delicious.\r\n\r\nI had to format it as FAT32 because as far as I can tell OSX doesn\'t support writing to NTFS volumes. This makes me sad. I presume linux can write to Mac\'s filesystem, but AFAIK windows can\'t, which unfortunately I need to support because that\'s what all my family use :( No fault of the drive here, just another windows gripe. Although linux has had NTFS write support stable for a while now, I wouldn\'t mind Mac catching up.\r\n\r\nIt is much quieter than I expected. It\'s under full load right now - I\'m rsyncing to it.\r\n\r\n5 year warranty, so I guess they have confidence in the product.\r\n\r\nInitial impression is positive, ask me again when I actually have to restore from it.\r\n\r\n*Unrelated footnote:* Technically I\'m back from my holiday, but I\'m snowed under with dancing commitments for now so coding updates (and enki updates) will still be sporadic.\r\n\r\n*UPDATE* Just reformatted for Time Machine, YAGNIed the work-with-family requirement.','<p>It has a stupid name. The title is the first and last time I will refer to it as anything other than a &#8220;Seagate 500Gb external drive&#8221;. What is not stupid is the packaging. It&#8217;s clear, concise, fun, and most importantly makes me feel like Seagate actually cares about the people who use its products. Observe the following shots of the static packaging and the instruction booklet:</p>\n\n\n <p><a href=\"http://www.flickr.com/photos/18919286@N08/2235621041/\" title=\"Packaging by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2171/2235621041_754b605d2a_m.jpg\" width=\"240\" height=\"218\" alt=\"Packaging\" /></a></p>\n\n\n <p><a href=\"http://www.flickr.com/photos/18919286@N08/2236410986/\" title=\"Instructions #1 by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2224/2236410986_b40c4851fa.jpg\" width=\"500\" height=\"166\" alt=\"Instructions #1\" /></a></p>\n\n\n <p><a href=\"http://www.flickr.com/photos/18919286@N08/2235620975/\" title=\"Instructions #2 by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2210/2235620975_b37849eb27.jpg\" width=\"500\" height=\"167\" alt=\"Instructions #2\" /></a></p>\n\n\n <p>Text on the last frame says: <em>&#8220;Note: Times may vary depending on how excited you are about using your new FreeAgent Pro data mover.&#8221;</em> Delicious.</p>\n\n\n <p>I had to format it as <span class=\"caps\">FAT32</span> because as far as I can tell <span class=\"caps\">OSX</span> doesn&#8217;t support writing to <span class=\"caps\">NTFS</span> volumes. This makes me sad. I presume linux can write to Mac&#8217;s filesystem, but <span class=\"caps\">AFAIK</span> windows can&#8217;t, which unfortunately I need to support because that&#8217;s what all my family use :( No fault of the drive here, just another windows gripe. Although linux has had <span class=\"caps\">NTFS</span> write support stable for a while now, I wouldn&#8217;t mind Mac catching up.</p>\n\n\n <p>It is much quieter than I expected. It&#8217;s under full load right now &#8211; I&#8217;m rsyncing to it.</p>\n\n\n <p>5 year warranty, so I guess they have confidence in the product.</p>\n\n\n <p>Initial impression is positive, ask me again when I actually have to restore from it.</p>\n\n\n <p><strong>Unrelated footnote:</strong> Technically I&#8217;m back from my holiday, but I&#8217;m snowed under with dancing commitments for now so coding updates (and enki updates) will still be sporadic.</p>\n\n\n <p><strong><span class=\"caps\">UPDATE</span></strong> Just reformatted for Time Machine, YAGNIed the work-with-family requirement.</p>',1,2,NULL,'2008-02-02 07:24:00','2008-02-02 07:24:00','2008-03-29 03:54:44'),(465,'Coconut Oats','coconut-oats','A more appropriate name may be \"Ghetto Dessert #1\". Once again, I neglected the supermarket and tried cooking with whatever was in the cupboards.\r\n\r\nh3. Coconut Oats\r\n\r\nServes 1-2\r\n\r\nh4. Ingredients\r\n\r\n * 1 bowl of oats\r\n * 1/2 can coconut milk\r\n * Caster sugar + maple Syrup *OR* brown sugar + cocoa\r\n\r\nh4. Method\r\n\r\n # Soak oats in coconut milk until it is absorbed (longer is better, I left mine for about 90 minutes)\r\n # Mix in your choice of condiments\r\n\r\nI experimented with a few different sweeteners - the four listed above individually and also honey. Honey didn\'t work so well, but the 2 combinations above I think were winners. Adding fruit to the maple syrup variant would be particularly tasty, but we\'re never home enough to have fruit on hand. I\'m going to try turning the chocolate one into porridge by warming it in a saucepan. I have another bowl sitting in the fridge that I\'m going to leave overnight a la bircher muesli to see just how much the oats can absorb.\r\n\r\nAnd for bonus points it\'s vegan - a rare property of my desserts.\r\n\r\n*UPDATE:* Leaving overnight is highly recommended. A tasty non-vegan option is mixing in nutella.\r\n\r\n*UPDATE 2:* Mix in castor sugar, dried apricot and cranberries, then serve with shredded coconut and flaked almonds. This is the best one.','<p>A more appropriate name may be &#8220;Ghetto Dessert #1&#8221;. Once again, I neglected the supermarket and tried cooking with whatever was in the cupboards.</p>\n\n\n <h3>Coconut Oats</h3>\n\n\n <p>Serves 1-2</p>\n\n\n <h4>Ingredients</h4>\n\n\n <ul>\n <li>1 bowl of oats</li>\n <li>1/2 can coconut milk</li>\n <li>Caster sugar + maple Syrup <strong>OR</strong> brown sugar + cocoa</li>\n </ul>\n\n\n <h4>Method</h4>\n\n\n <ol>\n <li>Soak oats in coconut milk until it is absorbed (longer is better, I left mine for about 90 minutes)</li>\n <li>Mix in your choice of condiments</li>\n </ol>\n\n\n <p>I experimented with a few different sweeteners &#8211; the four listed above individually and also honey. Honey didn&#8217;t work so well, but the 2 combinations above I think were winners. Adding fruit to the maple syrup variant would be particularly tasty, but we&#8217;re never home enough to have fruit on hand. I&#8217;m going to try turning the chocolate one into porridge by warming it in a saucepan. I have another bowl sitting in the fridge that I&#8217;m going to leave overnight a la bircher muesli to see just how much the oats can absorb.</p>\n\n\n <p>And for bonus points it&#8217;s vegan &#8211; a rare property of my desserts.</p>\n\n\n <p><strong><span class=\"caps\">UPDATE</span>:</strong> Leaving overnight is highly recommended. A tasty non-vegan option is mixing in nutella.</p>\n\n\n <p><strong><span class=\"caps\">UPDATE 2</span>:</strong> Mix in castor sugar, dried apricot and cranberries, then serve with shredded coconut and flaked almonds. This is the best one.</p>',1,1,NULL,'2008-02-09 08:58:00','2008-02-09 08:57:17','2008-03-29 03:53:28'),(507,'Mary Iron Chef - Chocolate Jaffa Boxes','chocolate-jaffa-boxes','<a href=\"http://www.flickr.com/photos/18919286@N08/2284134303/\" title=\"Mary at Kenneth Falls by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2133/2284134303_29a38c673b_m.jpg\" width=\"240\" height=\"180\" alt=\"Mary at Kenneth Falls\" class=\'inline\' /></a>\r\nThe picturesque Otways served an inspiring back drop to the inaugural *Mary Iron Chef Challenge*. Tension was high - I had teamed up with the renowned dessert specialist Amelia Ie, pitted against the young superstar couple Yujin and Katie (<a href=\"http://flickr.com/photos/18919286@N08/2284975622/\" title=\"The 4 chefs\">photo</a>). Chairman Tim flamboyantly revealed the challenge ingredient - <a href=\"http://flickr.com/photos/18919286@N08/2284187997/\" title=\"Challenge ingredient photo\">Chocolate!</a> - and with a bang of the saucepan lid gong started the 90 minute Timer Of Impending Dessert.\r\n\r\nAmelia and I made 3 dishes for this challenge. Our crowning achievement were the Chocolate Jaffa Boxes. As a judge gushed - \'the rich velvet couverture of the enclosure frolics playfully with the airy mousse, while the mango reminds me of the playful delights of summer\'. Accept that translation at your own risk.\r\n\r\nh3. Chocolate Jaffa Boxes\r\n\r\nMakes 8\r\n\r\nh4. Ingredients\r\n\r\n * 500g dark chocolate, melted\r\n * 250g milk chocolate, melted\r\n * 1 packed orange jelly crystals\r\n * Generous splash of brandy\r\n * 500ml thickened cream\r\n * 1 Mango\r\n\r\nh4. Method\r\n\r\n # Spread dark chocolate thinly over 2 trays covered in foil, saving a small amount for later. Refrigerate until solid - this will become the boxes.\r\n # Whisk cream until fluffy (use electric beaters)\r\n # Mix together brandy and jelly crystals, then dissolve crystals in microwave (takes about a minute). Inhale fumes deeply.\r\n # Add jelly mix to milk chocolate, then fold in half of the cream. You fold rather than stir because it helps keep the mixture aerated.\r\n # This bit takes some geometric nouse - take the solid dark chocolate out of the fridge and with a sharp knife divide each tray into 40 portions - groups of 5 will be used to make each box. A diagram here would be nice but I don\'t have the tools. The base portion can be bigger than the other 4, as long as they all come from the same strip so that they have the same edge length. Take your time with this step because you don\'t want to shatter any of the pieces.\r\n # Assemble each group of 5 portions into a box, using the left over melted chocolate to stick them together. Lookout, here comes some math: 40 x 2 / 5 = 8 boxes.\r\n # Spoon chocolate mix in to each box, then add a dollop of cream to each\r\n # Slice up the mango and arrange it NICELY on the top of each box\r\n # Refrigerate until the chocolate mix sets (we didn\'t do this because we only had 90 minutes, but the ones we left overnight were *much* tastier)\r\n\r\nThis challenge was a lot of fun. We got to wear funny hats. Special thanks to Amelia, without whose kitchen mastery I would have probably just served chocolate pieces in a bowl.\r\n\r\n <a href=\"http://www.flickr.com/photos/18919286@N08/2284097379/\" title=\"Iron Chef - Chocolate Jaffa Box by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2111/2284097379_9a3265d524_m.jpg\" width=\"240\" height=\"180\" alt=\"Iron Chef - Chocolate Jaffa Box\" /></a>\r\n\r\n_Apologies for the absence of tech posts lately, that\'s just how life is at the moment. Hopefully have something geekier to write about soon._','<p><a href=\"http://www.flickr.com/photos/18919286@N08/2284134303/\" title=\"Mary at Kenneth Falls by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2133/2284134303_29a38c673b_m.jpg\" width=\"240\" height=\"180\" alt=\"Mary at Kenneth Falls\" class=\'inline\' /></a>\nThe picturesque Otways served an inspiring back drop to the inaugural <strong>Mary Iron Chef Challenge</strong>. Tension was high &#8211; I had teamed up with the renowned dessert specialist Amelia Ie, pitted against the young superstar couple Yujin and Katie (<a href=\"http://flickr.com/photos/18919286@N08/2284975622/\" title=\"The 4 chefs\">photo</a>). Chairman Tim flamboyantly revealed the challenge ingredient &#8211; <a href=\"http://flickr.com/photos/18919286@N08/2284187997/\" title=\"Challenge ingredient photo\">Chocolate!</a> &#8211; and with a bang of the saucepan lid gong started the 90 minute Timer Of Impending Dessert.</p>\n\n\n <p>Amelia and I made 3 dishes for this challenge. Our crowning achievement were the Chocolate Jaffa Boxes. As a judge gushed &#8211; &#8216;the rich velvet couverture of the enclosure frolics playfully with the airy mousse, while the mango reminds me of the playful delights of summer&#8217;. Accept that translation at your own risk.</p>\n\n\n <h3>Chocolate Jaffa Boxes</h3>\n\n\n <p>Makes 8</p>\n\n\n <h4>Ingredients</h4>\n\n\n <ul>\n <li>500g dark chocolate, melted</li>\n <li>250g milk chocolate, melted</li>\n <li>1 packed orange jelly crystals</li>\n <li>Generous splash of brandy</li>\n <li>500ml thickened cream</li>\n <li>1 Mango</li>\n </ul>\n\n\n <h4>Method</h4>\n\n\n <ol>\n <li>Spread dark chocolate thinly over 2 trays covered in foil, saving a small amount for later. Refrigerate until solid &#8211; this will become the boxes.</li>\n <li>Whisk cream until fluffy (use electric beaters)</li>\n <li>Mix together brandy and jelly crystals, then dissolve crystals in microwave (takes about a minute). Inhale fumes deeply.</li>\n <li>Add jelly mix to milk chocolate, then fold in half of the cream. You fold rather than stir because it helps keep the mixture aerated.</li>\n <li>This bit takes some geometric nouse &#8211; take the solid dark chocolate out of the fridge and with a sharp knife divide each tray into 40 portions &#8211; groups of 5 will be used to make each box. A diagram here would be nice but I don&#8217;t have the tools. The base portion can be bigger than the other 4, as long as they all come from the same strip so that they have the same edge length. Take your time with this step because you don&#8217;t want to shatter any of the pieces.</li>\n <li>Assemble each group of 5 portions into a box, using the left over melted chocolate to stick them together. Lookout, here comes some math: 40&#215;2 / 5 = 8 boxes.</li>\n <li>Spoon chocolate mix in to each box, then add a dollop of cream to each</li>\n <li>Slice up the mango and arrange it <span class=\"caps\">NICELY</span> on the top of each box</li>\n <li>Refrigerate until the chocolate mix sets (we didn&#8217;t do this because we only had 90 minutes, but the ones we left overnight were <strong>much</strong> tastier)</li>\n </ol>\n\n\n <p>This challenge was a lot of fun. We got to wear funny hats. Special thanks to Amelia, without whose kitchen mastery I would have probably just served chocolate pieces in a bowl.</p>\n\n\n <pre><code><a href=\"http://www.flickr.com/photos/18919286@N08/2284097379/\" title=\"Iron Chef - Chocolate Jaffa Box by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2111/2284097379_9a3265d524_m.jpg\" width=\"240\" height=\"180\" alt=\"Iron Chef - Chocolate Jaffa Box\" /></a></code></pre>\n\n\n <p><em>Apologies for the absence of tech posts lately, that&#8217;s just how life is at the moment. Hopefully have something geekier to write about soon.</em></p>',1,1,NULL,'2008-02-22 23:58:00','2008-02-22 23:45:40','2008-02-23 00:16:35'),(660,'Paypal IPN fails date standards','paypal-ipn-fails-date-standards','Paypal Instant Payment Notification lets you know when you have received a paypal payment. Presumably, you then mark an order as paid or something. Do not use the current time as the @paid_at@ date - despite the \'instant\' in the title it can be many days later. You should use the @payment_date@ provided by paypal. Your accountant will thank you.\r\n\r\nBut here\'s the rub. From the IPN spec, @payment_date@ is:\r\nbq. Time/Date stamp generated by PayPal system [format: \"18:30:30 Jan 1, 2000 PST\"] \r\n\r\nSeen that date format before? No? Didn\'t think so. That\'s no RFC I\'ve seen before. The popular \"Paypal gem\":http://dist.leetsoft.com/api/paypal/ uses @Time.parse@, but this is incorrect (as of 2.0.0). Observe:\r\n\r\n--- ruby\r\n>> Time.parse(\"18:30:30 Mar 28, 2008 PST\")\r\n=> Fri Mar 28 18:30:30 1100 2008 # Good\r\n>> Time.parse(\"18:30:30 Feb 28, 2008 PST\")\r\n=> Fri Mar 28 18:30:30 1100 2008 # FAIL\r\n---\r\n\r\nAlso, @Time@ only has a range of about a week, so that could screw you over come any major system failures (either you or paypal). Also note the @payment_date@ is in PST, which unless you\'re on the right side of the US is fairly useless. I recommend the following:\r\n\r\n--- ruby\r\n>> DateTime.strptime(\"18:30:30 Jan 1, 2000 PST\", \"%H:%M:%S %b %e, %Y %Z\").new_offset(0)\r\n=> Sun, 02 Jan 2000 02:30:30 0000\r\n---\r\n\r\nThe un-intuitive @new_offset@ converts to UTC. Patch submitted. I hate you, Paypal.','<p>Paypal Instant Payment Notification lets you know when you have received a paypal payment. Presumably, you then mark an order as paid or something. Do not use the current time as the <code>paid_at</code> date &#8211; despite the &#8216;instant&#8217; in the title it can be many days later. You should use the <code>payment_date</code> provided by paypal. Your accountant will thank you.</p>\n\n\nBut here&#8217;s the rub. From the <span class=\"caps\">IPN</span> spec, <code>payment_date</code> is:\n <blockquote>\n <p>Time/Date stamp generated by PayPal system [format: &#8220;18:30:30 Jan 1, 2000 <span class=\"caps\">PST</span>&#8221;]</p>\n </blockquote>\n\n\n <p>Seen that date format before? No? Didn&#8217;t think so. That&#8217;s no <span class=\"caps\">RFC I</span>&#8217;ve seen before. The popular <a href=\"http://dist.leetsoft.com/api/paypal/\">Paypal gem</a> uses <code>Time.parse</code>, but this is incorrect (as of 2.0.0). Observe:</p><table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt>3<tt>\n</tt>4<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&gt;&gt; <span class=\"co\">Time</span>.parse(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">18:30:30 Mar 28, 2008 PST</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt>=&gt; <span class=\"co\">Fri</span> <span class=\"co\">Mar</span> <span class=\"i\">28</span> <span class=\"i\">18</span>:<span class=\"i\">30</span>:<span class=\"i\">30</span> <span class=\"i\">1100</span> <span class=\"i\">2008</span> <span class=\"c\"># Good</span><tt>\n</tt>&gt;&gt; <span class=\"co\">Time</span>.parse(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">18:30:30 Feb 28, 2008 PST</span><span class=\"dl\">&quot;</span></span>)<tt>\n</tt>=&gt; <span class=\"co\">Fri</span> <span class=\"co\">Mar</span> <span class=\"i\">28</span> <span class=\"i\">18</span>:<span class=\"i\">30</span>:<span class=\"i\">30</span> <span class=\"i\">1100</span> <span class=\"i\">2008</span> <span class=\"c\"># FAIL</span><tt>\n</tt></pre></td>\n</tr></table>\n<p>Also, <code>Time</code> only has a range of about a week, so that could screw you over come any major system failures (either you or paypal). Also note the <code>payment_date</code> is in <span class=\"caps\">PST</span>, which unless you&#8217;re on the right side of the US is fairly useless. I recommend the following:</p><table class=\"CodeRay\"><tr>\n <td class=\"line_numbers\" title=\"click to toggle\" onclick=\"with (this.firstChild.style) { display = (display == \'\') ? \'none\' : \'\' }\"><pre>1<tt>\n</tt>2<tt>\n</tt></pre></td>\n <td class=\"code\"><pre ondblclick=\"with (this.style) { overflow = (overflow == \'auto\' || overflow == \'\') ? \'visible\' : \'auto\' }\">&gt;&gt; <span class=\"co\">DateTime</span>.strptime(<span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">18:30:30 Jan 1, 2000 PST</span><span class=\"dl\">&quot;</span></span>, <span class=\"s\"><span class=\"dl\">&quot;</span><span class=\"k\">%H:%M:%S %b %e, %Y %Z</span><span class=\"dl\">&quot;</span></span>).new_offset(<span class=\"i\">0</span>)<tt>\n</tt>=&gt; <span class=\"co\">Sun</span>, <span class=\"i\">02</span> <span class=\"co\">Jan</span> <span class=\"i\">2000</span> <span class=\"i\">02</span>:<span class=\"i\">30</span>:<span class=\"i\">30</span> <span class=\"i\">0000</span><tt>\n</tt></pre></td>\n</tr></table>\n<p>The un-intuitive <code>new_offset</code> converts to <span class=\"caps\">UTC</span>. Patch submitted. I hate you, Paypal.</p>',1,1,'Home, Code, Ruby','2008-03-17 06:24:00','2008-03-17 06:24:44','2008-04-12 11:23:09'),(754,'I\'m number 8!','i-m-number-8','I had no idea \"Working With Rails\":http://workingwithrails.com ran a monthly hackfest. Basically, you contribute to rails, you get points, at the end of the month you can win stuff. To my surprise, I came in at \"#8 last month\":http://workingwithrails.com/hackfest/22-monthly-february-2-8 and got a free copy of \"Make\" magazine from O\'Reilly.\r\n\r\nSweet. Thank you doc patches.\r\n\r\nObligatory thumbs-up-with-swag photo:\r\n\r\n<a href=\"http://www.flickr.com/photos/18919286@N08/2367199683/\" title=\"Working With Rails Hackfest Prize by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2316/2367199683_3bb7d74ba7_m.jpg\" width=\"240\" height=\"180\" alt=\"Working With Rails Hackfest Prize\" /></a>','<p>I had no idea <a href=\"http://workingwithrails.com\">Working With Rails</a> ran a monthly hackfest. Basically, you contribute to rails, you get points, at the end of the month you can win stuff. To my surprise, I came in at <a href=\"http://workingwithrails.com/hackfest/22-monthly-february-2-8\">#8 last month</a> and got a free copy of &#8220;Make&#8221; magazine from O&#8217;Reilly.</p>\n\n\n <p>Sweet. Thank you doc patches.</p>\n\n\n <p>Obligatory thumbs-up-with-swag photo:</p>\n\n\n <p><a href=\"http://www.flickr.com/photos/18919286@N08/2367199683/\" title=\"Working With Rails Hackfest Prize by Xavier Shay, on Flickr\"><img src=\"http://farm3.static.flickr.com/2316/2367199683_3bb7d74ba7_m.jpg\" width=\"240\" height=\"180\" alt=\"Working With Rails Hackfest Prize\" /></a></p>',1,0,NULL,'2008-03-28 02:41:00','2008-03-28 02:40:36','2008-03-28 02:41:44');
/*!40000 ALTER TABLE `posts` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `schema_info`
--
DROP TABLE IF EXISTS `schema_info`;
CREATE TABLE `schema_info` (
`version` int(11) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `schema_info`
--
LOCK TABLES `schema_info` WRITE;
/*!40000 ALTER TABLE `schema_info` DISABLE KEYS */;
INSERT INTO `schema_info` VALUES (6);
/*!40000 ALTER TABLE `schema_info` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `sessions`
--
DROP TABLE IF EXISTS `sessions`;
CREATE TABLE `sessions` (
`id` int(11) NOT NULL auto_increment,
`session_id` varchar(255) NOT NULL,
`data` text,
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `index_sessions_on_session_id` (`session_id`),
KEY `index_sessions_on_updated_at` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `sessions`
--
LOCK TABLES `sessions` WRITE;
/*!40000 ALTER TABLE `sessions` DISABLE KEYS */;
INSERT INTO `sessions` VALUES (1,'20c860048693a3437516651fd0939d84','BAh7CDoTb3BlbmlkX3Nlc3Npb257ByIuT3BlbklEOjpDb25zdW1lcjo6bGFz\ndF9yZXF1ZXN0ZWRfZW5kcG9pbnQwIj1PcGVuSUQ6OkNvbnN1bWVyOjpEaXNj\nb3ZlcmVkU2VydmljZXM6Ok9wZW5JRDo6Q29uc3VtZXI6OjAiCmZsYXNoSUM6\nJ0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2Vk\newA6DmxvZ2dlZF9pblQ=\n','2008-02-21 19:16:35','2008-02-21 21:31:40'),(2,'94762a95aafe7f9369c38d3d7f2bd7da','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:46:26','2008-02-21 19:46:26'),(3,'1a89040740fc7e715a8e5a7ddbfd6132','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:46:33','2008-02-21 19:46:33'),(4,'25ed5b84e32b9638920ab8429a21d108','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:46:35','2008-02-21 19:46:35'),(5,'8dba1afa19f4bce1ac690c83ca5521da','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:46:47','2008-02-21 19:46:47'),(6,'746d46b0b729e08253155be8eb4079d7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:47:11','2008-02-21 19:47:11'),(7,'f16accb504f5b0a160d77da082aa5bcb','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:47:15','2008-02-21 19:47:15'),(8,'49197889e0178d44fc6f7272e91b6f27','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:49:17','2008-02-21 19:49:17'),(9,'798502309333a16dda1c2987f2d1843a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:50:44','2008-02-21 19:50:44'),(10,'b882aa37280223841f5558b15a026a1c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:50:55','2008-02-21 19:50:55'),(11,'aadac1f0b4bd1dde70b3c29a907bdd47','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:51:28','2008-02-21 19:51:28'),(12,'12b0e40bc0a8ed408260e3e4c2fb4618','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:51:35','2008-02-21 19:51:35'),(13,'9bcc6ef38251a82b59e7532d390040fb','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-21 19:54:42','2008-02-21 19:54:42'),(14,'94106eeaa070bb210367460e0b4cdffe','BAh7CDoObG9nZ2VkX2luVDoTb3BlbmlkX3Nlc3Npb257ByIuT3BlbklEOjpD\nb25zdW1lcjo6bGFzdF9yZXF1ZXN0ZWRfZW5kcG9pbnQwIj1PcGVuSUQ6OkNv\nbnN1bWVyOjpEaXNjb3ZlcmVkU2VydmljZXM6Ok9wZW5JRDo6Q29uc3VtZXI6\nOjAiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhh\nc2h7AAY6CkB1c2VkewA=\n','2008-02-23 11:32:03','2008-02-23 15:04:56'),(15,'3bc1f66317b74a2ce6bed50ac64e2048','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:51:16','2008-02-23 11:51:16'),(16,'c8b9ea3a6515c7399cbabc21987e0dd7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:51:30','2008-02-23 11:51:30'),(17,'85ad5aa8a4000f3f43835fd47fcfeea4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:52:02','2008-02-23 11:52:02'),(18,'9a308dc4a85cdcc399ca84b9b5d49fd4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:52:23','2008-02-23 11:52:23'),(19,'c78437f36c9071815e2f6ad4b42143e8','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:55:17','2008-02-23 11:55:17'),(20,'66b8b7a0d3e1c98c80283eaeeda7697e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:55:30','2008-02-23 11:55:30'),(21,'ec4927479c08438b35ef152c744b1c00','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:55:32','2008-02-23 11:55:32'),(22,'88628fed3da098b26d21dface00d343c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:57:13','2008-02-23 11:57:13'),(23,'5bad8abfe0b29911feb8400bc613a4c7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 11:57:30','2008-02-23 11:57:30'),(24,'4a0fda898aacb89dec7a2cac5b1bf3a1','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-23 12:27:01','2008-02-23 12:27:01'),(25,'e23d41bc2382121350c4c5aa85d68259','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 12:27:16','2008-02-23 12:27:16'),(26,'67da80fc92b9bbcb3db018ea7fc658bc','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-23 12:27:36','2008-02-23 12:27:36'),(27,'31f2f0358830b7863eed831036c5a380','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-23 12:28:04','2008-02-23 12:28:04'),(28,'7dda4131ec7281e57e194aa8dd9b9aff','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-23 12:28:29','2008-02-23 12:28:29'),(29,'3af2915068b0c0cdc8f6bcc9399379e4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 12:28:57','2008-02-23 12:28:57'),(30,'6539c28d5b5afff3ced4b310ab41abdd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-23 12:29:12','2008-02-23 12:29:12'),(31,'8e9a4717908f9c50de2acbc36ea866a7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:27:38','2008-02-23 13:27:38'),(32,'c6ecd64a4d4a4cda09eb2630094ee9c4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:28:04','2008-02-23 13:28:04'),(33,'a789fbc6afb41c93959f4dd2a72d23d6','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:28:09','2008-02-23 13:28:09'),(34,'661a1909883fdf200e3ac793d8c66a99','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:28:55','2008-02-23 13:28:55'),(35,'9916449043a748002726c25fdabab54a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-23 13:29:14','2008-02-23 13:29:14'),(36,'62c2dddc6538e58be95aa38b1d14282a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:48:13','2008-02-23 13:48:13'),(37,'c8638cf6fc6f0ddac1e1b7213bb1ee2b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:49:06','2008-02-23 13:49:06'),(38,'5198c576d92600d9d579ba7a54f74cc7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:49:51','2008-02-23 13:49:51'),(39,'3f509193ac8b406e6215465184b08785','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:49:53','2008-02-23 13:49:53'),(40,'b27cd1f862f9094e82e5d42ea451facf','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:49:54','2008-02-23 13:49:54'),(41,'0f1e772e2ffef46f0be3069cda9a9ed5','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:50:04','2008-02-23 13:50:04'),(42,'2c85a50a6f849f756ed6321e578c32be','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:50:14','2008-02-23 13:50:14'),(43,'f6fd8a8e694d8af0fcbe5e61f7ea8063','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:50:24','2008-02-23 13:50:24'),(44,'06028fd593f1a158f30b91c480e0432c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:50:39','2008-02-23 13:50:39'),(45,'1fdf3d91b77378eda1e0de387089ef7c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:51:04','2008-02-23 13:51:04'),(46,'ba4ebe2f8980b83563c76d058532e7ac','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:51:25','2008-02-23 13:51:25'),(47,'e5651c3d45f7ac25dac7da2f924f9e3a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:51:34','2008-02-23 13:51:34'),(48,'10e290db02ea4230932984e3737a1d42','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:51:35','2008-02-23 13:51:35'),(49,'48d33f1e1f7d3f4dc95b82cc80139e13','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:52:52','2008-02-23 13:52:52'),(50,'4d18eb3a6f056d75396c08770fe83ba9','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:53:01','2008-02-23 13:53:01'),(51,'13125773037af337fc55f9c97d694706','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:53:17','2008-02-23 13:53:17'),(52,'fcb2c2d601fbc222a162c2ceb72b073f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:53:49','2008-02-23 13:53:49'),(53,'7a6cc459ed902d60588da3e449ea8097','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:54:55','2008-02-23 13:54:55'),(54,'fc574da58ef3b2e4eb439444d8aa2699','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:55:13','2008-02-23 13:55:13'),(55,'68569a4ee346e8ee9f4726b46fa8cb6c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:55:33','2008-02-23 13:55:33'),(56,'2b638ca0000f5aa016c44d5eb12249b0','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:55:38','2008-02-23 13:55:38'),(57,'55cc138cc3ac33ddb3a7a4c95eace838','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 13:55:41','2008-02-23 13:55:41'),(58,'a07be2a1e4622efa43b3d45a90d886fb','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 14:47:44','2008-02-23 14:47:44'),(59,'293e1729f88feac5cd96a25befab350e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 14:48:50','2008-02-23 14:48:50'),(60,'37e8c52b738ed3ae027f17f305f26faa','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 14:58:37','2008-02-23 14:58:37'),(61,'a15baf50c70d5c839e9b84ecab4272ba','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 14:58:41','2008-02-23 14:58:41'),(62,'f71072022e9647274641b1a46e6eaf2e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 14:59:08','2008-02-23 14:59:08'),(63,'006fb72cfd8a68f56b96c6bb6084ed7a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:02:42','2008-02-23 15:02:42'),(64,'cac2e4c216d58e25db2f7ae2742ecc12','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:02:54','2008-02-23 15:02:54'),(65,'90642a6e6c6c6102e5cdbbc9d2ec285c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:06:13','2008-02-23 15:06:13'),(66,'f75340dbdcbadf503e3d3f6484791832','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:06:20','2008-02-23 15:06:20'),(67,'ea59ba8be963f864458c58955bef0ad7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:06:24','2008-02-23 15:06:24'),(68,'b2285b5b71ab281946883645f64f57de','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:08:25','2008-02-23 15:08:25'),(69,'c8997f5ba819d41dc9c5962be12a3610','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:12:20','2008-02-23 15:12:20'),(70,'f509c5a4686fd15e36d3eccadceebb1b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:15:23','2008-02-23 15:15:23'),(71,'5963dbd702cb3cc6ccea0e87808c45d5','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:15:56','2008-02-23 15:15:56'),(72,'bf1b8ea6ea754c3bddbacab95b1635d9','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:16:54','2008-02-23 15:16:54'),(73,'2bfbd4683479b04a71106b252e42a4b1','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:16:57','2008-02-23 15:16:57'),(74,'297541526c9577712af7ee5b1927253c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:18:44','2008-02-23 15:18:44'),(75,'39ec3a1061d969603c08f14d2df86a2b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:18:52','2008-02-23 15:18:52'),(76,'47bd6dea482d189d542c34736344dd7a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:19:59','2008-02-23 15:19:59'),(77,'a9f36852795eacba38603c13af67e7ce','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:22:16','2008-02-23 15:22:16'),(78,'bda225853ec07cccc618e67828d0d9bf','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:23:04','2008-02-23 15:23:04'),(79,'ba55255d6b0fa816fd63db7ba9d70ae2','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:23:18','2008-02-23 15:23:18'),(80,'7c8fc52f9b1c579f8eba1c15848e719c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:23:40','2008-02-23 15:23:40'),(81,'b0f8c4d0bb14b7e6160108f127c1637f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:24:03','2008-02-23 15:24:03'),(82,'e7d180b58708e8f68861b37a423244b0','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:24:46','2008-02-23 15:24:46'),(83,'0d3a315c8312f3b9533b2b0cf978f4ce','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:25:00','2008-02-23 15:25:00'),(84,'66eace4ded4a2395c410cacead65f160','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:25:03','2008-02-23 15:25:03'),(85,'6c5b0057161a381bf053996a8926dc1c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:25:06','2008-02-23 15:25:06'),(86,'9554789964d4697ff14de88a0f9d7ea8','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:25:09','2008-02-23 15:25:09'),(87,'0652a9dce3b1508bb84d0f0b68106f05','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:25:24','2008-02-23 15:25:24'),(88,'baa3ece7af119016e3daa39c8991bd26','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:27:02','2008-02-23 15:27:02'),(89,'ec557edc8395b6282566008fa14c629f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:27:19','2008-02-23 15:27:19'),(90,'e426bdea13d6a51566442688a476a890','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:31:38','2008-02-23 15:31:38'),(91,'bf4493a61cd4cee7cf8f7e47d78bbe31','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:31:49','2008-02-23 15:31:49'),(92,'488e0ca862358023b3a781f819c9d03e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:32:18','2008-02-23 15:32:18'),(93,'00bc8a417ba691aa42809324e9d6bdfa','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:32:29','2008-02-23 15:32:29'),(94,'7cdedeca5f5f890e7007656eb6899975','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:32:37','2008-02-23 15:32:37'),(95,'bc2b5cedc44e05025f953115c50fe8bd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:32:49','2008-02-23 15:32:49'),(96,'79d23bbbaf8ab6d3ceacf068afd74f0a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-23 15:33:01','2008-02-23 15:33:01'),(97,'e1ef880e42931b3a484da63609c05d1f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 18:36:42','2008-02-25 18:36:42'),(98,'cc4d6c397038924a96d93fe5b9bd0100','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 18:38:21','2008-02-25 18:38:21'),(99,'fcdb5d3a161dcc5d095eeaeef61d203f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:04:06','2008-02-25 19:04:06'),(100,'6170dd18bf674eb6ef10b8d7e63102d9','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:07:05','2008-02-25 19:07:05'),(101,'5cbbd3d66fc50605041b0a0e9309e455','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:08:44','2008-02-25 19:08:44'),(102,'fde050cbf3a66e9f823c3bb4009edfb3','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:08:56','2008-02-25 19:08:56'),(103,'9c8fa6dfd454d50988d7a00549b6c047','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:09:13','2008-02-25 19:09:13'),(104,'2b9247a1a3bd73de955ecda41e7c16c2','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:09:21','2008-02-25 19:09:21'),(105,'82cbe1ff1a5fc7d8434fab89525233d2','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:09:24','2008-02-25 19:09:24'),(106,'d4b25efe609f79a9cc5d1780aeeaf487','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:09:28','2008-02-25 19:09:28'),(107,'e6a45048c551e2fe44ad270a05078e22','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:09:32','2008-02-25 19:09:32'),(108,'2b0e02276b62b648405392ff681dd1ff','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:09:42','2008-02-25 19:09:42'),(109,'826220a209a79c45f5fa2d52721c964c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:09:51','2008-02-25 19:09:51'),(110,'31ff49a465506b579b97df5e23d43139','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:10:36','2008-02-25 19:10:36'),(111,'c22aa3ddc38b3027cc10cf430b02fc1b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:10:52','2008-02-25 19:10:52'),(112,'9ba8ba10efeee1163088b04efaa3e2f7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:11:04','2008-02-25 19:11:04'),(113,'c13488de530a23d93d5152d3e795b804','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:11:25','2008-02-25 19:11:25'),(114,'fb19a764871a9968f4f3760b5c9019d1','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:12:32','2008-02-25 19:12:32'),(115,'d1afc656c271ffce589b67e62f6e1775','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:12:50','2008-02-25 19:12:50'),(116,'4045349945bf6694d78a0834fb8d4fbe','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:21:02','2008-02-25 19:21:02'),(117,'2f868ee21dc9627a2a03bf10a9015f75','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:21:17','2008-02-25 19:21:17'),(118,'3cead1f7b51d5ef1e64d3bbdbbe7e0c8','BAh7CDoObG9nZ2VkX2luVDoTb3BlbmlkX3Nlc3Npb257ByIuT3BlbklEOjpD\nb25zdW1lcjo6bGFzdF9yZXF1ZXN0ZWRfZW5kcG9pbnQwIj1PcGVuSUQ6OkNv\nbnN1bWVyOjpEaXNjb3ZlcmVkU2VydmljZXM6Ok9wZW5JRDo6Q29uc3VtZXI6\nOjAiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhh\nc2h7AAY6CkB1c2VkewA=\n','2008-02-25 19:21:37','2008-02-25 19:31:40'),(119,'4114ae74fec398c65961d509166239f6','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:22:52','2008-02-25 19:22:52'),(120,'3f55cc366e2660cc001b942100afe5b8','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:25:50','2008-02-25 19:25:50'),(121,'5a44e77b209c3dcb9b65d344a0f5bb70','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:27:11','2008-02-25 19:27:11'),(122,'83865ec2f3a9b1d7ab1e01d27e5a6d7a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:27:27','2008-02-25 19:27:27'),(123,'91105b282d618e46b2e4f4c3c811b58e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-25 19:29:23','2008-02-25 19:29:23'),(124,'862b600f56ed47d14ffcb9d71a8dd1eb','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-25 19:29:35','2008-02-25 19:29:35'),(125,'98f70e7e26ab869e6aa1ac156a24d93b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-25 19:30:00','2008-02-25 19:30:00'),(126,'581f13bc0f54a6089b1ebac52c85a7ee','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-25 19:30:23','2008-02-25 19:30:23'),(127,'4c54c48aa2c0ae6226e5e796ed5caeab','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-25 19:31:24','2008-02-25 19:31:24'),(128,'ff53e04d3237d15dc40f5e0050dbaf27','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-25 19:31:38','2008-02-25 19:31:38'),(129,'e294acd93d81635f8ec86bcba9d79d02','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-28 17:32:42','2008-02-28 17:32:42'),(130,'91b81767854afa3a0e2b8f3a2d20b9bc','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-28 17:33:00','2008-02-28 17:33:00'),(131,'3f3f79da5f7bed31ca1a65174203afe0','BAh7BzoObG9nZ2VkX2luVCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6\nRmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-28 17:33:05','2008-02-28 17:38:11'),(132,'f07e51a81104f7cc40f7da606119a8b1','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-28 17:33:18','2008-02-28 17:33:18'),(133,'e6884f31cb5708b3f65cd16d38edda7c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-28 17:36:21','2008-02-28 17:36:21'),(134,'fadcc8c85b456a106fba7e55666da6a4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-28 17:37:46','2008-02-28 17:37:46'),(135,'e3c1d0462de0c25c03402772c3e81bd0','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-28 17:37:59','2008-02-28 17:37:59'),(136,'d8a9ccde9811a1a4c2c11e1a764e1086','BAh7BzoObG9nZ2VkX2luVCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6\nRmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-28 18:32:09','2008-02-28 19:50:16'),(137,'ba6ffc1f4ff88b52890655523c3ea565','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-28 18:34:15','2008-02-28 18:34:15'),(138,'9233ece2cd7ff56d0adb2a293ce0e769','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-28 18:34:26','2008-02-28 18:34:26'),(139,'7c4d80acb82b40bc9c1fe4fa287eb8e6','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-28 19:46:17','2008-02-28 19:46:17'),(140,'e1efaedbfbe9831ff2dceaebfa307855','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsGOgtub3RpY2UiEVBhZ2UgdXBkYXRlZAY6CkB1c2VkewY7BlQ=\n','2008-02-28 19:46:46','2008-02-28 19:46:46'),(141,'4c122f052701f94d0c45c350e6368982','BAh7BzoObG9nZ2VkX2luVCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6\nRmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 17:42:58','2008-02-29 17:58:30'),(142,'21e32d43c681db5b37975ae619520807','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 17:58:38','2008-02-29 17:58:38'),(143,'ff34f300ace9978760ff28c662e36d2b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:34:53','2008-02-29 23:34:53'),(144,'edb2e848a7b9a1a96ab902fd44351fee','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:35:05','2008-02-29 23:35:05'),(145,'f782317c64169a0eed273df795185021','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:42:05','2008-02-29 23:42:05'),(146,'50854ac7934367ac29c5d6a4e0e19cca','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:42:15','2008-02-29 23:42:15'),(147,'8b9117397657cdd711ff30523a0a47a3','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:42:20','2008-02-29 23:42:20'),(148,'e810bc12c99a547afb5a686735b14cba','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:42:52','2008-02-29 23:42:52'),(149,'b30fa596fb2e3887a8303de36b61428b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:42:56','2008-02-29 23:42:56'),(150,'a7004cb0e3ecc298e951cf64baec3536','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:45:08','2008-02-29 23:45:08'),(151,'2225ee12ea2e96bcbb0ae64184aee2f2','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:45:33','2008-02-29 23:45:33'),(152,'302e7ecb84bd88fe44ab8d10b8ed755e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-02-29 23:46:06','2008-02-29 23:46:06'),(153,'fd8bb48068e20ef82326e8226906ede4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:35:09','2008-03-01 00:35:09'),(154,'9522cb361b6b4584bb82b8adb87bae19','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:35:29','2008-03-01 00:35:29'),(155,'48328b4d87755aa62b37d8f368d78b5c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:35:41','2008-03-01 00:35:41'),(156,'7c5fc3e0d12f16ff4ce45acc855f603d','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:36:24','2008-03-01 00:36:24'),(157,'0da56b7cc1a66765d27607b5e9485cb7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:36:43','2008-03-01 00:36:43'),(158,'717a3733a710c55534b299e3de4ffe95','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:36:56','2008-03-01 00:36:56'),(159,'6fa95c03f07fd0a6c12081334f85fb50','BAh7CDoObG9nZ2VkX2luVDoTb3BlbmlkX3Nlc3Npb257ByIuT3BlbklEOjpD\nb25zdW1lcjo6bGFzdF9yZXF1ZXN0ZWRfZW5kcG9pbnQwIj1PcGVuSUQ6OkNv\nbnN1bWVyOjpEaXNjb3ZlcmVkU2VydmljZXM6Ok9wZW5JRDo6Q29uc3VtZXI6\nOjAiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhh\nc2h7AAY6CkB1c2VkewA=\n','2008-03-01 00:42:09','2008-03-01 00:43:31'),(160,'4f6808583eef9eaeeb15b5abe3034471','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:43:24','2008-03-01 00:43:24'),(161,'9cb70b10df85a4d661f45a9f29b8b63e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:44:00','2008-03-01 00:44:00'),(162,'c083ef6e4a38adb0b9178c06c2ad7d50','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:45:17','2008-03-01 00:45:17'),(163,'f43b983389b469de6835365c9efe2787','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:45:26','2008-03-01 00:45:26'),(164,'05f1e30bbe921279762db72bcc56b12c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:46:51','2008-03-01 00:46:51'),(165,'1571b83187cf7eeb6dc575b0c642833a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:47:30','2008-03-01 00:47:30'),(166,'378e41d6a2e1a65b1cd9b88e41dc9519','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:48:38','2008-03-01 00:48:38'),(167,'9460a32d260017d21db313af8e65fb9d','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:49:05','2008-03-01 00:49:05'),(168,'0ec7f5fb207d4b9dd4e93c6dae72ef86','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:49:33','2008-03-01 00:49:33'),(169,'9403a4001e3b0163fe69b8296f63e161','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:49:52','2008-03-01 00:49:52'),(170,'100e525ebd09e0a8ace56bf9b8abdaa7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:50:03','2008-03-01 00:50:03'),(171,'bd0b7f444f056e4918a5945817c189a1','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 00:51:07','2008-03-01 00:51:07'),(172,'0b5046bec51530577230abac0555c805','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 16:57:25','2008-03-01 16:57:25'),(173,'755b9853438e9d97f31bc475b5a52c2f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 16:57:27','2008-03-01 16:57:27'),(174,'a75897478edcda57248932af045ff576','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 16:57:30','2008-03-01 16:57:30'),(175,'cfba02ba1239a98ec246c6550d32eacf','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 16:57:56','2008-03-01 16:57:56'),(176,'1030be8fecebedfed2277ebe8c74754f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 16:58:00','2008-03-01 16:58:00'),(177,'aebb0aa53a320f0bfcdcbc69bc44685a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:01:02','2008-03-01 17:01:02'),(178,'e8a1a8553822cebd4ad08b029947ebfd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:05:40','2008-03-01 17:05:40'),(179,'06a701e35afb3bb0d56411764a132e9a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:06:01','2008-03-01 17:06:01'),(180,'d99b673fb63d7b53ce8add9a175ca0f0','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:06:27','2008-03-01 17:06:27'),(181,'139a94c7be25d51e893ac39c4e6aed57','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:06:36','2008-03-01 17:06:36'),(182,'0eb3698e75d6a0047f502558c286a54c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:07:12','2008-03-01 17:07:12'),(183,'3da6af58a10e1f46a29b092dbae88027','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:07:47','2008-03-01 17:07:47'),(184,'7cbf267136ca79137efc3ff6776629cd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:08:05','2008-03-01 17:08:05'),(185,'c4505b8ae0871ca1f35ddd0b739514fe','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:09:03','2008-03-01 17:09:03'),(186,'0aaa9bfeb6cc0da001b2aaed296de882','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:11:04','2008-03-01 17:11:04'),(187,'6082ddfcc161ac56c95b81d3255d516d','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:11:35','2008-03-01 17:11:35'),(188,'c1ccbaed215d72c1ac8d90324a169c52','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:12:12','2008-03-01 17:12:12'),(189,'6e0a85346477ae1048daebf3a69aa7bd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:12:36','2008-03-01 17:12:36'),(190,'d8641ddbdd36147a0679a8ee22833ea9','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:14:28','2008-03-01 17:14:28'),(191,'1a0cd83fa97aeccfc7e9487942c169e4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:14:35','2008-03-01 17:14:35'),(192,'234e83b97bc322344cd887bfcbfa989b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:14:52','2008-03-01 17:14:52'),(193,'f3ac44a7c22cff7deb8d79255e75a131','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:15:52','2008-03-01 17:15:52'),(194,'417d4b2a81b218365b3af901c90f221f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:15:57','2008-03-01 17:15:57'),(195,'72834ee9fd4d6ebc0a86fdbc38934683','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:16:10','2008-03-01 17:16:10'),(196,'0c0d043a96c1c33665c2af04137be38c','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:16:31','2008-03-01 17:16:31'),(197,'06115111682f992d6bad876796980cbf','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:17:42','2008-03-01 17:17:42'),(198,'f86940d7e0d87236bec05cc1569012f7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:17:58','2008-03-01 17:17:58'),(199,'32d2a769cd4fc7aa3ebb72c0c77a0c52','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:18:08','2008-03-01 17:18:08'),(200,'2bca57f49c6ff3706da65ea3a03f6e26','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:18:18','2008-03-01 17:18:18'),(201,'b1038e13e630a3f5246ac9544882be49','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:20:24','2008-03-01 17:20:24'),(202,'77eb3ec0beed9059b9cffd370eaa5867','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:20:40','2008-03-01 17:20:40'),(203,'da8e5e37f004476d7b6f0b377ab30efd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:20:45','2008-03-01 17:20:45'),(204,'0248754f2a8b1ceebe153e22e7c11bac','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:20:49','2008-03-01 17:20:49'),(205,'949b7b38fa311598af620f70543b1e60','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:20:56','2008-03-01 17:20:56'),(206,'1d1efa4f34727b70482f1082ec09332a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:22:12','2008-03-01 17:22:12'),(207,'7aea5c6f87abcd014ed4a076d8df8a17','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:22:30','2008-03-01 17:22:30'),(208,'d96dae084911dce2e3568d626213144e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:22:35','2008-03-01 17:22:35'),(209,'cfb93438e19653f5c9a81516264b746a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:24:20','2008-03-01 17:24:20'),(210,'522b4e94da3100c9812124298b675a4d','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:24:23','2008-03-01 17:24:23'),(211,'bd94ed333811408dfd1727241f84c93d','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:24:42','2008-03-01 17:24:42'),(212,'14ab5eb7e05b467f1eb4a716c9aad057','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:25:52','2008-03-01 17:25:52'),(213,'a259d562c4cf0cf1c0df31518a9279f4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:26:13','2008-03-01 17:26:13'),(214,'c18a52c50b311832b68593712bf20bef','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:26:32','2008-03-01 17:26:32'),(215,'2b65600e6f97075009f92e24500a8087','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:26:58','2008-03-01 17:26:58'),(216,'afab932cc3612ff50f6983c93f72897e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:27:36','2008-03-01 17:27:36'),(217,'09702baec836d483afacd1c3fd6ea4e6','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:27:48','2008-03-01 17:27:48'),(218,'ba6f71f6cb04629a2a7413d15d339879','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:29:26','2008-03-01 17:29:26'),(219,'56fd9ec33659ef0bb3f4a7751ddd84a7','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:37:03','2008-03-01 17:37:03'),(220,'aed88477382feebb9f749c14459eec42','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:37:42','2008-03-01 17:37:42'),(221,'24c6452e080fb0e858c53dec54e5715e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:37:54','2008-03-01 17:37:54'),(222,'c23fbc27f982cdbcf5c8b2ad1646959b','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:38:02','2008-03-01 17:38:02'),(223,'0710a3db4335dc58ee6df4125eca4a16','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:38:19','2008-03-01 17:38:19'),(224,'f3f90419619504a8b07d0ff6c9e48814','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:38:21','2008-03-01 17:38:21'),(225,'c6f74d9386408b8213430d21d02b3fb2','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-01 17:38:36','2008-03-01 17:38:36'),(226,'edf3c80dffa61d801ab467a6a12d0060','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:39:53','2008-03-14 18:42:37'),(227,'1c0cd52ca3b11b6c9ddd701e645dca91','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:49:04','2008-03-14 18:49:04'),(228,'495b058921a251ff277ffd3267d1e382','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:51:54','2008-03-14 18:51:54'),(229,'49e387d5191d3ebb441ab39c9bca8592','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:52:14','2008-03-14 18:52:14'),(230,'2afc1e09d23acd326838f6944314be60','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:52:23','2008-03-14 18:52:23'),(231,'5479c9584841ceee2b3cf859a81d78b1','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:52:34','2008-03-14 18:52:34'),(232,'16ac0639e587a3d1ff7b7c8cab6f0e5a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:52:48','2008-03-14 18:52:48'),(233,'4c6ff459b07b0fbab9605249a1e0bf24','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:53:04','2008-03-14 18:53:04'),(234,'202a114abf9856d8ecbe96b8841a715a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:53:19','2008-03-14 18:53:19'),(235,'0429925f12729149685ee263fb8daf2f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:53:34','2008-03-14 18:53:34'),(236,'60854f716a64a8ff16f38ee3962abe26','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:54:02','2008-03-14 18:54:02'),(237,'69822f354819c158b8101d3b3d31a45e','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:54:49','2008-03-14 18:54:49'),(238,'98057686ffedf4eada9d4ece42ed1d41','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:55:19','2008-03-14 18:55:19'),(239,'79c22284f82144bcab641294b536b0dd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:55:32','2008-03-14 18:55:32'),(240,'a359faa5740f29024cf306167df6da01','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:55:42','2008-03-14 18:55:42'),(241,'eb9e24a79479dd94b8aaa40fad273600','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:56:22','2008-03-14 18:56:22'),(242,'14e053097662e4e6a8422f9e09e911de','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:56:24','2008-03-14 18:56:24'),(243,'d41a2d7e735392d9c92f2bc821bbfcfa','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:56:34','2008-03-14 18:56:34'),(244,'3671fa0bf93dac4d382e61188d585b22','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:56:54','2008-03-14 18:56:54'),(245,'7a6d23e939da214d22ad36878513d234','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:57:03','2008-03-14 18:57:03'),(246,'7a14c3a0d315ecc9b6558ecfd75c2ea3','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:57:10','2008-03-14 18:57:10'),(247,'ef8c79870fb9393a50e98e1228058229','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:57:37','2008-03-14 18:57:37'),(248,'f9651f71d1cdcb9d8eada35aead027e2','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 18:59:36','2008-03-14 18:59:36'),(249,'47fa15a7afcb0d5ec4f85bced3000110','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:05:10','2008-03-14 19:05:10'),(250,'6745057a30f2af0f6913a3a88a1c4341','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:05:31','2008-03-14 19:05:31'),(251,'c106670d5da875ba17e1e21ddfee6abd','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:05:38','2008-03-14 19:05:38'),(252,'626ff37ddfb528ff0059e2d1723931ad','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:13:31','2008-03-14 19:13:31'),(253,'8590af41c07080dde17a0d4f75695cc0','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:15:17','2008-03-14 19:15:17'),(254,'c85e151bc9b5a0b7910da2d0c69ea7d6','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:15:37','2008-03-14 19:15:37'),(255,'a0a8b9eb71eddaf6a2af4395ae98a449','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:15:49','2008-03-14 19:15:49'),(256,'d38300cc1daeb1d55a99eeb5605e3b56','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:15:52','2008-03-14 19:15:52'),(257,'c84128b2852978454e752a944823cb19','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:16:00','2008-03-14 19:16:00'),(258,'0d5da09693e93dff30c3241a6537b8df','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:16:10','2008-03-14 19:16:10'),(259,'bd6bd20189809096662ed4a166599f7f','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:16:24','2008-03-14 19:16:24'),(260,'7755e5b373cdc7a1186989e805d98f2d','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:17:18','2008-03-14 19:17:18'),(261,'3021103bbdc0cba21b4e6da24c031a72','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:19:37','2008-03-14 19:19:37'),(262,'66c3428b06f4a651a59d2e35566fb200','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:19:53','2008-03-14 19:19:53'),(263,'e2dbd150ef75bf04878301ba2689b8f4','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:20:15','2008-03-14 19:20:15'),(264,'8c79d719f707e34e62d5ebdb7d2e0a1a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:20:19','2008-03-14 19:20:19'),(265,'47ef61a2c3f1d7b3031a839ab9605d40','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:20:35','2008-03-14 19:20:35'),(266,'c47db69bf0c2c071c2ce1c2776f1d49a','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:20:42','2008-03-14 19:20:42'),(267,'04930228dfd32f0ee0fd173694303414','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:20:48','2008-03-14 19:20:48'),(268,'fd36e53818daf64230b37c9feec6047d','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:21:58','2008-03-14 19:21:58'),(269,'bd54e9f257c9abfdf1dfc4912771eb10','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:22:18','2008-03-14 19:22:18'),(270,'345a4335e011236f65daf72b3812d1d1','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:22:32','2008-03-14 19:22:32'),(271,'5f0c2d09ea349aa82dec99d34ab1e798','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-14 19:22:43','2008-03-14 19:22:43'),(272,'c20cabb6ae1f2fac3ba5b9bec00548cf','BAh7BzoUcGVuZGluZ19jb21tZW50MCIKZmxhc2hJQzonQWN0aW9uQ29udHJv\nbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==\n','2008-03-19 16:57:36','2008-03-19 17:00:57'),(273,'dbe2a4180f741e7386c05f82d5174119','BAh7CDoUcGVuZGluZ19jb21tZW50MDoObG9nZ2VkX2luVCIKZmxhc2hJQzon\nQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7\nAA==\n','2008-04-12 10:57:40','2008-04-12 12:23:27'),(274,'a2b001727b26e6069f539d1c2569a374','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-04-12 11:03:41','2008-04-12 11:03:41'),(275,'f3f2c591edd65dc87b63825b024d80a6','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-04-12 11:30:42','2008-04-12 12:16:17'),(276,'403d52c72d5153bd18f7e9ec202f44f5','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-04-12 11:43:04','2008-04-12 11:43:04'),(277,'1757874734df0ae9fc19143a9746aa57','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-04-12 12:13:04','2008-04-12 12:13:04'),(278,'be643642fa382799ab42a6acc61c9608','BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo\nSGFzaHsABjoKQHVzZWR7AA==\n','2008-04-12 12:43:05','2008-04-12 12:43:05');
/*!40000 ALTER TABLE `sessions` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `taggings`
--
DROP TABLE IF EXISTS `taggings`;
CREATE TABLE `taggings` (
`id` int(11) NOT NULL auto_increment,
`tag_id` int(11) default NULL,
`taggable_id` int(11) default NULL,
`created_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `index_taggings_on_tag_id` (`tag_id`),
KEY `index_taggings_on_taggable_id_and_taggable_type` (`taggable_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `taggings`
--
LOCK TABLES `taggings` WRITE;
/*!40000 ALTER TABLE `taggings` DISABLE KEYS */;
INSERT INTO `taggings` VALUES (1,1,2,'2008-04-12 01:22:54'),(2,2,2,'2008-04-12 01:22:54'),(3,1,3,'2008-04-12 01:22:54'),(4,3,3,'2008-04-12 01:22:54'),(5,1,4,'2008-04-12 01:22:54'),(6,3,4,'2008-04-12 01:22:54'),(7,1,5,'2008-04-12 01:22:54'),(8,4,5,'2008-04-12 01:22:54'),(9,1,12,'2008-04-12 01:22:54'),(10,3,12,'2008-04-12 01:22:54'),(11,1,17,'2008-04-12 01:22:54'),(12,2,17,'2008-04-12 01:22:54'),(13,4,17,'2008-04-12 01:22:54'),(14,1,18,'2008-04-12 01:22:54'),(15,3,18,'2008-04-12 01:22:54'),(16,1,19,'2008-04-12 01:22:54'),(17,2,19,'2008-04-12 01:22:54'),(18,1,20,'2008-04-12 01:22:54'),(19,3,20,'2008-04-12 01:22:54'),(20,1,96,'2008-04-12 01:22:54'),(21,4,96,'2008-04-12 01:22:54'),(22,1,97,'2008-04-12 01:22:54'),(23,3,97,'2008-04-12 01:22:54'),(24,1,98,'2008-04-12 01:22:54'),(25,4,98,'2008-04-12 01:22:54'),(26,1,99,'2008-04-12 01:22:54'),(27,3,99,'2008-04-12 01:22:54'),(28,1,100,'2008-04-12 01:22:54'),(29,4,100,'2008-04-12 01:22:54'),(30,5,102,'2008-04-12 01:22:54'),(31,6,104,'2008-04-12 01:22:54'),(32,6,105,'2008-04-12 01:22:54'),(33,6,106,'2008-04-12 01:22:54'),(34,6,107,'2008-04-12 01:22:54'),(35,6,108,'2008-04-12 01:22:54'),(36,6,109,'2008-04-12 01:22:54'),(37,6,110,'2008-04-12 01:22:54'),(38,1,114,'2008-04-12 01:22:54'),(39,7,114,'2008-04-12 01:22:54'),(40,1,115,'2008-04-12 01:22:54'),(41,7,115,'2008-04-12 01:22:54'),(42,8,115,'2008-04-12 01:22:54'),(43,1,116,'2008-04-12 01:22:54'),(44,1,117,'2008-04-12 01:22:54'),(45,4,117,'2008-04-12 01:22:54'),(46,1,118,'2008-04-12 01:22:54'),(47,7,118,'2008-04-12 01:22:54'),(48,8,118,'2008-04-12 01:22:54'),(49,1,119,'2008-04-12 01:22:54'),(50,7,119,'2008-04-12 01:22:54'),(51,8,119,'2008-04-12 01:22:54'),(52,1,120,'2008-04-12 01:22:54'),(53,7,120,'2008-04-12 01:22:54'),(54,1,121,'2008-04-12 01:22:54'),(55,7,121,'2008-04-12 01:22:54'),(56,8,121,'2008-04-12 01:22:54'),(57,1,122,'2008-04-12 01:22:54'),(58,7,122,'2008-04-12 01:22:54'),(59,8,122,'2008-04-12 01:22:54'),(60,1,123,'2008-04-12 01:22:54'),(61,7,123,'2008-04-12 01:22:54'),(62,8,123,'2008-04-12 01:22:54'),(63,1,124,'2008-04-12 01:22:54'),(64,1,125,'2008-04-12 01:22:54'),(65,8,125,'2008-04-12 01:22:54'),(66,1,126,'2008-04-12 01:22:54'),(67,4,126,'2008-04-12 01:22:54'),(68,1,127,'2008-04-12 01:22:54'),(69,7,127,'2008-04-12 01:22:54'),(70,7,125,'2008-04-12 01:22:54'),(71,1,128,'2008-04-12 01:22:54'),(72,7,128,'2008-04-12 01:22:54'),(73,1,129,'2008-04-12 01:22:54'),(74,7,129,'2008-04-12 01:22:54'),(75,8,129,'2008-04-12 01:22:54'),(76,1,133,'2008-04-12 01:22:54'),(77,1,137,'2008-04-12 01:22:54'),(78,1,140,'2008-04-12 01:22:54'),(79,7,140,'2008-04-12 01:22:54'),(80,8,140,'2008-04-12 01:22:54'),(81,1,141,'2008-04-12 01:22:54'),(82,7,141,'2008-04-12 01:22:54'),(83,8,141,'2008-04-12 01:22:54'),(84,1,142,'2008-04-12 01:22:54'),(85,7,142,'2008-04-12 01:22:54'),(86,8,142,'2008-04-12 01:22:54'),(87,1,143,'2008-04-12 01:22:54'),(88,7,143,'2008-04-12 01:22:54'),(89,8,143,'2008-04-12 01:22:54'),(90,1,144,'2008-04-12 01:22:54'),(91,4,144,'2008-04-12 01:22:54'),(92,7,144,'2008-04-12 01:22:54'),(93,1,146,'2008-04-12 01:22:54'),(94,7,146,'2008-04-12 01:22:54'),(95,8,146,'2008-04-12 01:22:54'),(96,1,147,'2008-04-12 01:22:54'),(97,4,147,'2008-04-12 01:22:54'),(98,1,148,'2008-04-12 01:22:54'),(99,4,148,'2008-04-12 01:22:54'),(100,1,149,'2008-04-12 01:22:54'),(101,4,149,'2008-04-12 01:22:54'),(102,1,150,'2008-04-12 01:22:54'),(103,4,150,'2008-04-12 01:22:54'),(104,1,151,'2008-04-12 01:22:54'),(105,2,151,'2008-04-12 01:22:54'),(106,1,159,'2008-04-12 01:22:54'),(107,4,159,'2008-04-12 01:22:54'),(108,8,159,'2008-04-12 01:22:54'),(109,1,160,'2008-04-12 01:22:54'),(110,4,160,'2008-04-12 01:22:54'),(111,1,162,'2008-04-12 01:22:54'),(112,3,162,'2008-04-12 01:22:54'),(113,1,163,'2008-04-12 01:22:54'),(114,4,163,'2008-04-12 01:22:54'),(115,1,165,'2008-04-12 01:22:54'),(116,7,165,'2008-04-12 01:22:54'),(117,8,165,'2008-04-12 01:22:54'),(118,1,167,'2008-04-12 01:22:54'),(119,4,167,'2008-04-12 01:22:54'),(120,7,167,'2008-04-12 01:22:54'),(121,1,168,'2008-04-12 01:22:54'),(122,7,168,'2008-04-12 01:22:54'),(123,8,168,'2008-04-12 01:22:54'),(124,1,171,'2008-04-12 01:22:54'),(125,7,171,'2008-04-12 01:22:54'),(126,8,171,'2008-04-12 01:22:54'),(127,1,175,'2008-04-12 01:22:54'),(128,7,175,'2008-04-12 01:22:54'),(129,8,175,'2008-04-12 01:22:54'),(130,1,180,'2008-04-12 01:22:54'),(131,7,180,'2008-04-12 01:22:54'),(132,8,180,'2008-04-12 01:22:54'),(133,1,196,'2008-04-12 01:22:54'),(134,4,196,'2008-04-12 01:22:54'),(135,7,196,'2008-04-12 01:22:54'),(136,1,197,'2008-04-12 01:22:54'),(137,7,197,'2008-04-12 01:22:54'),(138,8,197,'2008-04-12 01:22:54'),(139,1,199,'2008-04-12 01:22:54'),(140,7,199,'2008-04-12 01:22:54'),(141,1,203,'2008-04-12 01:22:54'),(142,4,203,'2008-04-12 01:22:54'),(143,1,204,'2008-04-12 01:22:54'),(144,7,204,'2008-04-12 01:22:54'),(145,1,205,'2008-04-12 01:22:54'),(146,7,205,'2008-04-12 01:22:54'),(147,1,206,'2008-04-12 01:22:54'),(148,7,206,'2008-04-12 01:22:54'),(149,1,208,'2008-04-12 01:22:54'),(150,7,208,'2008-04-12 01:22:54'),(151,1,209,'2008-04-12 01:22:54'),(152,2,209,'2008-04-12 01:22:54'),(153,4,209,'2008-04-12 01:22:54'),(154,7,209,'2008-04-12 01:22:54'),(155,1,210,'2008-04-12 01:22:54'),(156,4,210,'2008-04-12 01:22:54'),(157,1,213,'2008-04-12 01:22:54'),(158,7,213,'2008-04-12 01:22:54'),(159,1,214,'2008-04-12 01:22:54'),(160,7,214,'2008-04-12 01:22:54'),(161,8,214,'2008-04-12 01:22:54'),(162,6,215,'2008-04-12 01:22:54'),(163,1,216,'2008-04-12 01:22:54'),(164,7,216,'2008-04-12 01:22:54'),(165,8,216,'2008-04-12 01:22:54'),(166,1,217,'2008-04-12 01:22:54'),(167,7,217,'2008-04-12 01:22:54'),(168,8,217,'2008-04-12 01:22:54'),(169,1,224,'2008-04-12 01:22:54'),(170,7,224,'2008-04-12 01:22:54'),(171,8,224,'2008-04-12 01:22:54'),(172,1,234,'2008-04-12 01:22:54'),(173,7,234,'2008-04-12 01:22:54'),(174,8,234,'2008-04-12 01:22:54'),(175,1,263,'2008-04-12 01:22:54'),(176,7,263,'2008-04-12 01:22:54'),(177,8,263,'2008-04-12 01:22:54'),(178,1,283,'2008-04-12 01:22:54'),(179,7,283,'2008-04-12 01:22:54'),(180,8,283,'2008-04-12 01:22:54'),(181,1,298,'2008-04-12 01:22:54'),(182,7,298,'2008-04-12 01:22:54'),(183,8,298,'2008-04-12 01:22:54'),(184,1,301,'2008-04-12 01:22:54'),(185,7,301,'2008-04-12 01:22:54'),(186,8,301,'2008-04-12 01:22:54'),(187,6,302,'2008-04-12 01:22:54'),(188,1,303,'2008-04-12 01:22:54'),(189,7,303,'2008-04-12 01:22:54'),(190,8,303,'2008-04-12 01:22:54'),(191,1,327,'2008-04-12 01:22:54'),(192,7,327,'2008-04-12 01:22:54'),(193,8,327,'2008-04-12 01:22:54'),(194,1,330,'2008-04-12 01:22:54'),(195,7,330,'2008-04-12 01:22:54'),(196,8,330,'2008-04-12 01:22:54'),(197,1,333,'2008-04-12 01:22:54'),(198,7,333,'2008-04-12 01:22:54'),(199,8,333,'2008-04-12 01:22:54'),(200,1,336,'2008-04-12 01:22:54'),(201,7,336,'2008-04-12 01:22:54'),(202,8,336,'2008-04-12 01:22:54'),(203,1,342,'2008-04-12 01:22:54'),(204,7,342,'2008-04-12 01:22:54'),(205,1,347,'2008-04-12 01:22:54'),(206,7,347,'2008-04-12 01:22:54'),(207,8,347,'2008-04-12 01:22:54'),(208,1,354,'2008-04-12 01:22:54'),(209,4,354,'2008-04-12 01:22:54'),(210,1,355,'2008-04-12 01:22:54'),(211,3,355,'2008-04-12 01:22:54'),(212,1,356,'2008-04-12 01:22:54'),(213,7,356,'2008-04-12 01:22:54'),(214,8,356,'2008-04-12 01:22:54'),(215,1,357,'2008-04-12 01:22:54'),(216,7,357,'2008-04-12 01:22:54'),(217,8,357,'2008-04-12 01:22:54'),(218,1,358,'2008-04-12 01:22:54'),(219,4,358,'2008-04-12 01:22:54'),(220,1,359,'2008-04-12 01:22:54'),(221,7,359,'2008-04-12 01:22:54'),(222,1,360,'2008-04-12 01:22:54'),(223,4,360,'2008-04-12 01:22:54'),(224,1,366,'2008-04-12 01:22:54'),(225,7,366,'2008-04-12 01:22:54'),(226,1,367,'2008-04-12 01:22:54'),(227,7,367,'2008-04-12 01:22:54'),(228,8,367,'2008-04-12 01:22:54'),(229,1,374,'2008-04-12 01:22:54'),(230,7,374,'2008-04-12 01:22:54'),(231,1,390,'2008-04-12 01:22:54'),(232,4,390,'2008-04-12 01:22:54'),(233,7,390,'2008-04-12 01:22:54'),(234,1,429,'2008-04-12 01:22:54'),(235,4,429,'2008-04-12 01:22:54'),(236,1,465,'2008-04-12 01:22:54'),(237,4,465,'2008-04-12 01:22:54'),(238,1,507,'2008-04-12 01:22:54'),(239,4,507,'2008-04-12 01:22:54'),(240,1,660,'2008-04-12 01:22:54'),(241,7,660,'2008-04-12 01:22:54'),(242,8,660,'2008-04-12 01:22:54'),(243,1,754,'2008-04-12 01:22:54'),(244,7,754,'2008-04-12 01:22:54'),(245,8,754,'2008-04-12 01:22:54'),(246,10,1,'2008-04-12 01:22:54'),(247,11,4,'2008-04-12 01:22:54'),(248,12,12,'2008-04-12 01:22:54'),(249,12,18,'2008-04-12 01:22:54'),(250,12,20,'2008-04-12 01:22:54'),(251,13,96,'2008-04-12 01:22:54'),(252,12,97,'2008-04-12 01:22:54'),(253,12,99,'2008-04-12 01:22:54'),(254,12,3,'2008-04-12 01:22:54'),(255,14,117,'2008-04-12 01:22:54'),(256,15,118,'2008-04-12 01:22:54'),(257,16,120,'2008-04-12 01:22:54'),(258,17,121,'2008-04-12 01:22:54'),(259,18,125,'2008-04-12 01:22:54'),(260,18,127,'2008-04-12 01:22:54'),(261,18,129,'2008-04-12 01:22:54'),(262,19,143,'2008-04-12 01:22:54'),(263,20,159,'2008-04-12 01:22:54'),(264,20,160,'2008-04-12 01:22:54'),(265,20,162,'2008-04-12 01:22:54'),(266,20,163,'2008-04-12 01:22:54'),(267,21,165,'2008-04-12 01:22:54'),(268,21,168,'2008-04-12 01:22:54'),(269,21,171,'2008-04-12 01:22:54'),(270,22,205,'2008-04-12 01:22:54'),(271,27,333,'2008-04-12 01:22:54'),(272,28,342,'2008-04-12 01:22:54'),(273,29,354,'2008-04-12 01:22:54'),(274,21,367,'2008-04-12 01:22:54'),(275,16,374,'2008-04-12 01:22:54'),(276,30,374,'2008-04-12 01:22:54'),(277,21,374,'2008-04-12 01:22:54'),(278,12,465,'2008-04-12 01:22:54'),(279,31,465,'2008-04-12 01:22:54'),(280,12,507,'2008-04-12 01:22:54'),(281,31,507,'2008-04-12 01:22:54');
/*!40000 ALTER TABLE `taggings` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `tags`
--
DROP TABLE IF EXISTS `tags`;
CREATE TABLE `tags` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) default NULL,
`taggings_count` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `index_tags_on_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dumping data for table `tags`
--
LOCK TABLES `tags` WRITE;
/*!40000 ALTER TABLE `tags` DISABLE KEYS */;
INSERT INTO `tags` VALUES (1,'Home',0),(2,'Ethics',0),(3,'Food',0),(4,'Life',0),(5,'About',0),(6,'Projects',0),(7,'Code',0),(8,'Ruby',0),(9,'Notes',0),(10,'Ethics',0),(11,'Recipes',0),(12,'Recipe',0),(13,'Language',0),(14,'alternate keyboard layout',0),(15,'Ruby on Rails View Templates',0),(16,'javascript',0),(17,'Rant',0),(18,'snippet',0),(19,'functional',0),(20,'railsconfeurope railsconfeurope07',0),(21,'rails',0),(22,'linux',0),(23,'svn',0),(24,'bazaar',0),(25,'subversion',0),(26,'bzr',0),(27,'ruby',0),(28,'erlang',0),(29,'rubegoldberg rube goldberg',0),(30,'jquery',0),(31,'recipe',0);
/*!40000 ALTER TABLE `tags` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2008-04-12 2:47:26