mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-03-25 09:05:47 +00:00
769 lines
No EOL
49 KiB
HTML
769 lines
No EOL
49 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<!-- saved from url=(0121)http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails -->
|
|
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
<title>Obligatory Post about Ruby on Rails - samhuri.net</title>
|
|
|
|
<meta content="48.472,-123.3683" name="ICBM">
|
|
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://sami.samhuri.net/xml/rsd">
|
|
<link rel="alternate" type="application/rss+xml" title="RSS" href="http://sami.samhuri.net/xml/rss20/article/15/feed.xml">
|
|
<link rel="alternate" type="application/atom+xml" title="Atom" href="http://sami.samhuri.net/xml/atom10/article/15/feed.xml">
|
|
<script src="./Obligatory Post about Ruby on Rails - samhuri.net_files/cookies.js" type="text/javascript"></script>
|
|
<script src="./Obligatory Post about Ruby on Rails - samhuri.net_files/prototype.js" type="text/javascript"></script>
|
|
<script src="./Obligatory Post about Ruby on Rails - samhuri.net_files/effects.js" type="text/javascript"></script>
|
|
<script src="./Obligatory Post about Ruby on Rails - samhuri.net_files/typo.js" type="text/javascript"></script>
|
|
|
|
<script type="text/javascript"></script>
|
|
<link href="./Obligatory Post about Ruby on Rails - samhuri.net_files/island.css" media="all" rel="Stylesheet" type="text/css">
|
|
<link href="./Obligatory Post about Ruby on Rails - samhuri.net_files/user-styles.css" media="all" rel="Stylesheet" type="text/css">
|
|
<link href="./Obligatory Post about Ruby on Rails - samhuri.net_files/print.css" media="print" rel="Stylesheet" type="text/css">
|
|
<style type="text/css" style="display: none !important;">object:not([type]),object[classid$=":D27CDB6E-AE6D-11cf-96B8-444553540000"],object[classid$=":d27cdb6e-ae6d-11cf-96b8-444553540000"],object[codebase*="swflash.cab"],object[data*=".swf"],object[type="application/x-shockwave-flash"],object[src*=".swf"],object[codetype="application/x-shockwave-flash"],embed[type="application/x-shockwave-flash"],embed[src*=".swf"],embed[allowscriptaccess],embed[flashvars],embed[wmode],object[classid$=":166B1BCA-3F9C-11CF-8075-444553540000"],object[codebase*="sw.cab"],object[data*=".dcr"],object[type="application/x-director"],object[src*=".dcr"],embed[type="application/x-director"],embed[src*=".dcr"],object[classid$=":15B782AF-55D8-11D1-B477-006097098764"],object[codebase*="awswaxf.cab"],object[data*=".aam"],object[type="application/x-authorware-map"],object[src*=".aam"],embed[type="application/x-authorware-map"],embed[src*=".aam"],object[classid$="32C73088-76AE-40F7-AC40-81F62CB2C1DA"],object[type="application/ag-plugin"],object[type="application/x-silverlight"],object[type="application/x-silverlight-2"],object[source*=".xaml"],object[sourceelement*="xaml"],embed[type="application/ag-plugin"],embed[source*=".xaml"]{display: none !important;}</style></head>
|
|
|
|
<body><div id="wm-ipp" style="position: relative; padding-top: 0px; padding-right: 5px; padding-bottom: 0px; padding-left: 5px; min-height: 70px; min-width: 800px; z-index: 9000; display: block; ">
|
|
<div id="wm-ipp-inside" style="position:fixed;padding:0!important;margin:0!important;width:97%;min-width:780px;border:5px solid #000;border-top:none;background-image:url(http://staticweb.archive.org/images/toolbar/wm_tb_bk_trns.png);text-align:center;-moz-box-shadow:1px 1px 3px #333;-webkit-box-shadow:1px 1px 3px #333;box-shadow:1px 1px 3px #333;font-size:11px!important;font-family:'Lucida Grande','Arial',sans-serif!important;">
|
|
<table style="border-collapse:collapse;margin:0;padding:0;width:100%;"><tbody><tr>
|
|
<td style="padding:10px;vertical-align:top;min-width:110px;">
|
|
<a href="http://wayback.archive.org/web/" title="Wayback Machine home page" style="background-color:transparent;border:none;"><img src="./Obligatory Post about Ruby on Rails - samhuri.net_files/wayback-toolbar-logo.png" alt="Wayback Machine" width="110" height="39" border="0"></a>
|
|
</td>
|
|
<td style="padding:0!important;text-align:center;vertical-align:top;width:100%;">
|
|
|
|
<table style="border-collapse:collapse;margin:0 auto;padding:0;width:570px;"><tbody><tr>
|
|
<td style="padding:3px 0;" colspan="2">
|
|
<form target="_top" method="get" action="http://wayback.archive.org/web/form-submit.jsp" name="wmtb" id="wmtb" style="margin:0!important;padding:0!important;"><input type="text" name="url" id="wmtbURL" value="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails" style="width:400px;font-size:11px;font-family:'Lucida Grande','Arial',sans-serif;" onfocus="javascript:this.focus();this.select();"><input type="hidden" name="type" value="replay"><input type="hidden" name="date" value="20070316171839"><input type="submit" value="Go" style="font-size:11px;font-family:'Lucida Grande','Arial',sans-serif;margin-left:5px;"><span id="wm_tb_options" style="display:block;"></span></form>
|
|
</td>
|
|
<td style="vertical-align:bottom;padding:5px 0 0 0!important;" rowspan="2">
|
|
<table style="border-collapse:collapse;width:110px;color:#99a;font-family:'Helvetica','Lucida Grande','Arial',sans-serif;"><tbody>
|
|
|
|
<!-- NEXT/PREV MONTH NAV AND MONTH INDICATOR -->
|
|
<tr style="width:110px;height:16px;font-size:10px!important;">
|
|
<td style="padding-right:9px;font-size:11px!important;font-weight:bold;text-transform:uppercase;text-align:right;white-space:nowrap;overflow:visible;" nowrap="nowrap">
|
|
|
|
Feb
|
|
|
|
</td>
|
|
<td id="displayMonthEl" style="background:#000;color:#ff0;font-size:11px!important;font-weight:bold;text-transform:uppercase;width:34px;height:15px;padding-top:1px;text-align:center;" title="You are here: 17:18:39 Mar 16, 2007">MAR</td>
|
|
<td style="padding-left:9px;font-size:11px!important;font-weight:bold;text-transform:uppercase;white-space:nowrap;overflow:visible;" nowrap="nowrap">
|
|
|
|
Apr
|
|
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- NEXT/PREV CAPTURE NAV AND DAY OF MONTH INDICATOR -->
|
|
<tr>
|
|
<td style="padding-right:9px;white-space:nowrap;overflow:visible;text-align:right!important;vertical-align:middle!important;" nowrap="nowrap">
|
|
|
|
<img src="./Obligatory Post about Ruby on Rails - samhuri.net_files/wm_tb_prv_off.png" alt="Previous capture" width="14" height="16" border="0">
|
|
|
|
</td>
|
|
<td id="displayDayEl" style="background:#000;color:#ff0;width:34px;height:24px;padding:2px 0 0 0;text-align:center;font-size:24px;font-weight: bold;" title="You are here: 17:18:39 Mar 16, 2007">16</td>
|
|
<td style="padding-left:9px;white-space:nowrap;overflow:visible;text-align:left!important;vertical-align:middle!important;" nowrap="nowrap">
|
|
|
|
<img src="./Obligatory Post about Ruby on Rails - samhuri.net_files/wm_tb_nxt_off.png" alt="Next capture" width="14" height="16" border="0">
|
|
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- NEXT/PREV YEAR NAV AND YEAR INDICATOR -->
|
|
<tr style="width:110px;height:13px;font-size:9px!important;">
|
|
<td style="padding-right:9px;font-size:11px!important;font-weight: bold;text-align:right;white-space:nowrap;overflow:visible;" nowrap="nowrap">
|
|
|
|
2006
|
|
|
|
</td>
|
|
<td id="displayYearEl" style="background:#000;color:#ff0;font-size:11px!important;font-weight: bold;padding-top:1px;width:34px;height:13px;text-align:center;" title="You are here: 17:18:39 Mar 16, 2007">2007</td>
|
|
<td style="padding-left:9px;font-size:11px!important;font-weight: bold;white-space:nowrap;overflow:visible;" nowrap="nowrap">
|
|
|
|
2008
|
|
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
</td>
|
|
|
|
</tr>
|
|
<tr>
|
|
<td style="vertical-align:middle;padding:0!important;">
|
|
<a href="http://wayback.archive.org/web/20070316171839*/http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails" style="color:#33f;font-size:11px;font-weight:bold;background-color:transparent;border:none;" title="See a list of every capture for this URL"><strong>1 captures</strong></a>
|
|
<div style="margin:0!important;padding:0!important;color:#666;font-size:9px;padding-top:2px!important;white-space:nowrap;" title="Timespan for captures of this URL">16 Mar 07 - 16 Mar 07</div>
|
|
</td>
|
|
<td style="padding:0!important;">
|
|
<a style="position:relative; white-space:nowrap; width:400px;height:27px;" href="" id="wm-graph-anchor">
|
|
<div id="wm-ipp-sparkline" style="position:relative; white-space:nowrap; width:400px;height:27px;background-color:#fff;cursor:pointer;border-right:1px solid #ccc;" title="Explore captures for this URL">
|
|
<img id="sparklineImgId" style="position:absolute; z-index:9012; top:0px; left:0px;" onmouseover="showTrackers('inline');" onmouseout="showTrackers('none');" onmousemove="trackMouseMove(event,this)" alt="sparklines" width="400" height="27" border="0" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/graph.jsp">
|
|
<img id="wbMouseTrackYearImg" style="display:none; position:absolute; z-index:9010;" width="25" height="27" border="0" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/transp-yellow-pixel.png">
|
|
<img id="wbMouseTrackMonthImg" style="display:none; position:absolute; z-index:9011; " width="2" height="27" border="0" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/transp-red-pixel.png">
|
|
</div>
|
|
</a>
|
|
|
|
</td>
|
|
</tr></tbody></table>
|
|
</td>
|
|
<td style="text-align:right;padding:5px;width:65px;font-size:11px!important;">
|
|
<a href="javascript:;" onclick="document.getElementById('wm-ipp').style.display='none';" style="display:block;padding-right:18px;background:url(http://staticweb.archive.org/images/toolbar/wm_tb_close.png) no-repeat 100% 0;color:#33f;font-family:'Lucida Grande','Arial',sans-serif;margin-bottom:23px;background-color:transparent;border:none;" title="Close the toolbar">Close</a>
|
|
<a href="http://faq.web.archive.org/" style="display:block;padding-right:18px;background:url(http://staticweb.archive.org/images/toolbar/wm_tb_help.png) no-repeat 100% 0;color:#33f;font-family:'Lucida Grande','Arial',sans-serif;background-color:transparent;border:none;" title="Get some help using the Wayback Machine">Help</a>
|
|
</td>
|
|
</tr></tbody></table>
|
|
|
|
</div>
|
|
</div>
|
|
<!-- BEGIN WAYBACK TOOLBAR INSERT -->
|
|
|
|
<script type="text/javascript" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/disclaim-element.js"></script>
|
|
<script type="text/javascript" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/graph-calc.js"></script>
|
|
<script type="text/javascript" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/jquery.min.js"></script>
|
|
<script type="text/javascript">
|
|
//<![CDATA[
|
|
var firstDate = 820454400000;
|
|
var lastDate = 1325375999999;
|
|
var wbPrefix = "http://web.archive.org/web/";
|
|
var wbCurrentUrl = "http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails";
|
|
|
|
var curYear = -1;
|
|
var curMonth = -1;
|
|
var yearCount = 16;
|
|
var firstYear = 1996;
|
|
var imgWidth=400;
|
|
var yearImgWidth = 25;
|
|
var monthImgWidth = 2;
|
|
var trackerVal = "none";
|
|
var displayDay = "16";
|
|
var displayMonth = "Mar";
|
|
var displayYear = "2007";
|
|
var prettyMonths = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
|
|
|
|
function showTrackers(val) {
|
|
if(val == trackerVal) {
|
|
return;
|
|
}
|
|
if(val == "inline") {
|
|
document.getElementById("displayYearEl").style.color = "#ec008c";
|
|
document.getElementById("displayMonthEl").style.color = "#ec008c";
|
|
document.getElementById("displayDayEl").style.color = "#ec008c";
|
|
} else {
|
|
document.getElementById("displayYearEl").innerHTML = displayYear;
|
|
document.getElementById("displayYearEl").style.color = "#ff0";
|
|
document.getElementById("displayMonthEl").innerHTML = displayMonth;
|
|
document.getElementById("displayMonthEl").style.color = "#ff0";
|
|
document.getElementById("displayDayEl").innerHTML = displayDay;
|
|
document.getElementById("displayDayEl").style.color = "#ff0";
|
|
}
|
|
document.getElementById("wbMouseTrackYearImg").style.display = val;
|
|
document.getElementById("wbMouseTrackMonthImg").style.display = val;
|
|
trackerVal = val;
|
|
}
|
|
function getElementX2(obj) {
|
|
var thing = jQuery(obj);
|
|
if((thing == undefined)
|
|
|| (typeof thing == "undefined")
|
|
|| (typeof thing.offset == "undefined")) {
|
|
return getElementX(obj);
|
|
}
|
|
return Math.round(thing.offset().left);
|
|
}
|
|
function trackMouseMove(event,element) {
|
|
|
|
var eventX = getEventX(event);
|
|
var elementX = getElementX2(element);
|
|
var xOff = eventX - elementX;
|
|
if(xOff < 0) {
|
|
xOff = 0;
|
|
} else if(xOff > imgWidth) {
|
|
xOff = imgWidth;
|
|
}
|
|
var monthOff = xOff % yearImgWidth;
|
|
|
|
var year = Math.floor(xOff / yearImgWidth);
|
|
var yearStart = year * yearImgWidth;
|
|
var monthOfYear = Math.floor(monthOff / monthImgWidth);
|
|
if(monthOfYear > 11) {
|
|
monthOfYear = 11;
|
|
}
|
|
// 1 extra border pixel at the left edge of the year:
|
|
var month = (year * 12) + monthOfYear;
|
|
var day = 1;
|
|
if(monthOff % 2 == 1) {
|
|
day = 15;
|
|
}
|
|
var dateString =
|
|
zeroPad(year + firstYear) +
|
|
zeroPad(monthOfYear+1,2) +
|
|
zeroPad(day,2) + "000000";
|
|
|
|
var monthString = prettyMonths[monthOfYear];
|
|
document.getElementById("displayYearEl").innerHTML = year + 1996;
|
|
document.getElementById("displayMonthEl").innerHTML = monthString;
|
|
// looks too jarring when it changes..
|
|
//document.getElementById("displayDayEl").innerHTML = zeroPad(day,2);
|
|
|
|
var url = wbPrefix + dateString + '/' + wbCurrentUrl;
|
|
document.getElementById('wm-graph-anchor').href = url;
|
|
|
|
//document.getElementById("wmtbURL").value="evX("+eventX+") elX("+elementX+") xO("+xOff+") y("+year+") m("+month+") monthOff("+monthOff+") DS("+dateString+") Moy("+monthOfYear+") ms("+monthString+")";
|
|
if(curYear != year) {
|
|
var yrOff = year * yearImgWidth;
|
|
document.getElementById("wbMouseTrackYearImg").style.left = yrOff + "px";
|
|
curYear = year;
|
|
}
|
|
if(curMonth != month) {
|
|
var mtOff = year + (month * monthImgWidth) + 1;
|
|
document.getElementById("wbMouseTrackMonthImg").style.left = mtOff + "px";
|
|
curMonth = month;
|
|
}
|
|
}
|
|
//]]>
|
|
</script>
|
|
|
|
<style type="text/css">body{margin-top:0!important;padding-top:0!important;min-width:800px!important;}#wm-ipp a:hover{text-decoration:underline!important;}</style>
|
|
|
|
<script type="text/javascript">
|
|
var wmDisclaimBanner = document.getElementById("wm-ipp");
|
|
if(wmDisclaimBanner != null) {
|
|
disclaimElement(wmDisclaimBanner);
|
|
}
|
|
</script>
|
|
<!-- END WAYBACK TOOLBAR INSERT -->
|
|
|
|
|
|
<div id="content" class="clearfix">
|
|
|
|
<div id="main">
|
|
<!--
|
|
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
|
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
<rdf:Description
|
|
rdf:about=""
|
|
trackback:ping="http://sami.samhuri.net/articles/trackback/15"
|
|
dc:title="Obligatory Post about Ruby on Rails"
|
|
dc:identifier="http://sami.samhuri.net/articles/read/15"
|
|
dc:description="I&#8217;m a Rails newbie and eager to learn. I welcome any suggestions or criticism you have. You can direct them to my inbox or leave me a comment below. I finally set myself up with a blog. I mailed"
|
|
dc:creator="sjs"
|
|
dc:date="2007-03-06T19:53:43-08:00" />
|
|
</rdf:RDF>
|
|
-->
|
|
|
|
<div class="post" onmouseover="if (getCookie('is_admin') == 'yes') { Element.show('admin_article'); }" onmouseout="Element.hide('admin_article');">
|
|
<a href="http://sami.samhuri.net/admin/content/edit/15" class="admintools" id="admin_article" style="display: none">edit</a>
|
|
<h2>Obligatory Post about Ruby on Rails</h2>
|
|
<p class="auth"><!-- Posted by <a href="mailto:sjs@uvic.ca">Sami Jensen Samhuri</a> -->
|
|
<span class="typo_date" title="Mon, 20 Feb 2006 08:31:00 GMT">on Monday, February 20, 2006</span></p>
|
|
<p><em>I'm a Rails newbie and eager to learn. I welcome any suggestions or criticism you have. You can direct them to <a href="mailto:sjs@uvic.ca">my inbox</a> or leave me a comment below.</em></p>
|
|
|
|
|
|
<p>I finally set myself up with a blog. I mailed my dad the address and mentioned that it was running <a href="http://www.typosphere.org/">Typo</a>, which is written in <a href="http://www.rubyonrails.com/">Ruby on Rails</a>. The fact that it is written in Rails was a big factor in my decision. I am currently reading <a href="http://www.pragmaticprogrammer.com/titles/rails/">Agile Web Development With Rails</a> and it will be great to use Typo as a learning tool, since I will be modifying my blog anyways regardless of what language it's written in.</p>
|
|
|
|
|
|
<p>Clearly Rails made an impression on me somehow or I wouldn't be investing this time on it. But my dad asked me a very good question:</p>
|
|
|
|
|
|
<blockquote>
|
|
<p style="border-left: 3px solid #fff; padding-left: 5px;;">Rails? What is so special about it? I looked at your page and it looks pretty normal to me. I miss the point of this new Rails technique for web development.</p>
|
|
</blockquote>
|
|
|
|
|
|
<p>It's unlikely that he was surprised at my lengthy response, but I was. I have been known to write him long messages on topics that interest me. However, I've only been learning Rails for two weeks or so. Could I possibly have so much to say about it already? Apparently I do.</p><h2>Ruby on Rails background</h2>
|
|
|
|
|
|
<p>I assume a pretty basic knowledge of what Rails is, so if you're not familiar with it now's a good time to read something on the official <a href="http://www.rubyonrails.com/">Rails website</a> and watch the infamous <a href="http://www.rubyonrails.com/screencasts">15-minute screencast</a>, where Rails creator, <a href="http://www.loudthinking.com/">David Heinemeier Hansson</a>, creates a simple blog application.</p>
|
|
|
|
|
|
<p>The screencasts are what sparked my curiosity, but they hardly scratch the surface of Rails. After that I spent hours reading whatever I could find about Rails before deciding to take the time to learn it well. As a result, a lot of what you read here will sound familiar if you've read other blogs and articles about Rails. This post wasn't planned so there's no list of references yet. I hope to add some links though so please contact me if any ideas or paraphrasing here is from your site, or if you know who I should give credit to.</p>
|
|
|
|
|
|
<h2>Rails through my eyes</h2>
|
|
|
|
|
|
<p><img src="./Obligatory Post about Ruby on Rails - samhuri.net_files/tools.jpg" align="right" alt="Black & Decker tools" style="margin: 5px;"> Rails is like my Black & Decker toolkit. I have a hammer, power screwdriver, tape measure, needle-nose pliers, wire cutters, a level, etc. This is exactly what I need—no more, no less. It helps me get things done quickly and easily that would otherwise be painful and somewhat difficult. I can pick up the tools and use them without much training. Therefore I am instantly productive with them.</p>
|
|
|
|
|
|
<p>The kit is suitable for many people who need these things at home, such as myself. Companies build skyscrapers and huge malls and apartments, and they clearly need more powerful tools than I. There are others that just need to drive in a nail to hang a picture, in which case the kit I have is overkill. They're better off just buying and using a single hammer. I happen to fall in the big grey middle <a href="http://poignantguide.net/ruby/chapter-3.html#section2">chunk</a>, not the other two.</p>
|
|
|
|
|
|
<p>I'm a university student. I code because it's satisfying and fun to create software. I do plan on coding for a living when I graduate. I don't work with ancient databases, or create monster sites like Amazon, Google, or Ebay. The last time I started coding a website from scratch I was using <a href="http://www.php.net/">PHP</a>, that was around the turn of the millennium. [It was a fan site for a <a href="http://www.nofx.org/">favourite band</a> of mine.]</p>
|
|
|
|
|
|
<p>After a year or so I realized I didn't have the time to do it properly (ie. securely and cleanly) if I wanted it to be done relatively soon. A slightly customized <a href="http://www.mediawiki.org/wiki/MediaWiki">MediaWiki</a> promptly took it's place. It did all that I needed quite well, just in a less specific way.</p>
|
|
|
|
|
|
<p>The wiki is serving my site extremely well, but there's still that itch to create my <strong>own</strong> site. I feel if Rails was around back then I may have been able to complete the project in a timely manner. I was also frustrated with PHP. Part of that is likely due to a lack of experience and of formal programming education at that time, but it was still not fun for me. It wasn't until I started learning Rails that I thought "<em>hey, I could create that site pretty quickly using this!</em>"</p>
|
|
|
|
|
|
<p>Rails fits my needs like a glove, and this is where it shines. Many professionals are making money creating sites in Rails, so I'm not trying to say it's for amateurs only or something equally silly.</p>
|
|
|
|
|
|
<h2>Web Frameworks and iPods?</h2>
|
|
|
|
|
|
<p>Some might say I have merely been swept up in hype and am following the herd. You may be right, and that's okay. I'm going to tell you a story. There was a guy who didn't get one of the oh-so-shiny iPods for a long time, though they looked neat. His discman plays mp3 CDs, and that was good enough for him. The latest iPod, which plays video, was sufficiently cool enough for him to forget that <strong>everyone</strong> at his school has an iPod and he would be trendy just like them now.</p>
|
|
|
|
|
|
<p>Shocker ending: he is I, and I am him. Now I know why everyone has one of those shiny devices. iPods and web frameworks have little in common except that many believe both the iPod and Rails are all hype and flash. I've realized that something creating this kind of buzz may actually just be a good product. I feel that this is the only other thing the iPod and Rails have in common: they are both <strong>damn good</strong>. Enough about the iPod, everyone hates hearing about it. My goal is to write about the other thing everyone is tired of hearing about.</p>
|
|
|
|
|
|
<h2>Why is Rails special?</h2>
|
|
|
|
|
|
<p><img src="./Obligatory Post about Ruby on Rails - samhuri.net_files/rails.png" align="right" alt="Ruby on Rails" style="margin: 5px;"> <strong>Rails is not magic.</strong> There are no exclusive JavaScript libraries or HTML tags. We all have to produce pages that render in the same web browsers. My dad was correct, there <em>is</em> nothing special about my website either. It's more or less a stock Typo website.</p>
|
|
|
|
|
|
<p>So what makes developing with Rails different? For me there are four big things that set Rails apart from the alternatives:</p>
|
|
|
|
|
|
<ol>
|
|
<li>Separating data, function, and design</li>
|
|
<li>Readability (which is underrated) </li>
|
|
<li>Database migrations</li>
|
|
<li>Testing is so easy it hurts</li>
|
|
</ol>
|
|
|
|
|
|
<h3>MVC 101 <em>(or, Separating data, function, and design)</em></h3>
|
|
|
|
|
|
<p>Now I'm sure you've heard about separating content from design. Rails takes that one step further from just using CSS to style your website. It uses what's known as the MVC paradigm: <strong>Model-View-Controller</strong>. This is a tried and tested development method. I'd used MVC before in Cocoa programming on Mac OS X, so I was already sold on this point.</p>
|
|
|
|
|
|
<ul>
|
|
<li>The model deals with your data. If you're creating an online store you have a product model, a shopping cart model, a customer model, etc. The model takes care of storing this data in the database (persistence), and presenting it to you as an object you can manipulate at runtime.</li>
|
|
</ul>
|
|
|
|
|
|
<ul>
|
|
<li>The view deals <em>only</em> with presentation. That's it, honestly. An interface to your app.</li>
|
|
</ul>
|
|
|
|
|
|
<ul>
|
|
<li>The controller binds the model to the view, so that when the user clicks on the <strong>Add to cart</strong> link the controller is wired to call the <code>add_product</code> method of the cart model and tell it which product to add. Then the controller takes the appropriate action such as redirecting the user to the shopping cart view.</li>
|
|
</ul>
|
|
|
|
|
|
<p>Of course this is not exclusive to Rails, but it's an integral part of it's design.</p>
|
|
|
|
|
|
<h3>Readability</h3>
|
|
|
|
|
|
<p>Rails, and <a href="http://www.ruby-lang.org/">Ruby</a>, both read amazingly like spoken English. This code is more or less straight out of Typo. You define relationships between objects like this:</p>
|
|
|
|
|
|
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Article</span> <span class="punct"><</span> <span class="constant">Content</span>
|
|
<span class="ident">has_many</span> <span class="symbol">:comments</span><span class="punct">,</span> <span class="symbol">:dependent</span> <span class="punct">=></span> <span class="constant">true</span><span class="punct">,</span> <span class="symbol">:order</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">created_at ASC</span><span class="punct">"</span>
|
|
<span class="ident">has_many</span> <span class="symbol">:trackbacks</span><span class="punct">,</span> <span class="symbol">:dependent</span> <span class="punct">=></span> <span class="constant">true</span><span class="punct">,</span> <span class="symbol">:order</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">created_at ASC</span><span class="punct">"</span>
|
|
<span class="ident">has_and_belongs_to_many</span> <span class="symbol">:categories</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=></span> <span class="punct">'</span><span class="string">article_id</span><span class="punct">'</span>
|
|
<span class="ident">has_and_belongs_to_many</span> <span class="symbol">:tags</span><span class="punct">,</span> <span class="symbol">:foreign_key</span> <span class="punct">=></span> <span class="punct">'</span><span class="string">article_id</span><span class="punct">'</span>
|
|
<span class="ident">belongs_to</span> <span class="symbol">:user</span>
|
|
<span class="punct">...</span></code></pre></div>
|
|
|
|
<p><code>dependent => true</code> means <em>if an article is deleted, it's comments go with it</em>. Don't worry if you don't understand it all, this is just for you to see some actual Rails code.</p>
|
|
|
|
|
|
<p>In the Comment model you have:</p>
|
|
|
|
|
|
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Comment</span> <span class="punct"><</span> <span class="constant">Content</span>
|
|
<span class="ident">belongs_to</span> <span class="symbol">:article</span>
|
|
<span class="ident">belongs_to</span> <span class="symbol">:user</span>
|
|
|
|
<span class="ident">validates_presence_of</span> <span class="symbol">:author</span><span class="punct">,</span> <span class="symbol">:body</span>
|
|
<span class="ident">validates_against_spamdb</span> <span class="symbol">:body</span><span class="punct">,</span> <span class="symbol">:url</span><span class="punct">,</span> <span class="symbol">:ip</span>
|
|
<span class="ident">validates_age_of</span> <span class="symbol">:article_id</span>
|
|
<span class="punct">...</span></code></pre></div>
|
|
|
|
<p>(I snuck in some validations as well)</p>
|
|
|
|
|
|
<p>But look how it reads! Read it out loud. I'd bet that my mom would more or less follow this, and she's anything but a programmer. That's not to say programming should be easy for grandma, <strong>but code should be easily understood by humans</strong>. Let the computer understand things that are natural for me to type, since we're making it understand a common language anyways.</p>
|
|
|
|
|
|
<p>Ruby and Ruby on Rails allow and encourage you to write beautiful code. That is so much more important than you may realize, because it leads to many other virtues. Readability is obvious, and hence maintainability. You must read code to understand and modify it. Oh, and happy programmers will be more productive than frustrated programmers.</p>
|
|
|
|
|
|
<h3 id="migrations">Database Migrations</h3>
|
|
|
|
|
|
<p>Here's one more life-saver: migrations. Migrations are a way to version your database schema from within Rails. So you have a table, call it <code>albums</code>, and you want to add the date the album was released. You could modify the database directly, but that's not fun. Even if you only have one server, all your configuration will be in one central place, the app. And Rails doesn't care if you have PostgreSQL, MySQL, or SQLite behind it. You can develop and test on SQLite and deploy on MySQL and the migrations will just work in both environments.</p>
|
|
|
|
|
|
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">AddDateReleased</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Migration</span>
|
|
<span class="keyword">def </span><span class="method">self.up</span>
|
|
<span class="ident">add_column</span> <span class="punct">"</span><span class="string">albums</span><span class="punct">",</span> <span class="punct">"</span><span class="string">date_released</span><span class="punct">",</span> <span class="symbol">:datetime</span>
|
|
<span class="constant">Albums</span><span class="punct">.</span><span class="ident">update_all</span> <span class="punct">"</span><span class="string">date_released = now()</span><span class="punct">"</span>
|
|
<span class="keyword">end</span>
|
|
|
|
<span class="keyword">def </span><span class="method">self.down</span>
|
|
<span class="ident">remove_column</span> <span class="punct">"</span><span class="string">albums</span><span class="punct">",</span> <span class="punct">"</span><span class="string">date_released</span><span class="punct">"</span>
|
|
<span class="keyword">end</span>
|
|
<span class="keyword">end</span></code></pre></div>
|
|
|
|
<p>Then you run the migration (<code>rake migrate</code> does that) and boom, your up to date. If you're wondering, the <code>self.down</code> method indeed implies that you can take this the other direction as well. Think <code>rake migrate VERSION=X</code>.</p>
|
|
|
|
|
|
<p><em>Along with the other screencasts is one on <a href="http://www.rubyonrails.org/screencasts">migrations</a> featuring none other than David Hansson. You should take a look, it's the third video.</em></p>
|
|
|
|
|
|
<h3>Testing so easy it hurts</h3>
|
|
|
|
|
|
<p>To start a rails project you type <code>rails project_name</code> and it creates a directory structure with a fresh project in it. This includes a directory appropriately called <em>test</em> which houses unit tests for the project. When you generate models and controllers it creates test stubs for you in that directory. Basically, it makes it so easy to test that you're a fool not to do it. As someone wrote on their site: <em>It means never having to say "<strong>I introduced a new bug while fixing another.</strong>"</em></p>
|
|
|
|
|
|
<p>Rails builds on the unit testing that comes with Ruby. On a larger scale, that means that Rails is unlikely to flop on you because it is regularly tested using the same method. Ruby is unlikely to flop for the same reason. That makes me look good as a programmer. If you code for a living then it's of even more value to you.</p>
|
|
|
|
|
|
<p><em>I don't know why it hurts. Maybe it hurts developers working with other frameworks or languages to see us have it so nice and easy.</em></p>
|
|
|
|
|
|
<h2>Wrapping up</h2>
|
|
|
|
|
|
<p>Rails means I have fun doing web development instead of being frustrated (CSS hacks aside). David Hansson may be right when he said you have to have been soured by Java or PHP to fully appreciate Rails, but that doesn't mean you won't enjoy it if you <em>do</em> like Java or PHP.</p>
|
|
|
|
|
|
<p><a href="http://www.relevancellc.com/blogs/wp-trackback.php?p=31">Justin Gehtland</a> rewrote a Java app using Rails and the number of lines of code of the Rails version was very close to that of the XML configuration for the Java version. Java has strengths, libraries available <strong>now</strong> seems to be a big one, but it's too big for my needs. If you're like me then maybe you'll enjoy Rails as much as I do.</p>
|
|
|
|
|
|
<h2>You're not done, you lied to me!</h2>
|
|
|
|
|
|
<p>Sort of... there are a few things that it seems standard to include when someone writes about how Rails saved their life and gave them hope again. For completeness sake, I feel compelled to mention some principles common amongst those who develop Rails, and those who develop on Rails. It's entirely likely that there's nothing new for you here unless you're new to Rails or to programming, in which case I encourage you to read on.</p>
|
|
|
|
|
|
<h3>DRY</h3>
|
|
|
|
|
|
<p>Rails follows the DRY principle religiously. That is, <strong>Don't Repeat Yourself</strong>. Like MVC, I was already sold on this. I had previously encountered it in <a href="http://www.pragmaticprogrammer.com/ppbook/index.shtml">The Pragmatic Programmer</a>. Apart from telling <em>some_model</em> it <code>belongs_to :other_model</code> and <em>other_model</em> that it <code>has_many :some_models</code> nothing has jumped out at me which violates this principle. However, I feel that reading a model's code and seeing it's relationships to other models right there is a Good Thing™.</p>
|
|
|
|
|
|
<h3>Convention over configuration <em>(or, Perceived intelligence)</em></h3>
|
|
|
|
|
|
<p>Rails' developers also have the mantra "<em>convention over configuration</em>", which you can see from the video there. (you did watch it, didn't you? ;) Basically that just means Rails has sane defaults, but is still flexible if you don't like the defaults. You don't have to write even one line of SQL with Rails, but if you need greater control then you <em>can</em> write your own SQL. A standard cliché: <em>it makes the simple things easy and the hard possible</em>.</p>
|
|
|
|
|
|
<p>Rails seems to have a level of intelligence which contributes to the wow-factor. After <a href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#migrations">these relationships</a> are defined I can now filter certain negative comments like so:</p>
|
|
|
|
|
|
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">article</span> <span class="punct">=</span> <span class="constant">Article</span><span class="punct">.</span><span class="ident">find</span> <span class="symbol">:first</span>
|
|
<span class="keyword">for</span> <span class="ident">comment</span> <span class="keyword">in</span> <span class="ident">article</span><span class="punct">.</span><span class="ident">comments</span> <span class="keyword">do</span>
|
|
<span class="ident">print</span> <span class="ident">comment</span> <span class="keyword">unless</span> <span class="ident">comment</span><span class="punct">.</span><span class="ident">downcase</span> <span class="punct">==</span> <span class="punct">'</span><span class="string">you suck!</span><span class="punct">'</span>
|
|
<span class="keyword">end</span></code></pre></div>
|
|
|
|
<p>Rails knows to look for the field <strong>article_id</strong> in the <strong>comments</strong> table of the database. This is just a convention. You can call it something else but then you have to tell Rails what you like to call it.</p>
|
|
|
|
|
|
<p>Rails understands pluralization, which is a detail but it makes everything feel more natural. If you have a <strong>Person</strong> model then it will know to look for the table named <strong>people</strong>.</p>
|
|
|
|
|
|
<h3>Code as you learn</h3>
|
|
|
|
|
|
<p>I love how I've only been coding in Rails for a week or two and I can do so much already. It's natural, concise and takes care of the inane details. I love how I <em>know</em> that I don't even have to explain that migration example. It's plainly clear what it does to the database. It doesn't take long to get the basics down and once you do it goes <strong>fast</strong>.</p>
|
|
</div>
|
|
<p class="meta">
|
|
Posted in <a href="http://sami.samhuri.net/articles/category/rails" rel="tag">rails</a>, <a href="http://sami.samhuri.net/articles/category/hacking" rel="tag">coding</a> <strong>|</strong> Tags <a href="http://sami.samhuri.net/articles/tag/hacking" rel="tag">hacking</a>, <a href="http://sami.samhuri.net/articles/tag/migration" rel="tag">migration</a>, <a href="http://sami.samhuri.net/articles/tag/rails" rel="tag">rails</a>, <a href="http://sami.samhuri.net/articles/tag/testing" rel="tag">testing</a> <strong>|</strong> <a href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#comments">2 comments</a> <strong>|</strong> <a href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#trackbacks">no trackbacks</a>
|
|
</p>
|
|
|
|
<a name="comments"></a><h4 class="blueblk">Comments</h4>
|
|
<p class="postmetadata alt">
|
|
<small><a href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#respond">Leave a response</a></small>
|
|
</p>
|
|
<ol class="comment-list" id="commentList">
|
|
|
|
<li id="comment-31" onmouseover="if (getCookie('is_admin') == 'yes') { Element.show('admin_comment_31'); }" onmouseout="Element.hide('admin_comment_31');">
|
|
<div id="admin_comment_31" style="display: none"><a class="admintools" href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#" onclick="if (confirm('Are you sure you want to delete this comment?')) { new Ajax.Request('/articles/nuke_comment/31', {asynchronous:true, evalScripts:true, onComplete:function(request){new Effect.Puff('comment-31',{duration:0.6});}}); }; return false;">nuke</a><a href="http://sami.samhuri.net/admin/comments/article/15/edit/31" class="admintools">edit</a></div>
|
|
<a name="comment-31"></a>
|
|
|
|
<cite><strong><a href="http://www.concept10.wordpress.com/">Tracey</a></strong> </cite> said 11 days later:<br>
|
|
<p>Great post on the Rails. I agree with what you said about 'readability'. Code beauty is very important to me.</p>
|
|
</li><li id="comment-34" onmouseover="if (getCookie('is_admin') == 'yes') { Element.show('admin_comment_34'); }" onmouseout="Element.hide('admin_comment_34');">
|
|
<div id="admin_comment_34" style="display: none"><a class="admintools" href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#" onclick="if (confirm('Are you sure you want to delete this comment?')) { new Ajax.Request('/articles/nuke_comment/34', {asynchronous:true, evalScripts:true, onComplete:function(request){new Effect.Puff('comment-34',{duration:0.6});}}); }; return false;">nuke</a><a href="http://sami.samhuri.net/admin/comments/article/15/edit/34" class="admintools">edit</a></div>
|
|
<a name="comment-34"></a>
|
|
|
|
<cite><strong><a href="http://sami.samhuri.net/">Sami</a></strong> </cite> said 12 days later:<br>
|
|
<p>Thanks Tracey, glad you enjoyed it.</p>
|
|
</li>
|
|
|
|
</ol>
|
|
|
|
<a name="trackbacks"></a><h4 class="blueblk">Trackbacks</h4>
|
|
<p>
|
|
Use the following link to trackback from your own site:<br>
|
|
<span class="light-bg">http://sami.samhuri.net/articles/trackback/15</span>
|
|
</p>
|
|
|
|
|
|
<p class="postmetadata alt">
|
|
<small>
|
|
<a href="http://sami.samhuri.net/xml/rss20/article/15/feed.xml" title="RSS Feed">RSS feed for this post</a>
|
|
<a href="http://sami.samhuri.net/articles/trackback/15">trackback uri</a>
|
|
</small>
|
|
</p>
|
|
|
|
<form action="http://sami.samhuri.net/articles/comment/15" class="commentform" id="commentform" method="post" onsubmit="new Ajax.Updater({success:'commentList'}, '/articles/comment/15', {asynchronous:true, evalScripts:true, insertion:Insertion.Bottom, onComplete:function(request){complete(request)}, onFailure:function(request){failure(request)}, onLoading:function(request){loading()}, parameters:Form.serialize(this)}); return false;">
|
|
|
|
|
|
<div class="comment-box">
|
|
<div id="errors"></div>
|
|
<div id="preview" style="display: none"></div>
|
|
|
|
<a name="respond"></a>
|
|
<table cellpadding="4" cellspacing="0" class="frm-tbl">
|
|
<tbody><tr>
|
|
<td><p><label for="comment_author">Your name</label></p></td>
|
|
<td> <input id="comment_author" name="comment[author]" size="20" type="text"> <small><a href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#" onclick="Element.toggle('guest_url'); Element.toggle('guest_email'); return false;">(leave url/email »)</a></small></td>
|
|
</tr>
|
|
<tr id="guest_url" style="display:none;">
|
|
<td><p><label for="comment_url">Your blog</label></p></td>
|
|
<td> <input id="comment_url" name="comment[url]" size="30" type="text"></td>
|
|
</tr>
|
|
<tr id="guest_email" style="display:none;">
|
|
<td><p><label for="comment_email">Your email</label></p></td>
|
|
<td> <input id="comment_email" name="comment[email]" size="30" type="text"></td>
|
|
</tr>
|
|
<tr>
|
|
<td><p><label for="comment_body">Your message</label></p></td>
|
|
<td valign="top" colspan="2">
|
|
<textarea cols="40" id="comment_body" name="comment[body]" rows="20"></textarea>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="2" id="frm-btns">
|
|
<span id="comment_loading" style="display:none;"><img alt="Spinner" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/spinner.gif"></span>
|
|
<a href="http://sami.samhuri.net/articles/2006/02/20/obligatory-post-about-ruby-on-rails#" onclick="new Ajax.Updater('preview', '/articles/comment_preview', {asynchronous:true, evalScripts:true, parameters:Form.serialize('commentform'), onComplete:function(request){Element.show('preview')}}); return false;">Preview comment</a>
|
|
<input type="submit" name="submit" id="form-submit-button" value="submit" class="button">
|
|
</td>
|
|
</tr>
|
|
</tbody></table>
|
|
</div>
|
|
</form>
|
|
|
|
<script type="text/javascript">
|
|
//<![CDATA[
|
|
show_dates_as_local_time()
|
|
//]]>
|
|
</script>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="leftcolumn">
|
|
<div id="header">
|
|
<h1 id="sitename"><a href="http://sami.samhuri.net/">sjs</a></h1>
|
|
<h2 id="subtitle"> </h2>
|
|
</div>
|
|
|
|
<div id="sidebar">
|
|
<div id="search"><form action="http://sami.samhuri.net/articles/search" id="sform" method="get">
|
|
<label for="q">Search: </label><input type="text" id="q" name="q" value="" size="15">
|
|
<img alt="Spinner-blue" id="search_spinner" src="./Obligatory Post about Ruby on Rails - samhuri.net_files/spinner-blue.gif" style="display:none;">
|
|
</form>
|
|
<script type="text/javascript">
|
|
//<![CDATA[
|
|
new Form.Element.Observer('q', 1, function(element, value) {new Ajax.Updater('search-results', '/live/search', {asynchronous:true, evalScripts:true, onComplete:function(request){Element.hide('search_spinner')}, onLoading:function(request){Element.show('search_spinner')}, parameters:'q=' + escape($F('q'))})})
|
|
//]]>
|
|
</script></div>
|
|
<div id="search-results"></div>
|
|
|
|
|
|
|
|
<div class="sidebar-node">
|
|
<h3>All about...</h3>
|
|
<ul>
|
|
<li><a href="http://sami.samhuri.net/pages/about">a bit about me</a></li>
|
|
<li><a href="http://sami.samhuri.net/pages/thunder">workstation</a></li>
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sidebar-node">
|
|
<h3>Categories</h3>
|
|
<ul id="categories">
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/amusement">amusement</a> <em>(5)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/gentoo">gentoo</a> <em>(1)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/hacking">coding</a> <em>(17)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/life">life</a> <em>(5)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/linux">linux</a> <em>(2)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/mac-os-x">mac os x</a> <em>(7)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/python">python</a> <em>(1)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/rails">rails</a> <em>(12)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/ruby">ruby</a> <em>(1)</em></li>
|
|
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/technology">technology</a> <em>(5)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/textmate">textmate</a> <em>(10)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/apple">apple</a> <em>(8)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/php">php</a> <em>(4)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/windows">windows</a> <em>(1)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/bootcamp">bootcamp</a> <em>(1)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/parallels">parallels</a> <em>(1)</em></li>
|
|
|
|
|
|
<li><a href="http://sami.samhuri.net/articles/category/firefox">firefox</a> <em>(1)</em></li>
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sidebar-node">
|
|
|
|
<h3>Archives</h3>
|
|
<ul id="archives">
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2007/03">March 2007</a>
|
|
<em>(2)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/12">December 2006</a>
|
|
<em>(1)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/09">September 2006</a>
|
|
<em>(2)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/08">August 2006</a>
|
|
<em>(1)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/07">July 2006</a>
|
|
<em>(5)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/06">June 2006</a>
|
|
<em>(4)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/05">May 2006</a>
|
|
<em>(2)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/04">April 2006</a>
|
|
<em>(1)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/03">March 2006</a>
|
|
<em>(4)</em>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="http://sami.samhuri.net/articles/2006/02">February 2006</a>
|
|
<em>(1)</em>
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sidebar-node">
|
|
<h3>Links</h3>
|
|
<ul>
|
|
<li><a href="http://phox.ca/cswiki/Main_Page">CSWiki</a></li>
|
|
<li><a href="http://jim.roepcke.com/">Have Browser, Will Travel</a></li>
|
|
<li><a href="http://blog.inquirylabs.com/">Going Up</a></li>
|
|
<li><a href="http://www.mirage.org/">encieno</a></li>
|
|
<li><a href="http://www.nofxwiki.net/">nofxwiki.net</a></li>
|
|
<li>sami at samhuri dot net</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sidebar-node">
|
|
<ul>
|
|
<li><a class="feed" href="http://sami.samhuri.net/xml/rss20/feed.xml" title="Articles feed"><img src="./Obligatory Post about Ruby on Rails - samhuri.net_files/feed-icon-48x48.png" alt="RSS 2.0" width="48" height="48"></a></li>
|
|
|
|
<!--
|
|
|
|
-->
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<br clear="all">
|
|
</div>
|
|
</div>
|
|
|
|
<div id="footer">
|
|
<ul>
|
|
<li><a href="http://typo.leetsoft.com/">Typo</a></li>
|
|
<li>•</li>
|
|
<li><a href="http://validator.w3.org/check?uri=referer">Valid XHTML</a></li>
|
|
<li>•</li>
|
|
<li><a href="http://jigsaw.w3.org/css-validator/validator?uri=http://sami.samhuri.net/">Valid CSS</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<script src="./Obligatory Post about Ruby on Rails - samhuri.net_files/urchin.js" type="text/javascript">
|
|
</script>
|
|
<script type="text/javascript">
|
|
_uacct = "UA-214054-3";
|
|
urchinTracker();
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
FILE ARCHIVED ON 17:18:39 Mar 16, 2007 AND RETRIEVED FROM THE
|
|
INTERNET ARCHIVE ON 2:41:14 Aug 21, 2011.
|
|
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
|
|
|
|
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
|
|
SECTION 108(a)(3)).
|
|
-->
|
|
<div id="footnotify_lightbox" style="position: fixed; width: 100%; height: 100%; top: 0px; left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgba(0, 0, 0, 0.699219); display: none; background-position: initial initial; background-repeat: initial initial; "></div><div id="footnotify_holder" style="position: absolute; display: none; "><div id="footnotify_arrow" style="letter-spacing: -1px; width: 32px; margin-top: 0px; margin-right: -11px; margin-bottom: 0px; margin-left: -11px; text-align: center; font-size: 13px; padding-top: 2em; line-height: 0.9em; color: rgb(0, 0, 0); ">◢◣</div><div id="footnotify_panel" style="padding-top: 1px; padding-right: 1px; padding-bottom: 1px; padding-left: 1px; margin-top: 0px; margin-right: -240px; margin-bottom: 0px; margin-left: -240px; width: 480px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(0, 0, 0); color: inherit; background-position: initial initial; background-repeat: initial initial; "><div id="footnotify_content" style="margin-top: 2em; margin-right: 2em; margin-bottom: 2em; margin-left: 2em; "></div></div></div><div id="footnotify_notification" style="position: fixed; top: 5px; left: 5px; z-index: 999; padding-top: 1em; padding-right: 1em; padding-bottom: 1em; padding-left: 1em; color: rgb(255, 255, 255); background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgba(0, 0, 0, 0.699219); display: none; background-position: initial initial; background-repeat: initial initial; "></div></body></html> |