tag:blogger.com,1999:blog-284621792024-03-16T18:08:26.945+11:00James CrispRuby on Rails, C#, .NET, book reviews, film reviews, mind hacks, Wing Chun and the occasional personal bit.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.comBlogger29125tag:blogger.com,1999:blog-28462179.post-42053640846296074582007-07-30T23:10:00.000+10:002007-07-30T23:34:53.938+10:00This Blog Has Moved!After more than a year on Blogger, I've bitten the bullet, bought myself a domain and installed WordPress. It's been good so far. Please update your feeds and links. Hope you like the new site and feel free to send me some feedback - the new site even has a contact form :-)<br /><br /><span style="font-weight: bold;">New Address</span><br />My blog is now to be found at:<br /><br /><a href="http://jamescrisp.org/">http://jamescrisp.org</a><br /><br /><br /><span style="font-weight: bold;">New Feeds</span><br /><ul><li><a href="http://jamescrisp.org/feed">Full </a>(http://jamescrisp.org/feed)</li><li><a href="http://jamescrisp.org/category/technical/feed">Technical</a> (http://jamescrisp.org/category/technical/feed)</li><li><a href="http://jamescrisp.org/category/personal/feed">Personal</a> (http://jamescrisp.org/category/personal/feed)<br /></li></ul><br /><span style="font-weight: bold;">This Address</span><br />My previous posts will still be hosted on Blogger, but any new posts will be at the new address (<a href="http://jamescrisp.org/">http://jamescrisp.org</a>).<br /><br>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com151tag:blogger.com,1999:blog-28462179.post-78679488525210087402007-07-24T20:54:00.001+10:002007-07-24T21:01:01.316+10:00Ultimate Laptop Protection<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD70TNletY9KePST3lOs3S9b7G-uWBLpdcXpxsGkEENqlTRRjcKyDHNrQTbDOMUQq62ghO4vVIp5MfeMwM9CkWMsM5Xw-WsgICds799b0SWAmlqabDDyK_GXJaia602dL6g_fB/s1600-h/IMG_2514.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD70TNletY9KePST3lOs3S9b7G-uWBLpdcXpxsGkEENqlTRRjcKyDHNrQTbDOMUQq62ghO4vVIp5MfeMwM9CkWMsM5Xw-WsgICds799b0SWAmlqabDDyK_GXJaia602dL6g_fB/s400/IMG_2514.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5090716312576788514" /></a><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikzlKAph4ZBkbM02JyYKqYebDeuva-gEtE82EkEfNa5eyfnNNtGMvzZWGM6pJ4IShidvhNOQ8EMzYSushL5L3mXRQImGpTd7Xq3whsqdU0yqpBlqvfxN5lWt0JrbILXgnRs0ra/s1600-h/IMG_2516.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikzlKAph4ZBkbM02JyYKqYebDeuva-gEtE82EkEfNa5eyfnNNtGMvzZWGM6pJ4IShidvhNOQ8EMzYSushL5L3mXRQImGpTd7Xq3whsqdU0yqpBlqvfxN5lWt0JrbILXgnRs0ra/s400/IMG_2516.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5090716316871755826" /></a><br /><br />Better than money can buy.<br /><br />Made by my dad :-)James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com156tag:blogger.com,1999:blog-28462179.post-18456767350135085052007-07-15T23:58:00.000+10:002007-07-16T00:08:40.271+10:00Consulting<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCBra8oLF_EJ4g-3AUCXhR-2dRYfFW8tC0O2TfPbcHfOOsDdf-DKdczsgCf1vS0eUiGpXsechrVzUXzxYZ3VuMI2Mn2VEnLc9yyXAkXfXJzBOxKX3GG4yThEh0rTHCBo9pOgtx/s1600-h/consulting-sm.gif"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCBra8oLF_EJ4g-3AUCXhR-2dRYfFW8tC0O2TfPbcHfOOsDdf-DKdczsgCf1vS0eUiGpXsechrVzUXzxYZ3VuMI2Mn2VEnLc9yyXAkXfXJzBOxKX3GG4yThEh0rTHCBo9pOgtx/s400/consulting-sm.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5087423459646076210" /></a><br />A present from my <a href="http://ohsoosun.blogspot.com">adorable fiancée</a> :-)James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com134tag:blogger.com,1999:blog-28462179.post-4612353404068095452007-07-02T09:21:00.000+10:002007-07-02T10:22:01.472+10:00Adding a New Rails Project under SubversionI generally use <a href="http://subversion.tigris.org/">Subversion</a> for source control when given the choice. In day to day usage, I like to use <a href="http://tortoisesvn.tigris.org/">Tortoise SVN</a> as it gives you a GUI with tick boxes for files to check in. However, it's handy to use the command line tool for project setup and automation.<br /><br />Often, there is a subversion repository already set up and running on another machine. In this situation, I generally:<br /><ol><li>Check out the repository at the top level into a temporary directory through Tortoise, add a new directory ([appname]) for the new project, and check it in.</li><li>Generate the new rails app (rails [appname]).</li><li>Check out [appname] from the repository into the local directory [appname] which contains the Rails project.</appname></li><li>Add and check in all files through Tortoise.</li><li>Run the following commands from the command line to remove logs and tmp from the repository:</li></ol><pre><br />svn remove log/*<br />svn commit -m "removing all log files from subversion"<br />svn propset svn:ignore "*.log" log/<br />svn update log/<br />svn commit -m "Ignoring all files in /log/ ending in .log"<br />svn remove tmp/*<br />svn propset svn:ignore "*" tmp/<br />svn update tmp/<br />svn commit -m "Ignoring all files in /tmp/"<br /></pre><br />There's more Rails/Subversion info to be found on the <a href="http://wiki.rubyonrails.org/rails/pages/HowtoUseRailsWithSubversion">Rails wiki</a>.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com159tag:blogger.com,1999:blog-28462179.post-37868629809736344942007-06-12T23:26:00.000+10:002007-06-13T00:09:19.634+10:00The Castle Project - Rails for .NET<a href="http://www.castleproject.org/">The Castle Project</a> is an interesting open source alternative to ASP.NET / ADO.NET. Among other things, the Castle Project provides a <a href="http://www.rubyonrails.org/">Rails</a>-like development framework for .NET. It has an ActiveRecord implementation built on top of <a href="http://www.nhibernate.org/">NHibernate</a>, a very Rails-like MVC setup called MonoRail, and uses <a href="http://nvelocity.sourceforge.net/">NVelocity</a> for template style views. It's worth checking out. This <a href="http://hammett.castleproject.org/wp-content/uploads/2007/01/mr%20formvalidation.html">screencast</a> gives a bit of an overview.<br /><br />There's tough competition around the corner though, with <a href="http://msdn2.microsoft.com/en-us/vstudio/aa700831.aspx">Orcas already in beta</a>, providing XAML, LINQ and O-R mapping.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com124tag:blogger.com,1999:blog-28462179.post-78135485107053740922007-06-05T19:52:00.000+10:002007-06-05T19:49:18.842+10:00Is .NET or Java dying?Are C# and .NET losing ground as <a href="http://martinfowler.com/bliki/RubyMicrosoft.html">Martin Fowler</a> suggests? Or is <a href="http://www.businessweek.com/technology/content/dec2005/tc20051213_042973.htm">Java's market share dropping</a>? What about Ruby? And what about the Australian market in particular?<br /><br />Here's what I've been able to find.<br /><br /><br /><span style="font-weight: bold;">Job Trends</span><br />Which technologies have the most demand for people?<br /><br />From <a href="http://www.indeed.com/jobtrends?q=.net%2C+java%2C+c%23%2C+ruby&l=">Indeed.com</a>, which claims to search "millions of jobs from thousands of job sites", but I suspect may have a USA focus:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj70kMOZEAAHZDAUfQOfQjaz7pXTQyU7ZYcdTzmgdVqscznGAFsKjoIEPPMYpp96t9DMnI8n1GL7hM9Zl6rxTv_SAl9X10LeUuOhJ3VHi1u4_SPXSSYPmG8hTQaIl49l9oqODOW/s1600-h/indeed.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj70kMOZEAAHZDAUfQOfQjaz7pXTQyU7ZYcdTzmgdVqscznGAFsKjoIEPPMYpp96t9DMnI8n1GL7hM9Zl6rxTv_SAl9X10LeUuOhJ3VHi1u4_SPXSSYPmG8hTQaIl49l9oqODOW/s400/indeed.png" alt="" id="BLOGGER_PHOTO_ID_5072397284993317698" border="0" /></a><br /><br /><br /><a href="http://www.vision6.com.au/ch/dx8s7z/236720/f5785zrx3.pdf">"Best Talent Index May 2007"</a> from <a href="http://best-international.com.au/">Best People Solutions</a> gives an Australian perspective:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1gnDzbdQWtUru8X_WwkhNSV5EXu4v64ABpRkJAJOfh0AWa5YW9OBH_ArCHjalDAHPyx-XCv4JuoNAmKKaBML7qVyqsG-hODssrCeyY2cu7cuGlyqDc0DoB_mADteKHCIpUfMA/s1600-h/best.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1gnDzbdQWtUru8X_WwkhNSV5EXu4v64ABpRkJAJOfh0AWa5YW9OBH_ArCHjalDAHPyx-XCv4JuoNAmKKaBML7qVyqsG-hODssrCeyY2cu7cuGlyqDc0DoB_mADteKHCIpUfMA/s400/best.JPG" alt="" id="BLOGGER_PHOTO_ID_5072435381353233234" border="0" /></a><br /><br />Here's job counts from the (largest?) primarily Australian job search site <a href="http://www.seek.com.au/">Seek</a> on 5 June 2007, 3pm (today):<br /><table class="ttable" bordercolordark="#003366" bordercolorlight="#c0c0c0" id="Table2" align="center" border="1"><tr><td><b>Keyword(s)</b></td><td><b>Number of positions found</b></td></tr><tr><td>Java</td><td>3,414</td></tr><tr><td>".NET" or "dot net"</td><td>2,744</td></tr><tr><td>"c#" or "c sharp"</td><td>1,722</td></tr><tr><td>ruby</td><td>100</td></tr></table><br />As an aside, I remember doing a search on Seek for "ruby" about 6 months ago, and getting under 20 jobs mentioning it.<br /><br /><br /><span style="font-weight: bold;">Search Engine Number of Hits</span><br /><br />Extract from the <a href="http://www.tiobe.com/tiobe_index/index.htm">TIOBE Programming Community Index for June 2007</a>:<br /><br /><table class="ttable" bordercolordark="#003366" bordercolorlight="#c0c0c0" id="Table2" align="center" border="1"><tbody><tr><th align="center"> Position<br />Jun 2007</th><th align="center" >Position<br />Jun 2006</th><th align="center" >Delta in Position</th><th align="center" >Programming Language</th><th align="center" >Ratings<br />Jun 2007</th><th align="center" >Delta<br />Jun 2006</th><th align="center" >Status</th></tr> <tr> <td align="center">1</td> <td align="center">1</td> <td align="center"><img src="http://www.tiobe.com/tiobe_index/images/Same.gif" border="0" /></td> <td><a href="http://www.tiobe.com/tiobe_index/Java.html">Java</a></td> <td align="center">20.025%</td> <td align="center">-1.10%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">2</td> <td align="center">2</td> <td align="center"><img src="http://www.tiobe.com/tiobe_index/images/Same.gif" border="0" /></td> <td><a href="http://www.tiobe.com/tiobe_index/C.html">C</a></td> <td align="center">15.967%</td> <td align="center">-2.29%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">3</td> <td align="center">3</td> <td align="center"><img src="http://www.tiobe.com/tiobe_index/images/Same.gif" border="0" /></td> <td><a href="http://www.tiobe.com/tiobe_index/C__.html">C++</a></td> <td align="center">11.118%</td> <td align="center">+0.45%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">4</td> <td align="center">4</td> <td align="center"><img src="http://www.tiobe.com/tiobe_index/images/Same.gif" border="0" /></td> <td><a href="http://www.tiobe.com/tiobe_index/%28Visual%29_Basic.html">(Visual) Basic</a></td> <td align="center">9.332%</td> <td align="center">-0.85%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">5</td> <td align="center">5</td> <td align="center"><img src="http://www.tiobe.com/tiobe_index/images/Same.gif" border="0" /></td> <td><a href="http://www.tiobe.com/tiobe_index/PHP.html">PHP</a></td> <td align="center">8.871%</td> <td align="center">-0.72%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">6</td> <td align="center">6</td> <td align="center"><img src="http://www.tiobe.com/tiobe_index/images/Same.gif" border="0" /></td> <td><a href="http://www.tiobe.com/tiobe_index/Perl.html">Perl</a></td> <td align="center">6.177%</td> <td align="center">+0.17%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">7</td> <td align="center">8</td> <td align="center"> <img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /> </td> <td><a href="http://www.tiobe.com/tiobe_index/C_.html">C#</a></td> <td align="center">3.483%</td> <td align="center">+0.25%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">8</td> <td align="center">7</td> <td align="center"> <img src="http://www.tiobe.com/tiobe_index/images/Down.gif" border="0" /> </td> <td><a href="http://www.tiobe.com/tiobe_index/Python.html">Python</a></td> <td align="center">3.161%</td> <td align="center">-0.30%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">9</td> <td align="center">10</td> <td align="center"> <img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /> </td> <td><a href="http://www.tiobe.com/tiobe_index/JavaScript.html">JavaScript</a></td> <td align="center">2.616%</td> <td align="center">+1.16%</td> <td align="left"> A</td> </tr> <tr height="25"> <td align="center">10</td> <td align="center">19</td> <td align="center"> <img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /><img src="http://www.tiobe.com/tiobe_index/images/Up.gif" border="0" /> </td> <td><a href="http://www.tiobe.com/tiobe_index/Ruby.html">Ruby</a></td> <td align="center">2.132%</td> <td align="center">+1.65%</td> <td align="left"> A</td></tr></tbody></table><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWNLWLRaYaUcUnImv8BcB-l_tjIKmayBLr0ihdyIbcalbHRd-1dLNaCnnWougB9ljr0TZj1KvcfUhLJXAxDAsb31QfkN0Bg2lAs-MLMYVzV6cpSSobF3lB-0X_SjUnSKreUB8w/s1600-h/tpci_trends.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWNLWLRaYaUcUnImv8BcB-l_tjIKmayBLr0ihdyIbcalbHRd-1dLNaCnnWougB9ljr0TZj1KvcfUhLJXAxDAsb31QfkN0Bg2lAs-MLMYVzV6cpSSobF3lB-0X_SjUnSKreUB8w/s400/tpci_trends.png" alt="" id="BLOGGER_PHOTO_ID_5072444821691349858" border="0" /></a><br />I think this gives a good idea of web buzz, but suggest that most non-IT companies do not publish information about their projects and chosen technologies and languages on the web.<br /><br /><br /><span style="font-weight: bold;">Conclusion</span><br />The data collected suggests that:<br /><ul><li>Both .NET and Java are major players in the job market with thousands of positions advertised, implying wide industry adoption of both.</li><li>Neither .NET nor Java seem to be undergoing any significant decline in jobs.<br /></li><li>Java has much more information about it on the internet, although .NET is slowing gaining ground and Java slowly losing it.<br /></li><li>Ruby is comparatively tiny but growing rapidly in terms of jobs and information on the internet.<br /></li></ul><br /><br /><span style="font-weight:bold;">Thanks</span><br />Thanks to <a href="http://jchyip.blogspot.com/">Jason Yip</a> and <a href="http://binkysilhouette.blogspot.com/">Suzi Edwards</a> for their help finding/sourcing information.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com146tag:blogger.com,1999:blog-28462179.post-29941567005149226252007-05-20T17:04:00.000+10:002007-05-20T17:39:27.325+10:00Can you spot the bug?In a model class which has 'quantity', 'quantity_already_taken' and 'quantity_requested' properties, I add the following:<br /><pre><br /> def before_save<br /> quantity = 1 if quantity == 0<br /> if quantity + quantity_already_taken > quantity_requested<br /> ......<br /></pre><br />Tests blow up everywhere with:<br /><pre><br /> TypeError: nil can't be coerced into Fixnum <br /> (on the line with the addition)<br /></pre><br />Why?<br /><br />After a little more debugging, it is clear that 'quantity' is nil. How could that happen? <br /><br />The answer lies in the fact that Ruby requires an explicit self reference when using attribute writers (aka, property setters) within the class itself. This feels clunky to me, but for your information, here's a <a href="http://www.rubyfleebie.com/use-self-explicitly/">rationalisation of the explicit self requirement</a>.<br /><br />So, in case you're wondering, what happened above is that the 'if' line created a nil local variable called 'quantity'! This local variable then had higher scope precedence than the class attribute with the same name. The addition line was then using the local 'quantity' rather than the class attribute and hence failed with the nil error.<br /><br />All fixed by explicity referencing self:<br /><pre><br /> def before_save<br /> self.quantity = 1 if (quantity == 0)<br /> if quantity + quantity_already_taken > quantity_requested<br /> ......<br /></pre>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com26tag:blogger.com,1999:blog-28462179.post-17982837373374259642007-05-18T22:07:00.000+10:002007-05-18T22:18:41.398+10:00Fixing a Palm Treo's Digitizer (Touch Screen)Let me tell you a sad tale. One day, out of the blue, the digitizer on my palm started to drift. Every hour, it got worse. This meant that when you tried to click a button like 'Add' the Palm thought you clicked 'Delete' - no fun at all! It was possible to temporarily improve the situation by running the re-calibration program built into the system, but within a few hours, where you clicked again had very little relation to where the Palm thought you had clicked. After about a week, it was not possible to run the re-calibration program, as the digitizer was so far out (program just looped forever so I had to reboot the Palm). I discovered that there are actually keyboard shortcuts for just about everything, and that the 4-way <span class="blsp-spelling-error" id="SPELLING_ERROR_0">nav</span> button gets you most places, so the device wasn't a total write off. However, it was slow and cumbersome to use.<br /><br />Now, one month later, I'm sure you'll be thrilled to know that things are better, the sorry tale has had a happy ending (touch wood!). Much googling led to many suggested approaches to fixing the problem including:<br /><ul><li>Various auto-calibration programs (<span class="blsp-spelling-error" id="SPELLING_ERROR_1">AutoDigi</span>, <span class="blsp-spelling-error" id="SPELLING_ERROR_2">DigiFix</span>, etc).</li><li>Running paper around the screen under the casing to remove gunk.</li><li>Cleaning the insides by putting a vacuum cleaner to all openings.</li></ul>However, all of these approaches ended with disappointment and no noticeable improvement.<br /><br />Finally, near buying myself a new device (aside: it is a shame that Palm has not managed to produce a device significantly better than my several year old <span class="blsp-spelling-error" id="SPELLING_ERROR_3">Treo</span> 600), I came across a <a href="http://www.pdaparts.com/">site</a> selling replacement <span class="blsp-spelling-error" id="SPELLING_ERROR_4">Treo</span> 600 digitizer/screen modules. They kindly provide a very useful <a href="http://www.youtube.com/v/a5LQKzMi9pI">movie</a> on pulling your <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Treo</span> apart to help you replace your digitizer/screen module. With little to lose, I decided to open up the case of my <span class="blsp-spelling-error" id="SPELLING_ERROR_6">Treo</span> and see if there was anything I could fix.<br /><br />Following the instructions in the movie was not too difficult. I didn't have a small star <span class="blsp-spelling-error" id="SPELLING_ERROR_7">alan</span> key myself (required for opening the case), but I borrowed one from my dear dad, who has an amazing tool collection. Also, lacking a plastic case opening tool, I used a butter knife - this worked OK, but did damage the plastic of the case a little. If you have something made from thin and strong plastic, like the case opener in the movie, it would be a better tool for the job. I had a great time pulling everything apart and finally had all the components spread out before me. I cleaned the screen carefully (there was a fair bit of <span class="blsp-spelling-error" id="SPELLING_ERROR_8">grot</span> around the edges), fixed the buckled taping on the side of the screen, and put everything back together, carefully re-seating the various cables.<br /><br />And now, almost a month later, the digitizer still seems to be working fine! Hurrah! So if you are contemplating what to do about your Palm's broken or drifting digitizer, I recommend pull it apart, clean it all and re-seat cables and then hope for the best!James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com43tag:blogger.com,1999:blog-28462179.post-75609736189528301292007-04-19T10:02:00.000+10:002007-04-29T15:07:22.751+10:00Experience of an International Amazon VirginRecently I ordered 12 books from Amazon. It was my first time.<br /><br />The process started really well - quite easy and pleasant to find the books I was after. Not to mention that amazing range and the great option of getting cheaper second hand books. Adding to the shopping cart was also a breeze.<br /><br />I was pretty impressed, good prices, nice process. But then the honeymoon was over. Time to check out - stream of consciousness. First, I need to enter address details. Fine, as expected. Then I get a message (from memory) "There is a slight problem with your order. Some of the books you have chosen cannot be shipped to your address. Change your delivery address or change the quantity to 0 on these books.". Not happy! One third of my books (second hand ones) cannot be sent. That means I need to cancel the check out process, remove 4 books from my cart and then try and find the same books from other more expensive suppliers which can be shipped international. So I try again, adding the same book from multiple suppliers to my cart, in the hope of finding one which can deliver to Australia. Then it's back to the checkout process again.. Problem - I missed one book and have to cancel the process and go back to basket process again. Great, all books are OK, finally time to complete the order. So I get to review my order, and it says at the top something like "With an Amazon credit card, this order would be $324 rather than $368". No other total including postage is provided. So is my order $368? Maybe? Further screens finally confirm that this is the case. Nowhere is it possible to see how much postage is per book - you have to work it out yourself doing best guesses and following the Amazon formula. Maybe it would have been better to get a new book rather than a second hand book, as second hand books have twice the postage charge.. ah well, too late now, I'm not going to go through the whole process yet another time! So finally I can check out and my credit card is charged. However, since my credit card is hit by a multitude of different vendors that use Amazon as a front, within seconds of each other, some transactions are rejected as my credit card does not allow too many transactions in too short a time (some sort of security feature?). Finally, after getting a few emails from Amazon saying the card could not be charged, and then telling Amazon to retry, my order is at last paid for and on the way.<br /><br />Okay, so what could be done to make this better?<br /><ol><li>Allow buyers to filter their results so they only see books that can be delivered to their addresses.</li><li>Do not use patronising messages like "there is a slight problem".</li><li>Do not suggest that people change their delivery address to another country.. that is clearly not going to happen!</li><li>Show the cost of postage all throughout the process. Book buyers know they are going to have to pay postage and want to optimise their orders taking it into account.<br /></li><li>Do not show the order total including postage for the first time as a confusing advertisement ("With an Amazon credit card, this order would be $324 rather than $368"). Instead, provide a simple breakdown in a table, including postage on each book.</li><li>Hit the credit card once per order, and divvy up the money at Amazon internally, rather than allowing each book vendor to do it and having credit card rejections as a result.</li></ol><span style="font-weight: bold;">UPDATE:</span> My books arrived about two weeks after I ordered them. Delivery was smooth and on time. Unfortunately, one of the <span class="blsp-spelling-error" id="SPELLING_ERROR_0">CDs</span> that came with a book was broken. Amazon has kindly agreed to replace it and the book.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEis4Sp-p8oSRdO0ivp7rK2WLBVyjdfHus96UAC_25iaiDVuATgOKwl4uVySM411QeSyBHDXAYTd_1oJVaubROG9DRfeOqdri-1sjecKl_-zvGOWuaNPA8fjhj-qs9qbPHV_I7IQ/s1600-h/IMG_1941.JPG"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEis4Sp-p8oSRdO0ivp7rK2WLBVyjdfHus96UAC_25iaiDVuATgOKwl4uVySM411QeSyBHDXAYTd_1oJVaubROG9DRfeOqdri-1sjecKl_-zvGOWuaNPA8fjhj-qs9qbPHV_I7IQ/s400/IMG_1941.JPG" alt="" id="BLOGGER_PHOTO_ID_5055431568138436610" border="0" /></a>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com35tag:blogger.com,1999:blog-28462179.post-67856530375832063342007-04-12T14:32:00.000+10:002007-04-12T14:44:53.471+10:00Tips for Developing Mephisto Plugins with Liquid and Rails<span style="font-weight: bold;"></span>When I was writing a <a href="http://jamescrisp.blogspot.com/2007/03/contact-feedback-form-plugin-for.html">contact form plugin</a> for <a href="http://mephistoblog.com/">Mephisto</a>, I had a lot of trouble finding documentation and ended up reading lots of code and experimenting. That was fun, but fairly slow, so I hope this post can save future plugin developers time, and help them avoid some of the gotchas I stumbled over.<br /><br /><span style="font-weight: bold;">Repository Directory Structure</span><br />At the most macro level, your repository needs to have a 'plugins' directory, and then a directory named after your plugin. Eg,<br />.../plugins/my_new_plugin/...<br />If this is not set up correctly, your plugin will not be able to be installed via 'ruby script/install plugin <repository>' method.<br /><br /></repository><span style="font-weight: bold;">Liquid Plugins Directory and Init.rb</span><br />As you probably know, Mephisto uses <a href="http://home.leetsoft.com/liquid">Liquid</a> for page templates. Liquid can be extended with new tags/blocks. The way to do this in a plugin is to set up a 'mephisto/liquid' directory with your extensions in it. See <a href="http://mephisto-contact-form-plugin.googlecode.com/svn/plugins/mephisto_contact_form/lib/mephisto/liquid/">example here</a>. So that's great, but you also need to register it in init.rb. Here's the contact form's <a href="http://mephisto-contact-form-plugin.googlecode.com/svn/plugins/mephisto_contact_form/init.rb">init.rb</a> - check out the line about 'register_tag'.<br /><br /><span style="font-weight: bold;">Mephisto Plugin Class</span><br />Mephisto trunk now has a base class for plugins - Mephisto::Plugin. Inheriting from this allows you to set up routes to brand new controllers you create. See <a href="http://mephisto-contact-form-plugin.googlecode.com/svn/plugins/mephisto_contact_form/lib/plugin.rb">contact form example here</a>. This opens the door to writing Mephisto plugins which do postback and processing. It is also possible to add in tabs and forms in the administration interface. <a href="http://techno-weenie.net/">Rick</a>'s <a href="http://svn.techno-weenie.net/projects/mephisto/plugins/mephisto_feedback/lib/plugin.rb">feedback plugin</a> shows how to do this.<br /><br /><span style="font-weight: bold;">Using Liquid Templates from Your Plugin</span><br />One of the trickiest bits was getting the plug-in controller to render a liquid template. This is important if you want your additions to Mephisto to have the same layout and colours as the rest of the site. The way I'll outline below works fine, but it is not ideal. Hopefully there is a better way to do this (eg, some sort of Liquid API for Mephisto plugins).. if you know how a better way, please let me know!<br /><br />I had my plugin controller inherit from the Mephisto ApplicationController to gain access to the method 'render_liquid_template_for'. You can see the <a href="http://mephisto-contact-form-plugin.googlecode.com/svn/plugins/mephisto_contact_form/lib/controllers/contact_form_controller.rb">code here</a>. However, this led to thorny problems where the plug-in classes were getting loaded only once when the server started, but Mephisto (and the ApplicationController) were getting reloaded for every request. First request worked fine, but nasty errors were spat out on the second and subsequent requests. To resolve this, I removed the plug-in from the 'load_once_paths'. You can see how to do this in the <a href="http://mephisto-contact-form-plugin.googlecode.com/svn/plugins/mephisto_contact_form/init.rb">init.rb</a>.<br /><br /><span style="font-weight: bold;">Models, Views & Controllers Directories and Init.rb</span><br />Okay, this is open to personal taste. I like to have similar directories in my plugin to a normal app. Eg, separate directories for controllers, model, etc. This causes a bit more work, as you need to add the extra directories to various global path variables. For an example of how to do this, take a look at 'models_path' and 'controllers_path' in this <a href="http://mephisto-contact-form-plugin.googlecode.com/svn/plugins/mephisto_contact_form/init.rb">init.rb</a> and the physical directory structure of the <a href="http://mephisto-contact-form-plugin.googlecode.com/svn/plugins/mephisto_contact_form/lib/">contact form's lib directory</a>.<br /><span style="font-weight: bold;"><br /></span>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com121tag:blogger.com,1999:blog-28462179.post-91352679449020672212007-04-03T23:11:00.000+10:002007-04-04T00:24:38.062+10:00Improve Rails Performance Through Eternal Browser Caching of AssetsI've been working on a rails app which has got quite a number of pages that share the same two <span class="blsp-spelling-error" id="SPELLING_ERROR_0">css</span> files and 3 javascript files. However, every time I visited any page of the app, all of <span class="blsp-spelling-error" id="SPELLING_ERROR_1">javascripts</span> and <span class="blsp-spelling-error" id="SPELLING_ERROR_2">css</span> files were being loaded from the server. Not good - site was very slow. Mucking around with 'about:cache' command in <span class="blsp-spelling-error" id="SPELLING_ERROR_3">Firefox</span> revealed that the <span class="blsp-spelling-error" id="SPELLING_ERROR_4">css</span> and javascript files had expiry dates set in the past - <span class="blsp-spelling-error" id="SPELLING_ERROR_5">ie</span>, no caching of them at all. Also, all the links to <span class="blsp-spelling-error" id="SPELLING_ERROR_6">sylesheets</span> and javascript files generated by rails had ?<long>[some long number] after them. Some <a href="http://www.endikos.com/2007/2/23/rails-asset-id-and-seo">research on the web</a> revealed that this is a new rails feature for caching - the long number is a timestamp for when the asset was last modified.<br /><br />Okay, so why were these assets not being cached? A quick check with <span class="blsp-spelling-error" id="SPELLING_ERROR_7">wget</span> --save-headers revealed that the web server was sending a <span class="blsp-spelling-error" id="SPELLING_ERROR_8">nocache</span> directive to the browser. This seems to be the default setup for <span class="blsp-spelling-error" id="SPELLING_ERROR_9">webrick</span> and also for my shared <span class="blsp-spelling-error" id="SPELLING_ERROR_10">apache</span> hosting on <a href="http://railsplayground.com/"><span class="blsp-spelling-error" id="SPELLING_ERROR_11">railsplayground</span></a>. Considering the new rails asset <span class="blsp-spelling-error" id="SPELLING_ERROR_12">management</span> system with the ?<last>[last modified timestamp] in the URLs, <span class="blsp-spelling-error" id="SPELLING_ERROR_13">nocache</span> seems wrong. The browser should never expire the cache since rails will handle cache invalidation by updating the asset <span class="blsp-spelling-error" id="SPELLING_ERROR_14">url</span> with a new timestamp.<br /><br />So, how can we implement no/very long cache expiry? In <span class="blsp-spelling-error" id="SPELLING_ERROR_15">apache</span>, you can use mod_expires or mod_headers to do this. My shared hosting does not support mod_expires, so I went for mod_headers in my .<span class="blsp-spelling-error" id="SPELLING_ERROR_16">htaccess</span> file.<br /><br />Using mod_headers:<br /><filesmatch><FilesMatch "\.*$"><br />Header set Cache-Control "max-age=29030400"<br /></FilesMatch></filesmatch><br /><br />OR using mod_expires:<br /><span class="blsp-spelling-error" id="SPELLING_ERROR_17">ExpiresActive</span> On<br /><span class="blsp-spelling-error" id="SPELLING_ERROR_18">ExpiresDefault</span> "access plus 1 year"<br /><br />Either of the the above will set up a cache expiry time of one year for all content (best you only do this for your rails app directories).<br /><br />With a cache expiry time of one year in place, my rails apps run much much faster.</last></long>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com33tag:blogger.com,1999:blog-28462179.post-48477276763473355632007-04-03T21:19:00.000+10:002007-04-03T23:08:49.634+10:00Naked Economics by Charles WheelanJust finished reading 'Naked Economics: Undressing the dismal science'. It was a present from a friend, and I've been meaning to read it for a while. Glad I finally got around to it.<br /><br />From the title, I assumed the book aimed to point out the failures of economics as a science. Not so at all - it was written by an economist and provides a high level overview of capitalism in layman's terms.<br /><br />Here's some interesting questions and explanations from the book:<br /><ul><li>Why do we have money? So that we can indirectly swap our labour or goods for the things we want, even if the person with the things we want is not interested in our labour or goods. Without money, we would need to barter. That's fine if you're swapping chickens for rice. But what happens if you do web design, and you want meat for dinner, and the butcher does not want a website?</li><li>Money has value only because we all believe it does. We have faith that if we sell something (<span class="blsp-spelling-error" id="SPELLING_ERROR_0">ie</span>, convert it to the common value unit), we will then be able to swap that money for something we want.</li><li>Why have markets anyway? Markets produce what people want - <span class="blsp-spelling-error" id="SPELLING_ERROR_1">ie</span>, what people are willing to pay for (or at least what we are convinced into wanting through advertising etc).<br /></li><li>Why not set the price of everything rather than letting it get worked out in a market? Well, it would be an enormous job, and things would not reflect the cost of production. <span class="blsp-spelling-error" id="SPELLING_ERROR_2">Eg</span>, bird flu wipes out half of the chickens in the world. There are now less chickens to go around so chicken becomes more expensive. People who really want chicken can still get it, but it costs them more. People who don't care as much or can't afford it eat fish or beef instead. If the cost of chicken was a constant mandated by the state, chicken distribution would need to be mandated in some other way. If there's not enough chicken for everyone who wants it, who should get it? First come first served? Political clout? Personal connections?<br /></li><li>Markets destroy. A new way to mechanize weaving may make thousands unemployed and destroy towns and communities. But according to <span class="blsp-spelling-error" id="SPELLING_ERROR_3">Wheelan</span>, the country as a whole is better off as we are able to produce more for less cost, hence increasing our standard of living. <span class="blsp-spelling-error" id="SPELLING_ERROR_4">Ie</span>, as a consumer, you may now be able to buy a shirt for half the price.<br /></li><li>In politics, small motivated groups often drive policy. <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Eg</span>, the general population does not care much one way or other on a subsidy on growing alfalfa. It might cost each person in Australia 0.01c per year. However, if the subsidy was to be removed, and the alfalfa farmers would care a lot. It may well mean their livelihoods so they would demonstrate, make campaign donations, vote as a block and generally make as much fuss as possible to make sure the subsidy was not removed.<br /></li><li>Why do people work in sweatshops? According to <span class="blsp-spelling-error" id="SPELLING_ERROR_6">Wheelan</span>, the pay is generally better than for other jobs available - <span class="blsp-spelling-error" id="SPELLING_ERROR_7">ie</span>, sweatshops are not the cause of the problem, rather a symptom of the general poverty and lack of opportunity in the area.<br /></li><li>Why is free trade good? So that everyone can do what they do best - <span class="blsp-spelling-error" id="SPELLING_ERROR_8">ie</span>, specialise to the max. The idea being that if everyone works on what they are most good at, then productivity overall is higher.</li><li>Why are tariffs bad? Because they support local industries that are not viable - <span class="blsp-spelling-error" id="SPELLING_ERROR_9">ie</span>, a poor uses of resources.<br /></li><li>Why do we need governments? To provide the rails for capitalism. <span class="blsp-spelling-error" id="SPELLING_ERROR_10">Eg</span>, to enforce laws (so you can't just kill me and take my stuff), to regulate the excesses of the free market and to provide goods and services that people need but that free markets will never provide. Also, to do things that individuals cannot do alone, but are in the interest of the population as a whole - <span class="blsp-spelling-error" id="SPELLING_ERROR_11">eg</span>, build infrastructure.</li><li>Care for the environment is a luxury good - <span class="blsp-spelling-error" id="SPELLING_ERROR_12">ie</span>, if you and your family are starving, cutting down trees to sell for food seems like a pretty good idea.</li><li>Companies destroy the environment because the current monetary cost of environmental destruction is usually minimal. If the full cost of environmental destruction was factored into the market (<span class="blsp-spelling-error" id="SPELLING_ERROR_13">eg</span>, companies have to pay for pollution and destruction), then environmental destruction would slow dramatically.<br /></li><li>Who can create new money? The reserve bank, and it does it by buying bonds from banks with money that did not previously exist.<br /></li><li>How can the reserve bank change interest rates? It can sell government bonds at its target rate and it can buy bonds (with brand new money) or sell bonds from/to trading banks to influence the amount of cash the banks have to lend. <span class="blsp-spelling-error" id="SPELLING_ERROR_14">Eg</span>, lots of cash at a trading bank means they'll lower the interest rates so as to rent out the money.</li><li>How come the economy can go into recession for no real reason? If people are worried, they don't spend. If people don't spend, then companies can't afford new/current investment. People get sacked and then can spend even less. People get more worried and the cycle continues.</li><li>Why is the level of savings in a country important? Money in the bank means it can be <span class="blsp-spelling-corrected" id="SPELLING_ERROR_15">lent</span> out to people who want to use it to create new businesses or expand current businesses. Access to capital allows growth.<br /></li></ul>I wouldn't take these on face value, and I would certainly question some of the assumptions on which they are based. However, they provide some interesting areas for further thought.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com17tag:blogger.com,1999:blog-28462179.post-25775068034620175172007-03-27T14:11:00.000+10:002007-03-27T17:00:07.468+10:00Starting at ThoughtWorks: First Five WeeksWell, I've been with <a href="http://www.thoughtworks.com/">ThoughtWorks</a> for just over 5 weeks now, and I thought I'd write down some thoughts before the hiring and joining process got lost in the misty swamp of my memory.<br /><br /><span style="font-weight: bold;">Hiring process</span><br />To cut a long story short, I did a phone screen with HR, a coding test and then some fairly quick aptitude and personality tests and finally 3 interviews. You have something like a week to do the coding test and then submit your code for review. I had the other tests and interviews on a single day. Although this sounds pretty horrendous, it actually wasn't too bad. Tests were pretty quick and the interviewers were astoundingly friendly. I finished by something like 3pm in the afternoon, including a lunch break, and surprisingly didn't feel too bad or stressed afterwards.<br /><br /><span style="font-weight: bold;">Induction</span><br />As a fairly impressive start, I had 2 days of induction in Melbourne (I live in Sydney). ThoughtWorks arranged drivers, hotel and flight so it was all very smooth. This was lucky as I was pretty jet lagged and confused - I'd just flown back from an <a href="http://jamescrisp.blogspot.com/search/label/Travel">overseas holiday</a> not long before. Induction was largely getting a company provided laptop, meeting people and getting an introduction to various internal systems and procedures. As an aside, I've heard that there is now an "immersion" process where you get sent to India for a week or two for induction but can't comment on that.<br /><br /><span style="font-weight: bold;">A few weeks on the beach</span><br />When you're not assigned to a client project, you are "on the beach". This means you go into your local office with your laptop. It's really great - there's no particular tasks assigned to you, but the opportunity is there to get involved in a lot of interesting stuff. To give you some examples, here's some of the stuff I've had the chance to do:<br /><ul><li>Write an <a href="http://jamescrisp.blogspot.com/2007/03/contact-feedback-form-plugin-for.html">open source plug-in for Mephisto</a> for <a href="http://studios.thoughtworks.com/">ThoughtWorks Studios</a><br /></li><li>Be involved in scoping out and estimating for a RFI from a new client</li><li>Pair with another developer to do code reviews of potential new recruits</li><li>Help out briefly with a fun project to develop a <a href="http://sourceforge.net/projects/jcruisemonitor/">driver for a USB build light</a> for continuous integration servers (red for broken build, green for good build, etc)</li><li>Help out on client projects - I was asked to whip up a little proof of concept for JRuby and Java integration and <a href="http://jamescrisp.blogspot.com/2007/02/jruby-setup.html">learnt a bit</a> getting this set up</li><li>Do a little bit of Google Maps integration<br /></li><li>Meet colleagues and learn more about procedures etc</li><li>Get invited to lunch with the managing director - this is something that happens for all new hires and I think it's really great</li><li>Almost go out on a pre-sales call (I've got to go back to Melbourne and will miss this unfortunately)<br /></li><li>Catch up on tech reading such as blogs, books etc</li><li>Go to a swanky <a href="http://www.thoughtworks.com.au/tech-briefing.html">talk</a> given by Martin Fowler and Kristan Vingrys<br /></li><li>Eat lots of free lunches (usually twice a week) and attend various talks at the office given by other consultants<br /></li><li>Drink lots of free coffee (ThoughtWorks has a coffee tab with a local cafe)</li></ul><span style="font-weight: bold;">First project<span style="font-weight: bold;"></span><br /></span>Much fun as it is on the beach, after a few weeks, I was itching to join the big boys and go on a project. Getting assigned to a project is the purview of your professional services manager, and can be pretty changeable. The saying is that "you don't really know what project you're on till you walk in the door of the client site" and I've even heard "you don't really know what project you're on till you're on the plane home". There's a grain of truth in these - it can look like you are going to go on a project and then it doesn't come through, or some other project becomes more important or whatever. I almost went on several different projects before finally ending up on quite a cool Ruby / Rails project with a startup in Melbourne. So, I got to join the jet set and have been flying down to Melbourne during the week, and back for the weekends. This is a bit tiring, but ThoughtWorks does its best to make things comfortable. I'm staying in a really nice corporate apartment in Melbourne, flights are arranged and paid for and drivers are scheduled for pickup and drop off to the airport. There's also a generous per diem allowance for food. The project is really cool, and I'm enjoying it, but can't say more as it is under a NDA.<br /><br /><span style="font-weight: bold;">Back on the beach.. but only for two days!<br /></span>My first project was two weeks, so after that I returned to the beach. Today is my second day on the beach. However, it turns out that the client was very happy with our first two weeks work and they've invited us back again until Easter. This means I need to fly back to Melbourne tomorrow. This won't continue indefinitely though - when I was discussing the project with my professional services manager, we agreed that I would not need to stay on a Melbourne project for more than 6 weeks. And clearly this is in ThoughtWorks interest as well - it costs a lot more to fly somebody in from Sydney every week and provide accommodation etc. I'm going to be transitioning off the project by Easter and a Melbourne based consultant is going to take over from me if the project continues further. It'll be good to be on a Sydney based project again but I feel it would be unfair not to say that ThoughtWorks has done a really good job in making working in another city as convenient and pleasant as possible.<br /><br /><span style="font-weight: bold;">Various benefits<br /></span>ThoughtWorks is pretty generous in the expenses department. They cover your mobile phone, home internet, per diem when away, give an allowance for training courses and books, etc. There's also lots of free lunches, food, coffee and catered events.<br /><br /><span style="font-weight: bold;">Transparency and knowing what's going on<br /></span>I've been quite impressed to get a monthly update email that talks about ThoughtWorks plans, goals and financials, headcount etc in significant detail. There's also various update meeting where you get to hear how projects are going and what's happening with various clients. Personally I'm really glad to see this type of thing, as at previous jobs, this has been privileged information, and most of the time, I have not really had any idea how well the company is doing financially as a whole, or what the future plans and directions are.<br /><br /><span style="font-weight: bold;">Variety and Unpredictability<br /></span>These are really two faces of the same coin, and depending on your character and experience, I think you might either love or hate this. You really don't know what project you're going to be working on, what your role will be, what industry the client is in, what type of development they need or for what platform or in what language. In fact, you don't even know what city you're going to be in during a given week. I'm enjoying this at this point as my last job was always in the same office, with the same technologies etc. However, I can see it may be trying in the long term, and it does make it difficult to do the shopping or organise things with friends during the week. On the other hand, I have heard that most of the work in Sydney is for big companies like banks and telcos in the city CBD within walking distance to the office, and the majority in Java. So perhaps my short experience so far is not the norm.<br /><br /><span style="font-weight: bold;">In conclusion...<br /></span>So far, I can honestly say that it's been really great working at ThoughtWorks. I've had a chance to do some of the stuff I've wanted to do for ages like work on a bit of open source and do some commercial Ruby on Rails work. My colleagues have been friendly and welcoming, and I've been wowed by the level of care that ThoughtWorks takes of its employees.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com40tag:blogger.com,1999:blog-28462179.post-48217088002329910942007-03-19T22:26:00.000+11:002007-04-17T13:14:15.012+10:00Contact / Feedback Form Plugin for Mephisto<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivAUwa6c6xD8epWuG7v0d85jYHVx79uvipq0JuB2EMd_xdtZ_bOiYXB4RY1sFNS_rjE9Sxo0hSrcvMm2a0Slk8GhAHd_cAgDH7DEG1cuqdYgUlDsb8fmCXuluPWMeGO-xYTwLE/s1600-h/plugin.JPG"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivAUwa6c6xD8epWuG7v0d85jYHVx79uvipq0JuB2EMd_xdtZ_bOiYXB4RY1sFNS_rjE9Sxo0hSrcvMm2a0Slk8GhAHd_cAgDH7DEG1cuqdYgUlDsb8fmCXuluPWMeGO-xYTwLE/s200/plugin.JPG" alt="" id="BLOGGER_PHOTO_ID_5039329850314218626" border="0" /></a><span style="font-weight: bold;">Introduction</span><br />If you use <a href="http://mephistoblog.com/">Mephisto</a>, a content management / blogging system written in <a href="http://www.rubyonrails.org/">Rails</a>, you may well be interested in using this new plug-in. It provides a form that lets visitors to your site leave their contact details and send you messages or feedback via email.<br /><br /><span style="font-weight: bold;">License</span><br />This plug-in was developed for the new <a href="http://studios.thoughtworks.com/">ThoughtWorks Studios site</a>. As I wrote it at and for work, it is copyright ThoughtWorks, 2007. However, ThoughtWorks, being generous souls, is happy for me to open source it under the <a href="http://www.apache.org/licenses/LICENSE-2.0.html">Apache 2.0 licence</a>, which pretty much means you have free reign to use it as you want.<br /><br /><span style="font-weight: bold;">Requirements</span><br /><ul><li>Mephisto Edge (the latest stable 0.7.3 release does not have support for Mephisto plugins)</li><li>Rails Edge (required by Mephisto edge)</li><li>ActionMailer (comes with Rails) correctly configured with SMTP server etc, so that emails can be delivered. See "Configuration" section <a href="http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer">here</a> for more details.<br /></li></ul><br /><span style="font-weight: bold;">Installation</span><br />ruby script/plugin install http://mephisto-contact-form-plugin.googlecode.com<br />/svn/plugins/mephisto_contact_form<br /><br />or in your vendor/plugins directory for Mephisto:<br /><br />svn checkout http://mephisto-contact-form-plugin.googlecode.com<br />/svn/plugins/mephisto_contact_form mephisto_contact_form<br /><br />Make sure you restart your web server at this point so that the plugin is loaded.<br /><br /><span style="font-weight: bold;">Setup</span><br />1. Create a new template called 'contact_us.liquid' though the admin web interface (under the 'Design' tab).<br />Paste in the following code:<br /><br /><H1>Contact Us</H1><br />{% contactform %}<br /><p>{{ form.name }}<label for="author"><small>Your name</small></label></p><br /><p>{{ form.email }}<label for="email"><small>Email address</small></label></p><br /><p>{{ form.phone}}<label for="phone"><small>Phone number (optional)</small></label></p><br /><p>{{ form.subject}}<label for="subject"><small>Subject</small></label></p><br /><p>{{ form.body }}</p><br /><p>{{ form.submit }}</p><br />{% endcontactform %}<br /><br />Feel free to modify labels, layout etc.<br /><br />2. Edit<br />{MEPHISTO_ROOT}\vendor\plugins<br />\mephisto_contact_form\lib\contact_notifier.rb<br />and put in the email address you want contact form submissions to go to.<br /><br />3. Link to "/contact_form" from your site.<br /><br /><span style="font-weight: bold;">Any issues / questions / suggestions?</span><br />Best to post comments on this blog.<br /><br /><span style="font-weight: bold;">Technical Info<br /></span>The contact form plugin is actually a combination of a rails plugin, a liquid block plugin and a Mephisto plugin. See this post about <a href="http://jamescrisp.blogspot.com/2007/04/tips-for-developing-mephisto-plugins.html">developing Mephisto plugins</a> for more information.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com143tag:blogger.com,1999:blog-28462179.post-89017364155130797392007-03-05T22:04:00.000+11:002007-03-06T17:50:19.537+11:00The Secrets of Consulting by Gerald WeinbergThe Secrets of Consulting by Gerald Weinberg is one of the most entertaining (largely?) non-fiction books that I have read - a heady mix of <a href="http://jamescrisp.blogspot.com/2007/01/how-to-win-friends-and-influence-people.html">How to Win Friends and Influence People</a>, <a href="http://en.wikipedia.org/wiki/Fear_and_Loathing_in_Las_Vegas">Fear and Loathing in <span class="blsp-spelling-error" id="SPELLING_ERROR_0">Las</span> Vegas</a> (just look at the illustrations!) , and the <a href="http://en.wikipedia.org/wiki/Ten_Commandments">10 Commandments</a>. The book provides general advice, case studies/stories and then derives general "rules" and recommendations from these.<br /><br />Personally, I found the chapter on the pricing of consulting to be particularly interesting. Thinking about setting a price previously, I would have suggested it should be enough to cover costs and make a bit of a profit. Weinberg points out that price is more than this - it is a big factor in the relationship and the level of respect for the consultant.<br /><br />The Weinberg's consulting "rules" are quite numerous - my personal favourites are:<br /><ul><li>"If you can't fix it, feature it."</li><li>"It may look like a crisis, but it's only the end of an illusion." </li><li>"You'll never accomplish anything if you care who gets the credit."</li><li>"If <span class="blsp-spelling-error" id="SPELLING_ERROR_1">something's</span> faked, it must need fixing."</li><li>"The name of the thing [label] is not the thing."</li><li>"It tastes better when you add your own egg."</li><li>"You don't get <span class="blsp-spelling-error" id="SPELLING_ERROR_2">nothin</span>' for <span class="blsp-spelling-error" id="SPELLING_ERROR_3">nothin</span>'. Moving in one direction incurs a cost in the other."</li><li>"Whatever the client is doing, advice something else."</li><li>"What you don't know may not hurt you, but what you don't remember always does."</li><li>"Clients always know how to solve their problems and always tell the solution in the first five minutes."</li><li>"When change is inevitable, we struggle most to keep what we value most."</li><li>"The biggest and longest lasting changes usually originate in attempts to preserve the very thing ultimately changes most."</li><li>"Effective problem-solvers may have many problems, but rarely have a single, dominant problem."</li><li>"Make sure they pay you enough so they'll do what you say. The most important act in consulting is setting the right fee."</li><li>"The more they pay you, the more they love you. The less they pay you, the less they respect you."</li><li>"Spend at least one day a week getting exposure." and "Spend at least 1/4 of your time doing nothing." and make sure your fee covers this.<br /></li><li>"Set a price so you won't regret it either way."</li><li>"If they don't like your work, don't take their money."<br /></li><li>"Cucumbers get more pickled than brine gets <span class="blsp-spelling-error" id="SPELLING_ERROR_4">cucumbered</span>."</li><li>"Give away your best ideas."</li><li>"Look for what you like in the present situation and comment on it."</li><li>"Study for understanding, not for criticism."</li><li>"Never promise more than 10% improvement.. if you happen to achieve more than 10% improvement, make sure it isn't noticed."</li><li>"Consultants tend to be the most effective on the third problem you give them."</li><li>"The child who receives a hammer for Christmas will discover that everything needs pounding."</li></ul>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com10tag:blogger.com,1999:blog-28462179.post-75068967020246302022007-03-01T22:18:00.000+11:002007-03-01T22:49:59.627+11:00_vimrc for RubyIn the past I've used gvim for Ruby coding. It's been pretty good, especially with the new tabbed editing and omni complete (bit like intellisense in Visual Studio) introduced in vim 7. However, when I downloaded vim at work, I was missing the secret sauce - a good vimrc. Here's my usual vimrc for ruby:<br /><pre>set nocompatible<br />behave xterm<br />set selectmode=mouse<br />set nu<br />set tabstop=2<br />set shiftwidth=2<br />set softtabstop=2<br />set ai<br />set columns=100<br />set lines=70<br />set guifont=Courier:h10<br />set expandtab<br />set smarttab<br />let g:rubycomplete_rails = 1</pre>Among other things, it makes the default window size bigger, uses a prettier font, sets up auto indenting ruby style, and turns on omni-complete.<br /><br />By the way, on windows, assuming a default install, vimrc is to be found here:<br /><pre>C:\Program Files\Vim\_vimrc</pre>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com17tag:blogger.com,1999:blog-28462179.post-32530037924732969822007-02-27T15:59:00.000+11:002007-02-27T22:54:23.127+11:00JRuby SetupRecently got a JRuby/Rails system with Java integration up and running. Unfortunately, it took quite a few hours, as most of the docs and code you find through Google are out of date.<br /><br />If you use JRuby 0.9.2 from Codehaus, you will get an error similar to this when you try to access a rails application:<br /><br />[2007-02-26 17:54:59] INFO WEBrick::HTTPServer start: pid=22540508 port=3000<br /><ArgumentError: Anonymous modules have no name to be referenced by><br />["c:/jruby-0.9.2/lib/ruby/gems/1.8/gems/activesupport-1.4.1/lib/<br />active_support/dependencies.rb:402:in `to_constant_name'...<br /><br />If you're stuck in this rut, fear not! <a href="http://blog.nicksieger.com/">Nick Sieger</a> has written <a href="http://trac.caldersphere.net/projects/main/wiki/JRubyQuickStart">very helpful instructions</a> which outline how to get and set up the latest development snapshot. Please note that in addition to the instructions, you need to set your JRUBY_HOME environment variable. Under Windows, do something like this:<br /><br />set JRUBY_HOME=c:\jruby<br /><br />If you'd prefer not to use the snapshot, you can get the source code through subversion from: <br /><br /><a href="http://svn.codehaus.org/jruby/trunk/jruby">http://svn.codehaus.org/jruby/trunk/jruby</a><br /><br />but at the time of this post, you need to run svn checkout or update with "--ignore-externals" to avoid the following error:<br /><br />Error: URL 'svn://rubyforge.org/var/svn/bfts/bfts/trunk' doesn't exist<br /><br />Many thanks to <a href="http://blog.nicksieger.com/">Nick Sieger</a> and the <a href="http://archive.jruby.codehaus.org/user">JRuby user mailing list</a> for their help.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com25tag:blogger.com,1999:blog-28462179.post-84724766647007173302007-02-25T21:59:00.000+11:002007-02-25T22:36:48.787+11:00Using floating point variables to represent money => not a good idea!I was reading through some code the other day and was surprised to find that it was using doubles to represent dollar amounts. Reason for the alarm bells is that doubles and floats cannot accurately represent many decimal fractions (<span class="blsp-spelling-error" id="SPELLING_ERROR_0">eg</span>, 0.1), since doubles and floats internally work with powers of 2 rather than powers of 10. These inaccuracies are likely to lead to significant errors, especially when performing arithmetic (<span class="blsp-spelling-error" id="SPELLING_ERROR_1">eg</span>, adding up a table of dollar amounts). See this <a href="http://www2.hursley.ibm.com/decimal/decifaq1.html#inexact">IBM article</a> for a more <span class="blsp-spelling-corrected" id="SPELLING_ERROR_2">in depth</span> explanation and examples. The solution is to use types that work with powers of ten internally. In C#, you can use 'decimal' and in Java or Ruby, '<span class="blsp-spelling-error" id="SPELLING_ERROR_3">BigDecimal</span>', to avoid these problems.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com11tag:blogger.com,1999:blog-28462179.post-85563568654296666912007-01-12T11:51:00.000+11:002007-02-25T22:59:39.651+11:00AntiPattern: BusinessObjects in the driving seatWhen you have a rich domain model with a business object centric design, and a Windows forms GUI, it can be very tempting to start putting significant process logic in the business objects. After continuing along this path a little further, you may realise that the process needs some sort of user input, and you use events or some sort of notifier pattern to gain user input required by the process, while still maintaining layering in terms of referencing. Then additionally you may need to access some sort of external service.<br /><br />Here is an example:<pre>class Order : BusinessObject<br />{<br /> public void SendOrder(INotifier notifier)<br /> {<br /> if (ReadyForDelivery ||<br /> Confirm("Are you sure you want to send order lines?"<br /> + " They are not ready for delivery."))<br /> {<br /> OrderLine[] orders = GetLinesToSend();<br /> foreach(OrderLine line in Lines)<br /> {<br /> SendLine(line); // send line using a web service?<br /> }<br /><br /> Notify("Lines sent successfully.");<br /> }<br /> }<br />}<br /><br />interface INotifier<br />{<br /> void Notify(string msg);<br /> bool Confirm(string msg);<br /> OrderLine[] GetLinesToSend();<br />}<br /></pre>I would like to suggest that this is an anti-pattern and a trap. Although there is no direct reference from the Business Layer to the GUI layer (INotifier is implemented in GUI and passed down), the Business Layer now requires the ability to stay instantiated, pause while waiting for responses from the notifier, and then continue execution. This will work for rich client applications, but not in a stateless web environment. The ideal of being able to swap in/out the GUI layers on top of the Business layer is now compromised.<br /><br />Instead, it would be possible to drive form the GUI layer, and call a service to send the Order Lines. In pseudo code below:<pre>void SendMenu_Click(...)<br />{<br /> if (Order.ReadyForDelivery ||<br /> MessageBox.Show(...) == DialogResult.Yes)<br /> {<br /> using (ChooseLineForm chooseLineForm =<br /> new ChooseLineForm(Order))<br /> {<br /> chooseLineForm.ShowDialog()<br /> }<br /> SendingSevice.SendLines(chooseLineForm.selectedLines);<br /> ...<br /> }<br />}<br /></pre>If the logic in the GUI layer became much more complex, it may be a good idea to pull it out into its own class (eg, LineSender). This class would be a type of GUI level controller, responsible for orchestrating the send process.<br /><br />Using this approach, there are a number of benefits:<br /><ul><li>BusinessObjects have no reliance on GUI implementation, so can be used for Rich Client and Web Client indiscriminately.</li><li>Web developers are free to implement the user input process in stateless way more appropriate to their platform.<br /></li><li>Functionality for sending Order lines (some sort of integration with a web service?) is pulled out into a service class which can be reused elsewhere (potentially sending other types of objects?) and unclutters the Order business object and removes its dependency on an external service.</li><li>Code is simpler and easier to follow.</li></ul>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com24tag:blogger.com,1999:blog-28462179.post-53460954012389009602007-01-11T09:56:00.000+11:002007-01-11T09:59:32.191+11:00In Defense of Simplicity AND Complexity<a href="http://www.scottberkun.com/blog/?p=502">Simplicity</a> and <a href="http://www.joelonsoftware.com/items/2006/12/09.html">complexity</a> seem to have become hot topics for some of my favourite technical bloggers of late. These fine people have taken the view that things should be either simple or complex. Right, seems logical, these are opposites. However, I would like to suggest that in a well designed appliance which addresses a complex process, it should have both a simple AND a complex interface.<br /><br />A couple of years ago, I bought a LG "Fuzzy Logic" washing machine. It has lots of buttons and settings on the front and one big button that says something like "Start". 97% of the time, I throw in my washing, some detergent and press the big start button, and the washing machine works out all the settings, displays them and then starts. In the 3% of the time when I want to do something different (eg, just a rinse), I use the more complex part of the interface to change the 'cycle' settings.<br /><br />Recently, I bought an IXUS 65. It's a lovely digital camera, and it provides both a complex and a simple interface. As soon as I put in the battery, I was able to take pretty nice pictures by just clicking the big button on the top. No problem, I was very happy. Over the next few days, I glanced through the manual and fiddled with more complex settings for ISO, colour etc. However, in 95% of shots, I simply want to click the one big button that takes a nice auto-everything picture. It's only occasionally that I want to change the settings to achieve a particular effect or to override a mistake in the auto settings.<br /><br />To conclude, I think that my talented fellow bloggers are all right. People like complex interfaces and simple interfaces, just at different times, for different tasks. The best gadgets and appliances offer both.James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com42tag:blogger.com,1999:blog-28462179.post-11376245195461986242007-01-06T13:27:00.000+11:002007-01-06T13:34:01.552+11:00Pandora - Streaming musicI'd like to point my readers to <a href="http://www.pandora.com">Pandora</a>, a streaming music service which lets you define multiple channels for the different types of music that you like. You start each channel with a seed, which is a song or <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_0">artist</span> that you like. The service <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_1">chooses</span> songs similar to this and you then mark them as thumbs up or thumbs down, thus refining the channel more towards your musical taste. It is Flash and browser based, no downloads necessary. Sign up is easy, and the service is free, though add supported. Highly recommended!James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com17tag:blogger.com,1999:blog-28462179.post-66347922075005870582006-12-28T12:45:00.000+11:002006-12-28T13:20:35.165+11:00Windows Crash: multiple_irp_complete_requests Stop: 0x00000044Thought I'd do a bit of a <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_0">defrag</span> on my old Windows 2000 box. A few minutes in, I got a blue screen:<br /><br />multiple_<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1">irp</span>_complete_requests<br />stop: 0x00000044 (0x852<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_2">CCE</span>68, 0x00000D39, 0x00000000, 0x00000000)<br /><br />Tried a <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_3">chkdsk</span> but got the same error, even when running it on boot in console mode.<br /><br />After some web trawling, found this <a href="http://answers.google.com/answers/threadview?id=552945"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_4">google</span> answer</a>, which suggested that the problem was caused by 'Intel Application Accelerator' conflicting with recent service packs.<br /><br />After <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_5">uninstalling</span> the 'Intel Application Accelerator' my <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_6">chkdsk</span> finished successfully and <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_7">defrag</span> seems to be going fine.<br /><br />Thank goodness for the <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_8">internet</span> and its wealth of technical solutions!James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com32tag:blogger.com,1999:blog-28462179.post-2565370712538066712006-12-26T17:03:00.000+11:002006-12-26T17:45:20.058+11:00Bye Bye EDI... Hello ThoughtWorksWell, after almost four years at <a href="http://www.edi.com.au/">EDI</a> (now called <a href="http://www.cargowise.com/"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_0">CargoWise</span> <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1">edi</span></a>) I am leaving. I finish in the middle of January next year. It has been an interesting time, and I have learnt a lot working with very talented people and from building the framework for a <a href="http://www.cargowise.com/solutions/enterprise-overview.shtml">big solution suite</a> (around 4 million lines of C# code). I've also had the opportunity to experience the very different joys and pitfalls of product management.<br /><br />I will be starting at <a href="http://www.thoughtworks.com/"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_2">ThoughtWorks</span></a> (of <a href="http://nunit.org/"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_3">NUnit</span></a>, <a href="http://jim.webber.name/">Jim Webber</a> and <a href="http://www.martinfowler.com/">Martin Fowler</a> fame) in the middle of February in the new year. I'm expecting that there will be a lot of new exciting stuff to learn, and a lot of variety in terms of clients and technologies. <a href="http://www.thoughtworks.com/"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_4">ThoughtWorks</span></a> are strongly <a href="http://en.wikipedia.org/wiki/Extreme_Programming"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_5">XP</span></a>, do a lot of development on client sites and even have some <a href="http://www.rubyonrails.org/">Rails</a> projects. The people I have met from <a href="http://www.thoughtworks.com/"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_6">ThoughtWorks</span></a> have all been very friendly and I look <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_7">forward</span> to starting there soon :-)James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com22tag:blogger.com,1999:blog-28462179.post-84658118403531493422006-11-30T22:31:00.000+11:002007-03-29T18:58:00.935+10:00Thai Ordering and Development Mode vs Production Mode for Rails AppsA while back, I wrote a little rails app for Thai food ordering at my work. My colleagues place orders using the system and then bring money to the nominated <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_0">orderer</span> of the week. Once all orders are in and paid for (this is also tracked in the app), the <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1">orderer</span> rings up our favourite Thai restaurant (<a href="http://www.laddas.com.au/"><span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_2">Laddas</span></a>) and places the order.<br /><br />I have the app running from fairly cheap shared hosting. At peak times during the ordering, I guess that they'd be 15 or so people simultaneously using the app. We've used it many times without problems. Thus, I was quite surprised and displeased (as were my colleagues), when my hosting account was suspended and we couldn't see what had been ordered this morning. A hasty email to my hosting provider revealed that my account had been suspended due to high load and "ruby flooding". They were kind enough to <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_4">un</span>-suspend my account and we completed the ordering process.<br /><br />I remembered seeing something about production mode in 'environment.<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_5">rb</span>'. Some googling confirmed my hunch - in development mode, rails apps are much more resource intensive. Caching is not used, and every single file needs to be reloaded every time it is required. After changing my app to production mode, it seemed to run <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_6">noticeably</span> faster. <a href="http://here.the.ycros.be/">Michael</a> and I ran 'top' and it looked like each request used less CPU.<br /><br />So, should you be in a similar situation, this is how to change your app to production mode on fast-<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_7">cgi</span> Apache shared hosting:<br /><ol><li>Confirm that 'database.<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_8">yml</span>' in your <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_9">app's</span> '<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_10">config</span>' directory has a section for production mode, and that it has up to date database connection details.<br /></li><li>Edit 'environment.<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_11">rb</span>' in your <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_12">app's</span> '<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_13">config</span>' directory.</li><li>Add this line:<br /><pre> <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_14">ENV</span>['RAILS_<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_15">ENV</span>'] ||= 'production'</pre></li><li>Run '<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_16">ps</span> -u [your_user_name]' to find if you have any 'dispatch.<span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_18">fgi</span>' processes running.</li><li>If so, kill all of them (they'll restart and use your new <span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_19">config</span> settings).</li><li>Browse to your app, it should now run faster.<br /></li></ol>James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com20tag:blogger.com,1999:blog-28462179.post-1157896880166539642006-09-10T23:43:00.000+10:002006-11-25T17:29:00.338+11:00Rails and the initialize() methodI spent a bit of time this weekend on a pet rails project. I came across a strange error when trying to create new records through the application. Editing was working just fine, but creating a new record just seemed to hang.<br /><br />Breakpoints came to my rescue. In Ruby, they're really handy. You can put a 'breakpoint' call anywhere in your code, and if you have a breakpointer process running (start this with the command 'ruby script/breakpointer'), you jump straight into an interactive ruby console debug session when the breakpoint is hit.<br /><br />Using the development log and a few breakpoints, I found the that the error was:<br /><pre>ArgumentError: wrong number of arguments (1 for 0)</pre>And that it was caused by a line similar to this:<br /><pre>@order = order.new(params[:order])</pre>Ie, the controller was creating a new order from the post parameters.<br /><br />Then all became clear - some time earlier, I'd overridden the initialize() method in the order to default some dates. My code was similar to this:<br /><pre>def initialize<br />super()<br /><br />if (@new_record)<br /> self.validFrom = Date.today<br /> self.validTo = 1.year.from_now.to_date<br />end<br />end<br /></pre><br />Great for creating new objects in my tests and console sessions where I always just created the object and then set properties. However, the controller was relying on passing in the post parameters in the constructor.<br /><br />The solution is to accept any number of params and pass these to the base class:<br /><pre>def initialize(*params)<br />super(*params)<br /><br />if (@new_record)<br /> self.validFrom = Date.today<br /> self.validTo = 1.year.from_now.to_date<br />end<br />end<br /></pre><br />That way, the order can accept the post parameters in its constructor.<br /><br />I'm very pleased to say that I can now create new records again!James Crisphttp://www.blogger.com/profile/00769345740252337538noreply@blogger.com33