<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-37875365</atom:id><lastBuildDate>Wed, 10 Mar 2010 03:29:51 +0000</lastBuildDate><title>Jer on Rails - Ruby on Rails and More</title><description>Jeremy Weiskotten on building "real stuff" with Rails.</description><link>http://blog.weiskotten.com/</link><managingEditor>noreply@blogger.com (Jeremy Weiskotten)</managingEditor><generator>Blogger</generator><openSearch:totalResults>37</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/JerOnRails" /><feedburner:info uri="jeronrails" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-8198610065280480139</guid><pubDate>Tue, 08 Dec 2009 16:28:00 +0000</pubDate><atom:updated>2009-12-08T11:51:37.833-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">testing</category><category domain="http://www.blogger.com/atom/ns#">sass</category><title>A Kick in the Sass: Making slow functional tests faster</title><description>&lt;p&gt;&lt;br /&gt;We started using &lt;a href="http://sass-lang.com/"&gt;Sass&lt;/a&gt; a while ago for all new styles, and recently converted all of our CSS to Sass. We noticed that after the conversion to Sass our test suite was suddenly taking a lot longer to run, in the neighborhood of 50% longer. Specifically, our functional and integration tests were a &lt;span style="font-weight:bold;"&gt;lot&lt;/span&gt; slower.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Gdc6BUabwRo/Sx6C3NlDASI/AAAAAAAAB4Y/5BGkflwn-WA/s1600-h/3368034717_d15afa2973.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 261px;" src="http://4.bp.blogspot.com/_Gdc6BUabwRo/Sx6C3NlDASI/AAAAAAAAB4Y/5BGkflwn-WA/s400/3368034717_d15afa2973.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5412907687195377954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;It turns out that, out of the box, Sass updates the "compiled" stylesheets on every request unless you're running in production mode. This is really handy in development when you're making incremental edits in a .sass file and refreshing the browser to see the results, but it's completely unnecessary when running tests since the .sass files aren't changing while the tests are running, and chances are your functional tests aren't dependent on any CSS files anyway.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;To change this behavior in our test environment, we added the following to config/environments/test.rb:&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/251766.js"&gt;&lt;/script&gt;&lt;br /&gt;Now Sass will only compile the stylesheets once (the first time one is needed), and our entire test suite is about 60% faster (YMMV).&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Gdc6BUabwRo/Sx6Bx0f5jnI/AAAAAAAAB4Q/7Mx0xnlxQr8/s1600-h/D737~Speed-Racer-Go-Speed-Posters.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 312px;" src="http://1.bp.blogspot.com/_Gdc6BUabwRo/Sx6Bx0f5jnI/AAAAAAAAB4Q/7Mx0xnlxQr8/s400/D737~Speed-Racer-Go-Speed-Posters.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5412906495051927154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-8198610065280480139?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/TEWWC-VrCn8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/TEWWC-VrCn8/kick-in-sass-making-slow-functional.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_Gdc6BUabwRo/Sx6C3NlDASI/AAAAAAAAB4Y/5BGkflwn-WA/s72-c/3368034717_d15afa2973.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2009/12/kick-in-sass-making-slow-functional.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-5470773510761475767</guid><pubDate>Tue, 08 Dec 2009 04:14:00 +0000</pubDate><atom:updated>2009-12-07T23:17:55.098-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">interview</category><title>The worst wide receiver in professional football: ME?</title><description>I was recently interviewed for EcontentMag. Strange being referred to as "Weiskotten", but now I'm a little more Internet-famous!&lt;br /&gt;&lt;br /&gt;If you're bored, &lt;a href="http://www.econtentmag.com/Articles/Editorial/Faces-of-Econtent/Jeremy-Weiskotten,-Senior-Software-Engineer,-PatientsLikeMe-57947.htm"&gt;check it out&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-5470773510761475767?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/HvOvcNoKzo4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/HvOvcNoKzo4/worst-wide-receiver-in-professional.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2009/12/worst-wide-receiver-in-professional.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-918944688532792603</guid><pubDate>Tue, 27 Oct 2009 23:27:00 +0000</pubDate><atom:updated>2010-01-31T19:35:30.741-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">qa</category><category domain="http://www.blogger.com/atom/ns#">careers</category><category domain="http://www.blogger.com/atom/ns#">PatientsLikeMe</category><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">jobs</category><title>Hiring Rails Developers and QA</title><description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Gdc6BUabwRo/SueSlhLrk_I/AAAAAAAAB4I/_dGRn9Lh0x0/s1600-h/18127v1-max-250x250.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 250px; height: 48px;" src="http://3.bp.blogspot.com/_Gdc6BUabwRo/SueSlhLrk_I/AAAAAAAAB4I/_dGRn9Lh0x0/s400/18127v1-max-250x250.png" alt="" id="BLOGGER_PHOTO_ID_5397443851687007218" border="0" /&gt;&lt;/a&gt;PatientsLikeMe is &lt;a href="http://www.patientslikeme.com/about/careers"&gt;growing&lt;/a&gt;! We're looking for a few great Rails developers. If you don't know Rails but are eager to learn, &lt;a href="http://www.rubyonrails.org/"&gt;start&lt;/a&gt; &lt;a href="http://railscasts.com/"&gt;learning&lt;/a&gt; &lt;a href="http://www.amazon.com/Agile-Web-Development-Rails-Programmers/dp/097669400X"&gt;now&lt;/a&gt; and get in touch.&lt;br /&gt;&lt;br /&gt;We're also looking for an experienced QA engineer to keep us honest and help us with automated acceptance/regression testing, among other things.&lt;br /&gt;&lt;br /&gt;To get you psyched up about coming to work with us, I thought I'd share a little about our team, our culture, and how we get things done.&lt;br /&gt;&lt;br /&gt;We have a couple of amazing designers on staff, &lt;a href="http://www.patientslikeme.com/members/view/3172"&gt;Kate&lt;/a&gt; and &lt;a href="http://www.patientslikeme.com/members/view/6751"&gt;Scott&lt;/a&gt;. We get to bring their works of art to life. Kate has lots of insight into usability and user testing, and Scott brings a wealth of knowledge about visualizations (and he &lt;a href="http://astronautdinosaur.com/"&gt;paints astronauts&lt;/a&gt;, which is awesome).&lt;br /&gt;&lt;br /&gt;We still support IE6. Yeah, that sucks, but a good chunk of our users (mostly patients) still use it. Lucky for us, &lt;a href="http://www.patientslikeme.com/members/view/91"&gt;Cris&lt;/a&gt; and &lt;a href="http://www.patientslikeme.com/members/view/adarowski"&gt;Adam&lt;/a&gt; know all the tricks. Semantic markup, IE hacks, accessibility... we've got it all. Well, except &lt;a href="http://mobihealthnews.com/1370/whcc-patientslikeme-doesnt-like-mobile/"&gt;mobile&lt;/a&gt;, but that can change.&lt;br /&gt;&lt;br /&gt;We do all kinds of crazy, awesome things in our free time. You already know about Scott's astro-art. I created &lt;a href="http://www.twackit.com/"&gt;Twackit&lt;/a&gt; to play around with the Twitter API. &lt;a href="http://www.patientslikeme.com/members/view/18959"&gt;James&lt;/a&gt; created a really cool &lt;a href="http://www.monkeyatlarge.com/archives/2009/08/30/day-in-the-life-of-the-mbta-system/"&gt;visualization of the MBTA system&lt;/a&gt;. &lt;a href="http://www.patientslikeme.com/members/view/804"&gt;Steve&lt;/a&gt; is a Solar System Ambassador for the NASA Jet Propulsion Lab and a school committee member. &lt;a href="http://www.patientslikeme.com/members/view/16596"&gt;Jeff "Mav" Dwyer&lt;/a&gt; wrote a &lt;a href="http://www.amazon.com/Pro-Web-Application-Development-ebook/dp/B001DA9MT6"&gt;book about GWT&lt;/a&gt; (don't worry, we won't make you read it) and hands out Google Wave invites like they're candy. &lt;a href="http://www.patientslikeme.com/members/view/2739"&gt;Rich&lt;/a&gt; helped Dan Cederholm (yes, &lt;span style="font-style: italic;"&gt;the&lt;/span&gt; Dan Cederholm) build &lt;a href="http://dribbble.com"&gt;Dribbble&lt;/a&gt;, and he &lt;span style="font-style: italic;"&gt;barely&lt;/span&gt; beat me in the finals of our Darts Madness tournament. &lt;a href="http://www.patientslikeme.com/members/view/18128"&gt;Joe&lt;/a&gt;, Kate and I helped get &lt;a href="http://www.healthdatarights.org/"&gt;HealthDataRights.org&lt;/a&gt; off the ground. Joe, Steve, Cris, and I participated in last year's Rails Rumble and launched &lt;a href="http://www.quest-for-life.org/"&gt;The Quest for Life&lt;/a&gt;. I'm sure I'm forgetting someone or something, but that should give you an idea of how we roll.&lt;br /&gt;&lt;br /&gt;Also, everyone loves Star Trek IV but me -- that's the one where they go back in time to save the humpback whales, which is stupid.&lt;br /&gt;&lt;br /&gt;We have an in-house &lt;a href="http://www.patientslikeme.com/research"&gt;research&lt;/a&gt; team that sometimes makes me feel like I don't know shit about shit. The stack of journal publications and presentations of their research can probably be blamed for most of the deforestation of the past 10 years. Our researchers help us figure out what data is interesting to patients and what's interesting for research purposes, and they make sure we ask the right questions the right way (something about science and bias and blah blah, etc.).&lt;br /&gt;&lt;br /&gt;Everyone has a MacBook Pro and an external monitor. We use VirtualBox to do IE testing. Some of us commute from out of town so we usually telecommute a couple days a week, but we stay in touch with IM and Campfire.&lt;br /&gt;&lt;br /&gt;We use Git for version control. It's pretty awesome. It's super-easy to work on branches. We're always creating remote branches to work on new features in isolation, and short-lived local branches for quick bug fixes.&lt;br /&gt;&lt;br /&gt;We release in 2 week iterations. Fast enough to get stuff out, not so fast you lose track of what's going on.&lt;br /&gt;&lt;br /&gt;There's still a lot of legacy stuff that uses ERB and regular CSS, but all our new stuff is Haml and Sass.&lt;br /&gt;&lt;br /&gt;We have  dartboard that gets a lot of use, a Nintendo Wii (with Rock Band, of course), and we've been known to watch an episode of Star Trek or a certain movie featuring The Dude after work while we sip White Russians (or Caucasians, if you prefer). Three desks double as a conference room table which doubles as a ping pong table, and lo the balls fly furiously.&lt;br /&gt;&lt;br /&gt;It's rare to find a job that's challenging, fun, or makes a difference in people's lives, but PatientsLikeMe offers all of that in one place. A really great perk of working at PatientsLikeMe is that we have an opportunity to improve the lives of real people -- thousands of patients with life-changing conditions. They're a very involved user-base, with no shortage of ideas and feedback.&lt;br /&gt;&lt;br /&gt;Oh, and we have direct deposit! You gotta have direct deposit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-918944688532792603?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/CJwFN_S3tng" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/CJwFN_S3tng/hiring-rails-developers-and-qa.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_Gdc6BUabwRo/SueSlhLrk_I/AAAAAAAAB4I/_dGRn9Lh0x0/s72-c/18127v1-max-250x250.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2009/10/hiring-rails-developers-and-qa.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-1831219511683502772</guid><pubDate>Sat, 17 Oct 2009 19:43:00 +0000</pubDate><atom:updated>2009-11-24T20:44:47.229-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">leopard</category><category domain="http://www.blogger.com/atom/ns#">mac</category><category domain="http://www.blogger.com/atom/ns#">setup</category><title>Setting up a Rails development environment on Leopard</title><description>I recently set up a new Rails environment on Mac OS X 10.5 (Leopard). I googled for various bits and pieces. &lt;span style="font-weight: bold;"&gt;I have no idea how much of this translates to 10.6 (Snow Leopard).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I leaned on MacPorts to install Git, Ruby, RubyGems, PostgreSQL, and some other stuff that my Rails work depends on.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;Xcode and MacPorts&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Install Xcode from OSX DVD.&lt;/li&gt;&lt;li&gt;Install MacPorts via http://www.macports.org/install.php&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;Git&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo port install git-core +svn+bash_completion+doc&lt;/li&gt;&lt;/ol&gt;To configure Git, including some handy aliases, add the following to ~/.gitconfig:&lt;br /&gt;&lt;br /&gt;[user]&lt;br /&gt;  email = {your@email.com}&lt;br /&gt;  name = {Your Name}&lt;br /&gt;[core]&lt;br /&gt;  editor = mate -w&lt;br /&gt;[color]&lt;br /&gt;  status = auto&lt;br /&gt;  diff = auto&lt;br /&gt;  branch = auto&lt;br /&gt;[alias]&lt;br /&gt;  co = checkout&lt;br /&gt;  st = status&lt;br /&gt;  ci = commit&lt;br /&gt;  df = !git diff | mate&lt;br /&gt;[push]&lt;br /&gt;  default = tracking&lt;br /&gt;&lt;br /&gt;To enable bash_completion, add the following lines to the end of your .bash_profile or .profile:&lt;br /&gt;&lt;br /&gt;  if [ -f /opt/local/etc/bash_completion ]; then&lt;br /&gt;      . /opt/local/etc/bash_completion&lt;br /&gt;  fi&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ruby 1.8.7 and RubyGems&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo port install ruby&lt;/li&gt;&lt;li&gt;sudo port install rb-rubygems&lt;/li&gt;&lt;/ol&gt;(Note that gems will be installed to /opt/local/lib/ruby/gems/1.8.)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Symlink /usr/local/bin/ruby to MacPorts install&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo ln -s /opt/local/bin/ruby /usr/local/bin/ruby&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;ImageMagick and RMagick&lt;/span&gt;&lt;br /&gt;http://rmagick.rubyforge.org/install-osx.html&lt;ol&gt;&lt;li&gt;sudo port install imagemagick +gs +q16&lt;/li&gt;&lt;li&gt;sudo gem install rmagick&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;PostgreSQL&lt;/span&gt; 8.3&lt;br /&gt;http://shifteleven.com/articles/2008/03/21/installing-postgresql-on-leopard-using-macports&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo port install postgresql83 postgresql83-server&lt;/li&gt;&lt;li&gt;sudo mkdir -p /opt/local/var/db/postgresql83/defaultdb&lt;/li&gt;&lt;li&gt;sudo chown postgres:postgres /opt/local/var/db/postgresql83/defaultdb&lt;/li&gt;&lt;li&gt;sudo su postgres -c '/opt/local/lib/postgresql83/bin/initdb -D /opt/local/var/db/postgresql83/defaultdb'&lt;/li&gt;&lt;/ol&gt;And so that pg server launches at system startup...&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo launchctl load -w /Library/LaunchDaemons/org.macports.postgresql83-server.plist&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;PostgreSQL Ruby adapter&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo env ARCHFLAGS="-arch i386" gem install postgres -- --with-pgsql-lib=/opt/local/lib/postgresql83 --with-pgsql-include=/opt/local/include/postgresql83&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;Sphinx&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo port install sphinx +postgresql83&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;Common gems&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;sudo gem install rails fastercsv redgreen git_remote_branch capistrano capistrano-ext ruby-debug ruby-prof sqlite3-ruby&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;SSH key&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;ssh-keygen (Enter to select all defaults)&lt;/li&gt;&lt;li&gt;Add public key to GitHub account.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-1831219511683502772?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/f4M_pL28Oog" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/f4M_pL28Oog/setting-up-rails-development.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.weiskotten.com/2009/10/setting-up-rails-development.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-9192237247952701282</guid><pubDate>Thu, 30 Apr 2009 00:23:00 +0000</pubDate><atom:updated>2009-04-29T21:03:30.860-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">git</category><title>My Git Workflow</title><description>(Or, how I learned to stop worrying and love the Git.)&lt;br /&gt;&lt;br /&gt;&lt;a style="font-weight: bold;" href="http://whygitisbetterthanx.com/"&gt;Git is awesome&lt;/a&gt;&lt;span style="font-weight: bold;"&gt;.&lt;/span&gt; If you're using it, you know what I mean. If you're not, don't be afraid to &lt;a href="http://gitready.com/"&gt;give it a try&lt;/a&gt;! Git is the gateway to a new generation of code sharing on GitHub and convenient deployment to Heroku. It's built around fast branching, so it's really easy (and did I mention fast?) to create and throw away experimental branches.&lt;br /&gt;&lt;br /&gt;I'm pretty new to Git. I used it a bit in the past (with GitHub) but hadn't really gotten into it until the past month or so when we made the switch at PatientsLikeMe from Subversion. It took me a couple of weeks to really understand what was going on, feel comfortable, and settle into a rhythm and workflow. This post will probably be old news to Git veterans, but I think there are enough Git noobs still out there that some will find this interesting or valuable (let me know if you're one of them!).&lt;br /&gt;&lt;br /&gt;Here's how I use Git day-to-day, whether for new feature development or fixing bugs:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;A. Work &lt;/span&gt;in a local branch&lt;ol&gt;&lt;li&gt;git checkout -b &lt;span style="font-style: italic;"&gt;dev-branch-name&lt;/span&gt; (to create and switch to a local branch, where "dev-branch-name" is something meaningful)&lt;/li&gt;&lt;li&gt;Do some awesome (more or less) work.&lt;/li&gt;&lt;li&gt;git status, git add, git commit -am "Something about what I did"&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;B. Integrate&lt;/span&gt; with the latest&lt;br /&gt;&lt;ol&gt;&lt;li&gt;git checkout master&lt;/li&gt;&lt;li&gt;git pull (gets the stuff my teammates have pushed)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;git checkout &lt;span style="font-style: italic;"&gt;dev-branch-name&lt;/span&gt;&lt;/li&gt;&lt;li&gt;git rebase master (integrates my work with my teammates')&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Run the test suite.&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;C. Merge&lt;/span&gt; with the rest of the team&lt;br /&gt;&lt;ol&gt;&lt;li&gt;git checkout master&lt;/li&gt;&lt;li&gt;git merge &lt;span style="font-style: italic;"&gt;dev-branch-name&lt;/span&gt;&lt;/li&gt;&lt;li&gt;git push&lt;/li&gt;&lt;/ol&gt;It looks like a lot of steps and a lot to remember, but you can automate much of it if you'd like to save some keystrokes. I still like to go through the motions. You could use a simpler, more traditional (read: SVN) workflow, doing all of your work in master, but then you're missing out on a lot of the benefit of Git.&lt;br /&gt;&lt;br /&gt;To make git a bit more user-friendly, I set up most of the aliases suggested by &lt;a href="http://justinfrench.com/index.php?id=253"&gt;Justin French&lt;/a&gt;, so I actually use "git co" for checkout and "git ci" for commit, and I have a git completion bash script set up so that I don't have to type out full branch names.&lt;br /&gt;&lt;br /&gt;Switching between master and various local branches can get a bit confusing. To keep myself on track, and to try to avoid working in the wrong branch, I set up a &lt;a href="http://github.com/guides/put-your-git-branch-name-in-your-shell-prompt"&gt;shell prompt&lt;/a&gt; that displays the current branch for the git-aware working directory that I'm in. Here's the actual prompt I'm using:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;function parse_git_branch&lt;br /&gt;{&lt;br /&gt;ref=$(git-symbolic-ref HEAD 2&gt; /dev/null) || return&lt;br /&gt;echo ${ref#refs/heads/}&lt;br /&gt;}&lt;br /&gt;export PS1='\[\033[0;34m\]\A \[\033[00m\]\w \[\033[01;31m\]$(parse_git_branch)\[\033[00m\] $ '&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I've also started using &lt;a href="http://github.com/webmat/git_remote_branch/tree/master"&gt;grb&lt;/a&gt; to make working with (read: tracking) remote branches easier. It's easy to install and has good documentation to get you started. Then there's &lt;a href="http://gitx.frim.nl/"&gt;GitX&lt;/a&gt;, a powerful tool to browse your repository and to see which code is on what branches. I don't lean on GitX as much as I could, but it comes in handy.&lt;br /&gt;&lt;br /&gt;There's a lot about Git that I still need to learn, so please leave a comment with your tips and tricks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-9192237247952701282?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/1LZsKm2ZBLs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/1LZsKm2ZBLs/my-git-workflow.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://blog.weiskotten.com/2009/04/my-git-workflow.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-6954976773444907466</guid><pubDate>Tue, 21 Apr 2009 17:34:00 +0000</pubDate><atom:updated>2009-04-29T21:02:33.803-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">twitter</category><category domain="http://www.blogger.com/atom/ns#">twackit</category><title>Announcing Twackit</title><description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://twackit.com/"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 265px; height: 301px;" src="http://2.bp.blogspot.com/_Gdc6BUabwRo/Se4MDfhoIzI/AAAAAAAABxY/26jOHdrghj8/s400/screenshot_thumbnail.png" alt="" id="BLOGGER_PHOTO_ID_5327208663367557938" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Last weekend I launched &lt;/span&gt;&lt;a style="font-weight: bold;" href="http://twackit.com/"&gt;Twackit&lt;/a&gt;, which gives you a simple way to use Twitter to keep track of different kinds of metrics that are important to you... stuff like your weight, gas mileage, expenses, caloric intake, hours of sleep -- pretty much anything with a simple numeric value and a hashtaggable category that you might want to keep track of over time.&lt;br /&gt;&lt;br /&gt;There's some basic content and &lt;a href="http://twackit.com/doctorzaius/weight"&gt;reports&lt;/a&gt; are generated from the data you tweet to @&lt;a href="http://twitter.com/twackit"&gt;twackit&lt;/a&gt;. I've gotten some good feedback, and a couple people are already using it for things I never thought of, which is really cool! I wanted to get something simple up and running and try to get people using it, then see what kinds of requirements shake out from real-world use cases.&lt;br /&gt;&lt;br /&gt;Since this is a Rails-oriented blog, I thought I'd share a few technical details:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Twackit is running on &lt;span style="font-weight: bold;"&gt;Rails 2.3.2&lt;/span&gt;, the current stable version.&lt;/li&gt;&lt;li&gt;I'm using &lt;span style="font-weight: bold;"&gt;SQLite3&lt;/span&gt; for development, but Heroku ignores your database.yml and uses Postgres, which is cool with me.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It's hosted on &lt;a style="font-weight: bold;" href="http://www.heroku.com/"&gt;Heroku&lt;/a&gt;, which was very easy to set up in part because...&lt;/li&gt;&lt;li&gt;I'm using &lt;span style="font-weight: bold;"&gt;Git&lt;/span&gt; for version control. The code is not open source (at least not yet), and I'm only using a local repo, so no GitHub.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I used &lt;a style="font-weight: bold;" href="http://haml.hamptoncatlin.com/"&gt;Haml&lt;/a&gt; for the layout and views, except for a few partials that encapsulate HTML and Javascript for third-party stuff, like Google Analytics and UserVoice. I just pasted their code into ERB templates.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The reports use the &lt;span style="font-weight: bold;"&gt;Google Visualization API&lt;/span&gt; for things like the Annotated Time Line, which is really cool but not very useful until you have a lot of data. (So start adding data, everyone!)&lt;/li&gt;&lt;li&gt;The Annotated Time Line wants Javascript Date objects. Unfortunately, Rails implements Date#to_json to return a String representation of the date, so I ended up hacking it to build a String of the form "new Date(year, month, day)". I'll keep looking for a better way.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I used &lt;a style="font-weight: bold;" href="http://railstips.org/"&gt;John Nunemaker&lt;/a&gt;&lt;span style="font-weight: bold;"&gt;'s &lt;/span&gt;&lt;a style="font-weight: bold;" href="http://github.com/jnunemaker/twitter/tree/master"&gt;Twitter gem&lt;/a&gt; to call the Twitter search API and to get the user's name and photo URL to make the report a little prettier.&lt;/li&gt;&lt;li&gt;Twackit searches for new tweets every hour or so. To support very basic background jobs, Heroku invokes a rake task named "cron" (which I think is a terrible choice because of the name collision, but at least it's intuitive).&lt;/li&gt;&lt;li&gt;The cron rake task records some stats about each cron import, like how long it took, how many tweets were imported, and for how many distinct users. The task finishes in less than a second, since there isn't much data coming in (yet!).&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;So please check &lt;a href="http://twackit.com/"&gt;Twackit&lt;/a&gt; out and let me know what you think. And of course, &lt;a href="http://twitter.com/home?status=Check%20out%20http://twackit.com%20to%20find%20out%20how%20to%20track%20different%20metrics%20and%20observe%20trends%20over%20time"&gt;tell everyone about it&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-6954976773444907466?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/oa-xrNKpcVU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/oa-xrNKpcVU/announcing-twackit.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_Gdc6BUabwRo/Se4MDfhoIzI/AAAAAAAABxY/26jOHdrghj8/s72-c/screenshot_thumbnail.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2009/04/announcing-twackit.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-1943092694735595354</guid><pubDate>Sun, 11 Jan 2009 21:36:00 +0000</pubDate><atom:updated>2009-01-11T16:54:57.006-05:00</atom:updated><title>Generating TinyURLs from Rails</title><description>Sometimes URLs are just too long, especially if you have constraints like the 140 character limit on &lt;a href="http://www.twitter.com/"&gt;Twitter&lt;/a&gt;. Free services like &lt;a href="http://www.tinyurl.com/"&gt;TinyURL&lt;/a&gt; have been around for years, providing short URLs that issue a permanent redirect to the long URL.&lt;br /&gt;TinyURL has a micro-API. Issuing a simple HTTP GET request to:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family: courier new;"&gt;http://tinyurl.com/api-create.php?url=http://www.myreallyreallylongurl.com/its-really-long&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;renders a response like:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family: courier new;"&gt;http://tinyurl.com/8y94xz&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I recently created a Ruby on Rails plugin called &lt;a href="http://github.com/patientslikeme/tinify_urls/tree/master"&gt;tinify_urls&lt;/a&gt; that encapsulates this kind of thing, supporting the TinyURL, &lt;a href="http://snipurl.com/"&gt;SnipURL&lt;/a&gt;, and &lt;a href="http://is.gd/"&gt;is.gd&lt;/a&gt; APIs. The plugin has been tested against Rails 2.1.&lt;br /&gt;&lt;br /&gt;I'm looking forward to feedback so &lt;a href="mailto:jeremy@weiskotten.com"&gt;let me know&lt;/a&gt; if you have a chance to try it out. To install the plugin:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family: courier new;"&gt;script/plugin install git://github.com/patientslikeme/tinify_urls.git&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-1943092694735595354?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/jNEPDifuXo0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/jNEPDifuXo0/generating-tinyurls-from-rails.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2009/01/generating-tinyurls-from-rails.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-4390764544340905420</guid><pubDate>Sat, 26 Jul 2008 01:09:00 +0000</pubDate><atom:updated>2008-07-25T21:31:05.560-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">cache</category><title>Caching ActiveRecord Objects with Rails.cache file_store</title><description>Rails 2.1 presents Rails.cache, a clean little interface for caching stuff. It makes it easy to switch between different cache stores. For example, &lt;a href="http://www.patientslikeme.com"&gt;we&lt;/a&gt; are using memcached in production but something else, like the file system, in development where performance and scalability aren't as important as forward progress.&lt;br /&gt;&lt;br /&gt;Memcached supports the caching of ActiveRecord objects, so you can do something simple like this:&lt;br /&gt;&lt;br /&gt;    &lt;code&gt;all_things = Rails.cache.fetch('Thing.all') { Thing.all }&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This single line of code looks for a cache entry by the key "Thing.all". If it's not found, the code in the block is executed and the results are added to the cache and returned. If that cache entry is found, the value is returned from the cache, avoiding a trip to the database. Very useful for things that rarely change.&lt;br /&gt;&lt;br /&gt;This works great with memcached store, but not with file store, which seems to prefer simple strings. A workaround is to convert the ActiveRecord objects to YAML when putting them in the cache and converting back from YAML when reading:&lt;br /&gt;&lt;br /&gt;    &lt;code&gt;all_things = YAML::load(Rails.cache.fetch('Thing.all') { Thing.all.to_yaml })&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I recommend encapsulating this in a class method:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class Thing &lt; ActiveRecord::Base&lt;br /&gt;&amp;nbsp;&amp;nbsp;def self.all_cached&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;YAML::load(Rails.cache.fetch('Thing.all') { all.to_yaml })&lt;br /&gt;&amp;nbsp;&amp;nbsp;end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-4390764544340905420?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/2t9EHVop7Vc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/2t9EHVop7Vc/caching-activerecord-objects-with.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://blog.weiskotten.com/2008/07/caching-activerecord-objects-with.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-4936600070530100668</guid><pubDate>Thu, 10 Apr 2008 02:02:00 +0000</pubDate><atom:updated>2008-04-22T00:06:20.039-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">blogs</category><category domain="http://www.blogger.com/atom/ns#">rails</category><title>Rails Blogs I Read</title><description>We recently hired a new &lt;a href="http://www.patientslikeme.com/members/view/jdwyer"&gt;developer&lt;/a&gt; at &lt;a href="http://www.patientslikeme.com/"&gt;PatientsLikeMe&lt;/a&gt; who, like I was when I started, is new to Ruby and Rails. To help him get started, I compiled this list of Rails-focused blogs that I enjoy. No particular order. They're not all updated extremely regularly, but they're all interesting, and they have good archives. Thanks to all of the writers who spread Railsy goodness!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/" target="_blank"&gt;http://blog.jayfields.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://errtheblog.com/" target="_blank"&gt;http://errtheblog.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.softiesonrails.com/" target="_blank"&gt;http://www.softiesonrails.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://giantrobots.thoughtbot.com/" target="_blank"&gt;http://giantrobots.thoughtbot&lt;wbr&gt;.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://weblog.jamisbuck.org/" target="_blank"&gt;http://weblog.jamisbuck.org/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://drnicwilliams.com/" target="_blank"&gt;http://drnicwilliams.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.railsenvy.com/" target="_blank"&gt;http://www.railsenvy.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.therailsway.com/" target="_blank"&gt;http://www.therailsway.com/&lt;/a&gt;&lt;a href="http://bokardo.com/" target="_blank"&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.clarkware.com/cgi/blosxom" target="_blank"&gt;http://www.clarkware.com/cgi&lt;wbr&gt;/blosxom&lt;/a&gt;&lt;br /&gt;&lt;a href="http://nubyonrails.com/" target="_blank"&gt;http://nubyonrails.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.hungrymachine.com/" target="_blank"&gt;http://blog.hungrymachine.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.hasmanythrough.com/"&gt;http://blog.hasmanythrough.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.koziarski.net/" target="_blank"&gt;http://www.koziarski.net/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.railsjitsu.com/" target="_blank"&gt;http://www.railsjitsu.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://onrails.org/" target="_blank"&gt;http://onrails.org/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bokardo.com/" target="_blank"&gt;http://bokardo.com/&lt;/a&gt; - Not really a Rails blog, but Josh Porter is a thought leader on social networking, which is very relevant to &lt;a href="http://www.patientslikeme.com/"&gt;my job&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://bokardo.com/" target="_blank"&gt;&lt;/a&gt;&lt;br /&gt;I also highly recommend Ryan Bates' &lt;a href="http://www.railscasts.com/"&gt;Railscasts&lt;/a&gt;. They're free, and only about 10-minutes apiece so they're highly focused and easily digestible. You may know Ryan from the &lt;a href="http://www.railsforum.com/"&gt;Rails Forum&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm sure there are a million other great Rails blogs in this great web of interwebitubes, so post your favorites!&lt;br /&gt;&lt;a href="http://www.railscasts.com/" target="_blank"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-4936600070530100668?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/zSca8L2YGDc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/zSca8L2YGDc/rails-blogs-i-read.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2008/04/rails-blogs-i-read.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-2167533930539141362</guid><pubDate>Thu, 10 Jan 2008 03:11:00 +0000</pubDate><atom:updated>2009-04-24T23:49:05.438-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">foreign keys</category><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">database</category><title>You Should Use Foreign Key Constraints in Rails</title><description>&lt;span style="background: rgb(255, 255, 204) none repeat scroll 0% 0%; font-weight: bold; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;Updated 4/24/09&lt;/span&gt;: If you want to use foreign keys in your Rails project, checkout the &lt;a href="http://github.com/patientslikeme/migration_helpers/tree/master"&gt;migration_helpers&lt;/a&gt; and &lt;a href="http://errtheblog.com/posts/51-sexy-migrations"&gt;sexy_migrations&lt;/a&gt; plugins for a couple of options.&lt;br /&gt;&lt;br /&gt;Jeff from &lt;a href="http://softiesonrails.com/"&gt;Softies on Rails&lt;/a&gt; recommends &lt;a href="http://forum.softiesonrails.com/forums/3/topics/138"&gt;doing without foreign keys&lt;/a&gt; and posted &lt;a href="http://softiesonrails.com/2008/1/9/you-don-t-need-foreign-key-constraints-in-rails"&gt;You Don't Need Foreign Key Constraints In Rails&lt;/a&gt; to ask for our opinions.&lt;br /&gt;&lt;br /&gt;He's right. You don't &lt;span style="font-weight: bold;"&gt;need&lt;/span&gt; foreign key constraints, just like you don't need a seatbelt in your car. Your car will drive perfectly fine without one. You can drive cross-country at 120 miles per hour without a seatbelt, but may the &lt;a href="http://www.venganza.org/"&gt;Flying Spaghetti Monster&lt;/a&gt; have mercy on your soul you if you hit a tree.&lt;br /&gt;&lt;br /&gt;It looks like everyone is on the FK side so far in the discussion. Of course, DHH hasn't commented yet, so that may change. Rails is notorious for being "opinionated software", and this is one of those opinions that a lot of people happen to disagree with, and is one of very few (the only?) cases where the majority of Rails users overwhelmingly, strongly disagree with the Rails philosophy.&lt;br /&gt;&lt;br /&gt;One argument against FKs is that Rails' ActiveRecord provides nifty relationship wiring and validation utilities, like belongs_to, has_many, and has_and_belongs_to_many. Since this logic is defined in the model, repeating it in the database is not DRY. Plus, FKs make it harder to deal with complicated relationships. You can't delete an entity without first deleting its child entities (which :dependent =&gt; :destroy will do for you if you want closure).&lt;br /&gt;&lt;br /&gt;These aren't bad reasons, but they're not good enough to outweigh the benefits of foreign keys, or to offset the potential cost of a weird bug that creates inconsistent data. A fairly common problem in Rails with foreign keys is setting up and tearing down test fixtures -- but let's face it, fixtures pretty much suck, and there are ways to deal with the problem. &lt;a href="http://en.wikipedia.org/wiki/Referential_integrity"&gt;Referential integrity&lt;/a&gt; is more important than cheap, lazy fixtures.&lt;br /&gt;&lt;br /&gt;Foreign keys may repeat something that's already defined in the model code, but so what? Relationships rarely change, and when they do, you're probably going to be changing the database anyway. It's not as if they're likely to get out of sync.&lt;br /&gt;&lt;br /&gt;Foreign keys are pretty easy to create, especially if you use a plugin like &lt;a href="http://errtheblog.com/posts/51-sexy-migrations"&gt;sexy migrations&lt;/a&gt;, and they don't add much overhead. (Oddly, Rails 2.0 borrowed quite a bit from sexy migrations but didn't include the foreign key stuff.)&lt;br /&gt;&lt;br /&gt;Some databases provide the option to automatically index the columns that are foreign keys, saving you the trouble of adding the indexes yourself. (Foreign keys tend to be columns that you'd want to index. Consider fetching a post's comments: &lt;span style="font-family:courier new;"&gt;SELECT * FROM comments WHERE post_id = 1&lt;/span&gt; should use an index on post_id.)&lt;br /&gt;&lt;br /&gt;And of course, without foreign keys how can you generate an ER diagram to hang on the wall for everyone to admire? (Note: I don't do that.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-2167533930539141362?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/vyiiPLiEMyk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/vyiiPLiEMyk/you-should-use-foreign-key-constraints.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">11</thr:total><feedburner:origLink>http://blog.weiskotten.com/2008/01/you-should-use-foreign-key-constraints.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-2460970348625046307</guid><pubDate>Sun, 23 Dec 2007 20:28:00 +0000</pubDate><atom:updated>2007-12-23T21:35:18.394-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">java</category><category domain="http://www.blogger.com/atom/ns#">fluent interface</category><category domain="http://www.blogger.com/atom/ns#">builder</category><category domain="http://www.blogger.com/atom/ns#">immutable</category><title>Fluent Builders</title><description>&lt;span style="font-style: italic;"&gt;(This post is a bit "off the Rails"... I do still think about Java every now and then, like an old girlfriend... a bloated corporate girlfriend who talks a lot without saying much.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've been a fan of the &lt;a href="http://en.wikipedia.org/wiki/Builder_pattern"&gt;Builder pattern&lt;/a&gt; for a while, and appreciate Martin Fowler's &lt;a href="http://martinfowler.com/bliki/FluentInterface.html"&gt;article&lt;/a&gt; on fluent interfaces as well as a &lt;a href="http://timeandmoney.sourceforge.net/"&gt;variety&lt;/a&gt; of &lt;a href="http://stephan.reposita.org/archives/2007/10/17/creating-a-fluent-interface-for-google-collections/"&gt;real-world&lt;/a&gt; &lt;a href="http://www.jmock.org/"&gt;examples&lt;/a&gt;. Combining the two is pretty sexy, especially in a relatively noisy language like Java -- I like to call the combination a "fluent builder".  In many cases, a fluent builder provides a form of &lt;a href="http://en.wikipedia.org/wiki/Keyword_argument"&gt;named parameters&lt;/a&gt;, making code that uses the API very easy to read and understand. Fluent builders are useful in constructing &lt;a href="http://java.sun.com/docs/books/tutorial/essential/concurrency/immutable.html"&gt;immutable objects&lt;/a&gt; (love 'em!) without requiring &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Date.html#constructor_detail"&gt;overloaded/telescoping constructors&lt;/a&gt; with lots of arguments, particularly when some or all of the arguments are of the same type (an obvious use case for named parameters).&lt;br /&gt;&lt;br /&gt;I seem to be in good company. I came across a &lt;a href="http://www.blogger.com/developers.sun.com/learning/javaoneonline/2007/pdf/TS-2689.pdf"&gt;presentation&lt;/a&gt; by &lt;span style="font-weight: bold;"&gt;Josh Bloch&lt;/span&gt; that discusses this as part of his work on a long overdue version of his outstanding book &lt;a href="http://www.amazon.com/Effective-Java-Programming-Language-Guide/dp/0201310058"&gt;Effective Java&lt;/a&gt;. I'd really like to see some of his examples make their way into the JDK, like the HashMap factory.&lt;br /&gt;&lt;br /&gt;As discussed in Bloch's presentation and &lt;a href="http://en.wikipedia.org/wiki/Factory_method_pattern"&gt;elsewhere&lt;/a&gt;, static factory methods are a simple alternative to constructors, but are much more powerful. You can't have more than one constructor with the same set of argument types, but you can have multiple factory methods. You can't return a cached/singleton/pooled instance from a constructor, but you can from a factory method. You can't return an instance of a sub-type from a constructor, but you can from a factory method. Each static factory method can have a meaningful name that contributes to readable, fluent code.&lt;br /&gt;&lt;br /&gt;The point is that static factory methods are an important part of building a flexible fluent interface. You don't always need them, but the cost of writing them is so low, and the potential benefit is so great (even if &lt;a href="http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It"&gt;YAGNI&lt;/a&gt;), you might as well.&lt;br /&gt;&lt;br /&gt;Here's a contrived example of some code that doesn't use a fluent builder (yet):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Car car = new Car();&lt;br /&gt;car.setYear(2007);&lt;br /&gt;car.setMake("Toyota");&lt;br /&gt;car.setModel("Camry");&lt;br /&gt;car.setColor("blue");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is perfectly fine, but we can do better. A car's year, make, and model are certainly not going to change, and for our purposes we'll assume that it will never be repainted. Let's pass these args to the constructor instead of using setters, which allows us to make these attributes immutable.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Car car = new Car(2007, "Toyota", "Camry", "blue");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Much more concise, but it's not going to be easy to remember the order of all of those arguments, especially since there are three Strings. Let's try it now with a fluent builder:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Car car = Car.builder()&lt;br /&gt;   .year(2007)&lt;br /&gt;   .make("Toyota")&lt;br /&gt;   .model("Camry")&lt;br /&gt;   .color("blue")&lt;br /&gt;   .build();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's examine what this code is doing. First, we call the static factory Car.builder() to get our fluent builder object. Then we call methods on the builder object to initialize the year, make, model, and color. It's very clear which fields have which values. The methods are chained together, which isn't necessary (we could cache the builder in a local variable) but is usually preferable because it reduces noise. Finally, we call the builder's build() method, which creates the Car object with the attributes we've given the builder.&lt;br /&gt;&lt;br /&gt;We could put all of the chained method calls (year, make, model, color) on a single line, but it's usually more readable to put each on a separate line. It may be useful to note that we have some leeway when it comes to the order, as long as we finish the chain with a call to the build method (which returns the immutable Car instance).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# It doesn't matter what order we set the values in.&lt;br /&gt;Car car = Car.builder()&lt;br /&gt;   .color("blue")&lt;br /&gt;   .make("Toyota")&lt;br /&gt;   .model("Camry")&lt;br /&gt;   .year(2007)&lt;br /&gt;   .build();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here's the code for the Car and its Builder:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Car {&lt;br /&gt;&lt;br /&gt;  public static class Builder {&lt;br /&gt;      private int year;&lt;br /&gt;      private String make;&lt;br /&gt;        private String model;&lt;br /&gt;        private String color;&lt;br /&gt;&lt;br /&gt;        public Builder year(int year) { this.year = year; return this; }&lt;br /&gt;        public Builder make(String make) { this.make = make; return this; }&lt;br /&gt;        public Builder model(String model) { this.model = model; return this; }&lt;br /&gt;        public Builder color(String color) { this.color = color; return this; }&lt;br /&gt;&lt;br /&gt;        public Car build() {&lt;br /&gt;          return new Car(year, make, model, color);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private final int year;&lt;br /&gt;   private final String make;&lt;br /&gt;   private final String model;&lt;br /&gt;   private final String color;&lt;br /&gt;&lt;br /&gt;   private Car(int year, String make, String model, String color) {&lt;br /&gt;      this.year = year;&lt;br /&gt;        this.make = make;&lt;br /&gt;        this.model = model;&lt;br /&gt;        this.color = color;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static Builder builder() {&lt;br /&gt;      return new Builder();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There is some repetition. The Builder must duplicate all of the fields that it needs to build the Car. There's some potential refactoring, particularly to abstract a simple interface and some generic and reusable behavior, maybe backing it with a HashMap.&lt;br /&gt;&lt;br /&gt;The code is much more complex than just using setters or a constructor. The benefits may not be clear right away, but I think that using the fluent builder provides a much more pleasant, programmer-friendly API in the long run.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-2460970348625046307?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/_DOZF5qRrQw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/_DOZF5qRrQw/fluent-builders.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/12/fluent-builders.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-5020778770197166732</guid><pubDate>Wed, 19 Sep 2007 15:04:00 +0000</pubDate><atom:updated>2007-09-19T19:22:35.548-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ruby</category><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">enterprise</category><title>Rails and the Enterprise</title><description>Ask 100 enterprise developers (who are probably using Java or C#) if Rails is ready for the enterprise. My guess is that 75 will say no, 1 will say yes,  9 will say "I don't know", and the other 15 will ask "What's Rails?"&lt;br /&gt;&lt;br /&gt;Assuming my estimates bear some resemblance to reality, this tells us a couple of things:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A good chunk of enterprise developers have never even heard of Rails, or have heard of it but don't really know what it is.&lt;/li&gt;&lt;li&gt;Many enterprise developers don't know enough about Rails to understand its qualities, weaknesses, and enterprise-readiness.&lt;/li&gt;&lt;li&gt;Many enterprise developers believe that Rails is a toy framework. This is often attributable to DHH's "Build a blog in 15-minutes with scaffolding (WHOOPS!)" &lt;a href="http://media.rubyonrails.org/video/rails_take2_with_sound.mov"&gt;screencast&lt;/a&gt;, which was a great way to generate excitement about a new framework, but didn't demonstrate the depth and breath of Rails.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Very few enterprise developers believe that Rails is capable of solving enterprise problems, or have a religious devotion to the familiar and tend to fear change.&lt;/li&gt;&lt;/ol&gt;I know that Rails &lt;span style="font-style: italic;"&gt;is&lt;/span&gt; being used in the enterprise, which implies that it is ready for at least some enterprise problems. &lt;a href="http://www.thoughtworks.com/"&gt;ThoughtWorks&lt;/a&gt; is certainly leading the pack in this area, and they're making progress with their enterprise clients by running on JRuby, which allows them to deploy Rails apps in a WAR and integrate more easily with legacy Java products. They also built Mingle, an Agile project management tool that is designed for the enterprise (but presumably is easy enough for small teams to use too).&lt;br /&gt;&lt;br /&gt;So why aren't more enterprise projects using Rails? I think one reason is that Rails has only been around for about 3 years, and enterprises are notoriously slow at adopting new technology, especially radically new platforms like Rails. There are a lot of enterprise Java shops still using JDK 1.4 or 1.3! (If you're one of them, my condolences.) It's often a struggle just to introduce a new dependency on an open source project -- something as small as Apache Commons to something as big as Hibernate. Lawyers are called in to examine licenses. Security auditing may be done. Integration concerns are brought up. And then you have to actually teach dozens or hundreds of developers how to use it.&lt;br /&gt;&lt;br /&gt;Another reason for the lack of enterprise adoption is the FUD around Ruby's and Rails' performance, scalability, and maintainability. Sound familiar? These are the same arguments that dogged Java for years, and still do today in some circles. Religious debates abound between static vs dynamic typing, compiled vs interpreted, explicit vs meta-programming magic.&lt;br /&gt;&lt;br /&gt;I think the answer is that it depends on the enterprise, and it depends on the problems. (Answers that begin with "it depends" sound like a cop-out, but in this case I think it's true.) A good craftsman uses the right tool for the job. Sometimes Java is the right tool, and low-level JDBC is needed to integrate with legacy databases. Sometimes you really need great Unicode support, and I agree that Rails is really lacking there. But there are plenty of cases where a greenfield project needs be produced quickly featuring all the latest hotness (Ajax, REST, and the rest), or a legacy database can be migrated into something Rails-friendly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-5020778770197166732?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/lwOjqlx-Z10" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/lwOjqlx-Z10/rails-and-enterprise.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">11</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/rails-and-enterprise.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-246820642900727665</guid><pubDate>Sat, 15 Sep 2007 03:24:00 +0000</pubDate><atom:updated>2007-09-15T00:08:31.069-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ruby</category><title>Jay Fields: Array#collect_with_remaining</title><description>Jay Fields posted &lt;a href="http://blog.jayfields.com/2007/09/ruby-arraycollectwithremaining.html"&gt;another interesting Ruby example&lt;/a&gt; and a call for alternate solutions. (Check his post out for problem details.) After some digging in the Ruby API docs and a lot of experimenting in tests and irb, here's my offering:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def collect_with_remaining&lt;br /&gt;  enum_with_index.inject([]) do |result, (element, index)|&lt;br /&gt;    result &amp;lt;&amp;lt; yield(element, values_at(*(0...size).to_a - [index]))&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I took the opportunity to learn about #enum_with_index, particularly how it's used in conjunction with #inject. I also learned how the asterisk prefix expands an Array into parameters -- I've seen this before somewhere (Rails source?) but never really understood what it was doing. It came in handy here, because I had an array but #values_at only takes Ranges or indices as raw parameters.&lt;br /&gt;&lt;br /&gt;So how this method works...&lt;br /&gt;&lt;ol&gt;&lt;li&gt; In order to get the elements of the array other than "element" at "index", we create a range of every index in the array, &lt;span style="font-family: courier new;"&gt;(0...size)&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;We convert this range to an array (&lt;span style="font-family: courier new;"&gt;#to_a&lt;/span&gt;) and we remove the index of the current element (&lt;span style="font-family: courier new;"&gt; - [index]&lt;/span&gt;). This gives us an array of every index but the current element's.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;We send the elements in this array to &lt;span style="font-family: courier new;"&gt;#values_at&lt;/span&gt;, thanks to the aforementioned asterisk notation, and we get ba all of the elements except for the current one.&lt;/li&gt;&lt;/ol&gt; One thing I don't like about this solution is that it creates the same range of indexes and converts it to the same array of indexes for each element. If I did some profiling and weren't happy with performance, I would pull that code out of the block so that it's only done once, and would only add one line of code doing so.&lt;br /&gt;&lt;br /&gt;Other than that, I think this example really benefits from the elegance afforded by Ruby, not to mention the powerful features of some core APIs. It's terse but doesn't seem &lt;span style="font-style: italic;"&gt;too&lt;/span&gt; obtuse or obscure; while it uses some relatively advanced Ruby, it shouldn't be too hard to figure out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-246820642900727665?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/wQB-q1yIY70" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/wQB-q1yIY70/jay-fields-arraycollectwithremaining.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/jay-fields-arraycollectwithremaining.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-7300742378234197235</guid><pubDate>Fri, 14 Sep 2007 12:47:00 +0000</pubDate><atom:updated>2007-12-21T19:05:39.579-05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">job title</category><category domain="http://www.blogger.com/atom/ns#">careers</category><category domain="http://www.blogger.com/atom/ns#">PatientsLikeMe</category><category domain="http://www.blogger.com/atom/ns#">jobs</category><title>Job Title</title><description>What do we call people who create software? Here's a list off the top of my head of common titles, either official or self-applied within the industry, in no particular order:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;computer programmer&lt;/li&gt;&lt;li&gt;software engineer&lt;/li&gt;&lt;li&gt;software designer&lt;br /&gt;&lt;/li&gt;&lt;li&gt;software architect&lt;/li&gt;&lt;li&gt;(software/application/solutions/&lt;span style="font-style: italic;"&gt;language or framework name&lt;/span&gt;) developer&lt;/li&gt;&lt;li&gt;coder&lt;/li&gt;&lt;li&gt;hacker&lt;/li&gt;&lt;li&gt;technologist&lt;/li&gt;&lt;li&gt;geek/ninja/pirate&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;One could argue that there are distinctions between some of these titles based on experience, education, role, or skills in the good old-fashioned art of bullshitting. I'm sure there is some nuance, the sore thumb being "architect" which is typically reserved for the top-tier, who often find themselves acting more as a project manager than a hands-on creator of software.&lt;br /&gt;&lt;br /&gt;Some of these titles can be modified with adjectives to imply experience, or lack thereof; for example: "junior", "lead", "senior", "principal", "distinguished", or "chief". Sometimes titles are used to narrow or expand the role, like "Junior UI Developer" or "Senior Systems Architect".&lt;br /&gt;&lt;br /&gt;So what do I call myself? My &lt;a href="http://www.patientslikeme.com"&gt;company&lt;/a&gt; is currently filling a spot for a &lt;a href="http://www.patientslikeme.com/about/careers/1-senior-application-developer"&gt;Senior Application Developer&lt;/a&gt;, which is basically the role I'm in, but my official title is "Senior Software Engineer". When people ask, I just tell them I'm a software engineer (without the "senior" modifier so I don't come off as a pompous dork) -- and if they don't know what that means, I tell them I'm a computer programmer.&lt;br /&gt;&lt;br /&gt;Ultimately, I don't care what my title is, as long as I'm fairly compensated, have plenty of interesting problems to solve, and have the opportunity to learn and teach (often at the same time). I think most people, regardless of title or industry, feel the same.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-7300742378234197235?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/IWwbMgLwNvI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/IWwbMgLwNvI/job-title.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/job-title.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-7124189306949419627</guid><pubDate>Thu, 13 Sep 2007 16:12:00 +0000</pubDate><atom:updated>2007-09-13T14:08:23.838-04:00</atom:updated><title>First Class</title><description>We have a collection of items that we're semantically marking up as an unordered list of links. The catch is that the first item should be styled differently, by having a CSS class, than the rest of the items (which have no specific class).&lt;br /&gt;&lt;br /&gt;We can do this using &lt;span style="font-style: italic;"&gt;each_with_index&lt;/span&gt; and testing for &lt;span style="font-style: italic;"&gt;index == 0&lt;/span&gt; to determine what class to use. Or we can pass the &lt;span style="font-style: italic;"&gt;:collection&lt;/span&gt; to &lt;span style="font-style: italic;"&gt;render :partial&lt;/span&gt; and use a &lt;span style="font-style: italic;"&gt;:spacer_template&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Here's another approach, which doesn't need an index and keeps the whole thing all in one place:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;ul&amp;gt;&lt;br /&gt;&amp;lt;li class="first"&amp;gt;&lt;br /&gt;&amp;lt;%= items.map { |item| link_to item.name, :action =&amp;gt; 'show' }.join('&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;') %&amp;gt;&lt;br /&gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This should be extracted into a helper method to hide the complexity (the calls to #map and #join), especially if something like this will be done in more than one place:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def list_items(items, first_class, others_class='') &lt;br /&gt;  first_style = " class=\"#{first_class}\"" unless first_class.blank?&lt;br /&gt;  others_style = " class=\"#{others_class}\"" unless others_class.blank?&lt;br /&gt;  sep = "&amp;lt;li#{others_style}&amp;gt;"&lt;br /&gt;  "&amp;lt;li#{first_style}&amp;gt;#{items.map { |item| yield(item) }.join(sep)}"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&amp;lt;ul&amp;gt;&lt;br /&gt;&amp;lt;%= list_items (people, 'head_of_household') { |person| link_to person.name, :action =&amp;gt; 'show' } %&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-7124189306949419627?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/B5zE9mhVWzs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/B5zE9mhVWzs/first-class.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/first-class.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-700986088910789052</guid><pubDate>Wed, 12 Sep 2007 15:36:00 +0000</pubDate><atom:updated>2007-09-12T13:36:48.866-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">sql</category><category domain="http://www.blogger.com/atom/ns#">surrogate keys</category><category domain="http://www.blogger.com/atom/ns#">database</category><title>I heart surrogate keys</title><description>Brad Hudson &lt;a href="http://204nocontent.blogspot.com/2007/09/database-rant-surrogate-keys.html"&gt;argues against&lt;/a&gt; surrogate keys in favor of using natural keys. (His main point is in regards to what he calls "relationship tables", a.k.a. a table that forms a many-to-many relationship between two others, but I want to discuss the topic in a broader sense.)&lt;br /&gt;&lt;br /&gt;I'm not a DBA by any means, but I have a lot of (over 10 years) experience designing relational schemas and using them with a variety of languages and ORM strategies.&lt;br /&gt;&lt;br /&gt;I wouldn't want a database that doesn't use surrogate keys -- particularly integer IDs.&lt;br /&gt;&lt;br /&gt;First, some basic definitions behind the discussion.&lt;br /&gt;&lt;br /&gt;A "key" is the attribute or set of attributes (columns) that uniquely identifies an entity (a row).&lt;br /&gt;&lt;br /&gt;A "natural key" is an attribute that has some business or domain value; for example, an ISBN number identifies a book.&lt;br /&gt;&lt;br /&gt;A "composite" or "composite natural key" is a set of attributes that have business or domain value that together identify an entity; for example, the name of a city and the state you'd find it in identify a city (you need the state to distinguish between Springfield, Illinois, and Springfield, Massachusetts).&lt;br /&gt;&lt;br /&gt;A "surrogate key" is an attribute that does not have business or domain value, and only exists for the purposes of identifying an entity. Also commonly called an "id" or "sequence number", surrogate keys are usually a numeric/integer type, but may be a GUID or other more complex type.&lt;br /&gt;&lt;br /&gt;There is just no way, absolutely none, that a natural key can be more time-efficient than a surrogate key composed of a single integer. The only way it can be AS efficient is if your natural key happens to be a single integer (which is rare in my experience). I don't know of any other data type that can be indexed/looked up as efficiently. Composite natural keys, which usually include at least one varchar, just can't be as fast.&lt;br /&gt;&lt;br /&gt;Surrogate keys do require an extra column per table, typically a 4- or 8-byte integer, in addition to the natural key columns. At first this sounds like it's less space-efficient -- an extra column with no business value? However, keep in mind that this is a relational database -- foreign keys would otherwise have to be defined by duplicating the entire natural key, consuming much more space for no better performance. Surrogate keys, on the other hand, make for simple foreign keys.&lt;br /&gt;&lt;br /&gt;Sometimes it is useful for performance or convenience to duplicate (denormalize) the natural key even when a surrogate is used, but wiring your relationships through a simple integer column is significantly easier to use and maps much more easily to an object-oriented model. Modern ORM tools like Hibernate and Rails' ActiveRecord do a good job of eager fetching with complex JOIN queries to prevent n+1 SELECTs, so denormalization often isn't necessary.&lt;br /&gt;&lt;br /&gt;I haven't even gotten into cases where a table doesn't have an obvious natural key, or natural keys that don't change 99% of the time but are hell when they do. A good example of this is social security numbers -- they can change!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-700986088910789052?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/ILdMpNtyCs8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/ILdMpNtyCs8/i-heart-surrogate-keys.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/i-heart-surrogate-keys.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-6747695053876038760</guid><pubDate>Tue, 11 Sep 2007 15:09:00 +0000</pubDate><atom:updated>2007-09-11T11:21:21.778-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ruby</category><title>Jay Fields: Array#chunk</title><description>Jay Fields of ThoughtWorks posted an &lt;a href="http://blog.jayfields.com/2007/09/ruby-arraychunk.html"&gt;interesting problem&lt;/a&gt;: break an Array up into a given number of "chunks". He provides an example implementation and unit tests that define the expected behavior.&lt;br /&gt;&lt;br /&gt;I posted my solution in a comment, but here it is again (I really need to figure out how to get syntax coloring...):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Array&lt;br /&gt;  def chunk(number_of_chunks)&lt;br /&gt;    chunks = (1..number_of_chunks).collect { [] }&lt;br /&gt;    self.each_with_index do |item, index|&lt;br /&gt;      chunks[index % number_of_chunks] &amp;lt;&amp;lt; item&lt;br /&gt;    end&lt;br /&gt;    chunks&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  alias / chunk&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;My solution is a little different than his. First, mine doesn't modify the contents of the Array; his does. Since his does, the method should really be named "chunk!", that exclamation point being the convention that indicates that it mutates the instance on which it is called. The unit tests don't enforce one or the other, so I decided to follow convention.&lt;br /&gt;&lt;br /&gt;Second, I've got one loop, which is over the Array being chunked. He's got a nested loop, which I think increases the complexity quite a bit.&lt;br /&gt;&lt;br /&gt;There's a little bit of cleverness in my solution. I figure out which chunk to add the item to using the % modulus operator. This may not be a very common idiom, but it works and you don't need a separate counter with boundary testing to cycle back to zero.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-6747695053876038760?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/4yLRyhReGKM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/4yLRyhReGKM/jay-fields-arraychunk.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/jay-fields-arraychunk.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-1214982263960630911</guid><pubDate>Mon, 10 Sep 2007 01:21:00 +0000</pubDate><atom:updated>2007-09-09T21:37:52.175-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">wisdom of crowds</category><category domain="http://www.blogger.com/atom/ns#">social network</category><category domain="http://www.blogger.com/atom/ns#">fantasy football</category><title>The Wisdom of Crowds: Fantasy Football Edition</title><description>Every fantasy football team owner/manager has a weekly, daily, or even minutely ritual of analyzing, researching, scrutinizing, guessing, second-guessing, and finally deciding which of their players they'll put the fate of their Tuesday-morning happiness on. There are lots of resources available to dig through -- sports magazines and websites, fantasy-focused magazines and websites, pre-game analysts on the TV, the guy two cubicles over. These resources all have one thing in common: they rely on the wisdom of a handful of "experts" to give you the inside scoop.&lt;br /&gt;&lt;br /&gt;The problem is that these experts are often wrong, or by the time you get the information it's out of date.&lt;br /&gt;&lt;br /&gt;My fantasy football league is run on Yahoo, which provides good enough tools for us amateurs. Every year the geniuses at the Y! give us cool new features. One of this year's additions is called "Sit or Start", and is Yahoo's attempt to introduce wisdom of the crowd to fantasy sports. Basically players across all of Yahoo's leagues can vote for who to sit and who to start. If you choose to vote, you're presented with two players (same position) and you vote for one the other (to start). Then you're presented with two more, and you repeat until you get bored, which in my experience might be an hour or two later.&lt;br /&gt;&lt;br /&gt;The end result is that thousands of players just like you weigh in with their varying levels of expert-opinion. Like other "wisdom of crowds" experiments (see almost any social network) it's debatable whether the information derived this way is any more accurate or reliable than that provided by experts, but I think it's a great tool. If nothing else, it's one more bit of data that I can use to micromanage my team.&lt;br /&gt;&lt;br /&gt;On a side note, I'm kicking ass in week 1!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-1214982263960630911?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/iLuugL1ttgc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/iLuugL1ttgc/wisdom-of-crowds-fantasy-football.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/wisdom-of-crowds-fantasy-football.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-4700955603058075714</guid><pubDate>Thu, 06 Sep 2007 21:11:00 +0000</pubDate><atom:updated>2007-09-06T17:29:46.873-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">waterfall</category><category domain="http://www.blogger.com/atom/ns#">agile</category><category domain="http://www.blogger.com/atom/ns#">encapsulation</category><category domain="http://www.blogger.com/atom/ns#">tdd</category><category domain="http://www.blogger.com/atom/ns#">testing</category><title>The Forgotten Benefit of Unit Testing</title><description>&lt;a href="http://www.deaded.com/staticpages/index.php/whywritetestcode"&gt;Why Write Test Code&lt;/a&gt; is a good overview of some of the benefits of writing automated tests, including unit tests. There's another benefit that's frequently forgotten when people discuss this topic.&lt;br /&gt;&lt;br /&gt;High cohesion and low coupling. Better known as &lt;span style="font-style: italic;"&gt;good encapsulation&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Unit tested code is often designed (in a pure test-first TDD process) or refactored into well-defined classes that take responsibility for their own state and behavior. This is necessary in order to isolate the class (or unit) under test, giving components better-defined interfaces and minimizing dependencies between collaborators. Otherwise your tests tend to be complicated to set up, slow, and brittle -- harder to create and harder to maintain.&lt;br /&gt;&lt;br /&gt;So unit testing isn't just about testing, it's about &lt;span style="font-style: italic;"&gt;code design&lt;/span&gt;, and at some level it's about architecture. Testing considerations drive small (micro) design decisions which inevitably influence larger (macro) design decisions. I think this is a key difference between the "Agile" and "waterfall" design processes. Agile projects are designed from within based on evolving requirements, from the micro to the macro, where waterfall projects are designed more from the "big picture" by Man Behind the Curtain, cascading down into the minutia.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-4700955603058075714?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/DQWtaVxVpOg" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/DQWtaVxVpOg/forgotten-benefit-of-unit-testing.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/forgotten-benefit-of-unit-testing.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-4871298596387167824</guid><pubDate>Thu, 06 Sep 2007 19:36:00 +0000</pubDate><atom:updated>2007-09-06T15:44:54.539-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">competition</category><category domain="http://www.blogger.com/atom/ns#">rails</category><title>Rails Rumble 2007</title><description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_Gdc6BUabwRo/RuBW94WE4tI/AAAAAAAABEw/r9VCn3nRTHA/s1600-h/railsrumble_yellow_250.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://bp1.blogger.com/_Gdc6BUabwRo/RuBW94WE4tI/AAAAAAAABEw/r9VCn3nRTHA/s400/railsrumble_yellow_250.png" alt="" id="BLOGGER_PHOTO_ID_5107177598535131858" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span class="on" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;I wish I had learned about &lt;a href="http://railsrumble.com/"&gt;Rails Rumble&lt;/a&gt; sooner! Wishful thinking: I might have been able to make time to participate &amp;amp; put a team together.&lt;br /&gt;&lt;br /&gt;Maybe I'll at least get to judge some cool, innovative Rails apps. I look forward to seeing what comes out of this competition -- if this year's results aren't too intimidating, I'll try to get a team together next year!&lt;/span&gt;&lt;br /&gt;Don't miss the &lt;a href="http://giantrobots.thoughtbot.com/2007/9/6/let-s-get-ready-to-rumble"&gt;title belt&lt;/a&gt; supplied by &lt;a href="http://thoughtbot.com/"&gt;thoughtbot&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-4871298596387167824?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/0y-WKS-59O4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/0y-WKS-59O4/rails-rumble-2007.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp1.blogger.com/_Gdc6BUabwRo/RuBW94WE4tI/AAAAAAAABEw/r9VCn3nRTHA/s72-c/railsrumble_yellow_250.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/rails-rumble-2007.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-8491998227488841371</guid><pubDate>Thu, 06 Sep 2007 17:18:00 +0000</pubDate><atom:updated>2007-09-06T17:08:23.957-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">build</category><category domain="http://www.blogger.com/atom/ns#">automation</category><category domain="http://www.blogger.com/atom/ns#">continuous integration</category><title>On Continuous Integration</title><description>&lt;span style="font-style: italic;"&gt;"Nightly builds are nice, but may be overkill for most teams."&lt;/span&gt;&lt;br /&gt;- Isaac Sacolick, &lt;span style="font-size:100%;"&gt;&lt;a style="font-style: italic;" href="http://ctotodevelopers.blogspot.com/"&gt;Best Practices for Software Developers&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There's a lot of truth in this article, but I disagree with Isaac's final point.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Nightly builds may be overkill, but &lt;span style="font-style: italic;"&gt;only&lt;/span&gt; if you are building frequently; that is, practicing &lt;a href="http://www.martinfowler.com/articles/continuousIntegration.html"&gt;continuous integration&lt;/a&gt;. I've heard first-hand of teams that build (on an isolated, non-development box) only once a week, or even once a month. These delayed builds rarely succeed without significant human intervention, turning what should be a simple process into hours or days of scrambling, wasting valuable development hours on integration.&lt;br /&gt;&lt;br /&gt;Ideally a build would occur automatically whenever someone checks in code. If that's not feasible (because the build takes too long), there are some options:&lt;br /&gt;&lt;br /&gt;1. Make the build faster if you can. &lt;span style="font-weight: bold;"&gt;Optimize&lt;/span&gt; it like you would any other software issue -- identify and remove the biggest bottleneck, retest, repeat until it's &lt;span style="font-style: italic;"&gt;fast enough&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;2. &lt;span style="font-weight: bold;"&gt;Reduce&lt;/span&gt; your build process to a minimal set of tasks to verify the code. If you're working in a compiled language like C++ or Java, compile the code. If you're doing any unit testing, automate the tests. Things that change less frequently, like install/deployment scripts, may not need to be exercised as frequently.&lt;br /&gt;&lt;br /&gt;3. If you've optimized and reduced as much as you can and it's still not fast enough to build at every check-in, build at &lt;span style="font-weight: bold;"&gt;frequent intervals&lt;/span&gt; throughout the day. Every 10 minutes, every hour, every 4 hours -- whatever you can afford, but the more frequently the better.&lt;br /&gt;&lt;br /&gt;It's also important to note that a broken build is not the end of the world (at least you know the system works!) but it should be fixed ASAP. No one should updated from or commit to a repository that can't be built, and development more or less halts until the build is fixed. If your team is integrating and building continuously, most build issues are relatively easy and quick to fix ("oh, I forgot to add this new file...").&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-8491998227488841371?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/oiP5sKDFPWI" height="1" width="1"/&gt;</description><enclosure type="text/html" url="http://ctotodevelopers.blogspot.com/2007/09/top-10-tips-on-version-control-for.html" length="0" /><link>http://feedproxy.google.com/~r/JerOnRails/~3/oiP5sKDFPWI/on-continuous-integration.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/on-continuous-integration.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-8489725924508991204</guid><pubDate>Thu, 06 Sep 2007 15:58:00 +0000</pubDate><atom:updated>2007-09-06T12:21:34.315-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ruby</category><category domain="http://www.blogger.com/atom/ns#">PatientsLikeMe</category><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">jobs</category><title>PatientsLikeMe is Hiring!</title><description>&lt;a href="http://www.patientslikeme.com/"&gt;PatientsLikeMe&lt;/a&gt; is looking for some &lt;span style="font-weight: bold;"&gt;smart&lt;/span&gt; people. We're a small, well-funded startup in Cambridge, Massachusetts, building communities (aka "social networks") and tools for people with challenging, life-changing diseases and conditions.&lt;br /&gt;&lt;br /&gt;PatientsLikeMe was recently &lt;a href="http://money.cnn.com/galleries/2007/biz2/0708/gallery.next_disruptors.biz2/7.html"&gt;featured&lt;/a&gt; in Business 2.0 magazine as a &lt;span style="font-weight: bold;"&gt;disruptor that will change the world&lt;/span&gt;. We currently support ALS (Lou Gehrig's disease), multiple sclerosis, and Parkinson's disease, and we're close to rolling out support for HIV.&lt;br /&gt;&lt;br /&gt;We're growing fast and our talented (and very busy) team needs some help. We're looking for a &lt;a href="http://jobs.37signals.com/jobs/2125"&gt;Junior Web Designer/UI Engineer&lt;/a&gt; and a &lt;a href="http://jobs.37signals.com/jobs/2126"&gt;Senior Application Developer&lt;/a&gt; who has or &lt;span style="font-style: italic;"&gt;really really&lt;/span&gt; wants some experience with Ruby on Rails.&lt;br /&gt;&lt;br /&gt;Feel free to &lt;a href="mailto:jweiskotten@patientslikeme.com"&gt;contact me&lt;/a&gt; if you'd like more information, a developer's perspective, or to submit your resume.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-8489725924508991204?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/iDyMOAjLI8U" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/iDyMOAjLI8U/patientslikeme-is-hiring.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/09/patientslikeme-is-hiring.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-4195233892247732354</guid><pubDate>Thu, 23 Aug 2007 13:36:00 +0000</pubDate><atom:updated>2007-08-23T09:42:04.469-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">encapsulation</category><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><title>Fat Models</title><description>Unlike the fashion world, we Rails geeks prefer &lt;a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model"&gt;fat models&lt;/a&gt;. When we see Kirstie Alley we say, “That’s what I’m talkin’ about.”&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_Gdc6BUabwRo/Rs2NcoWE4sI/AAAAAAAABD4/Zm6roxPYAE8/s1600-h/kip.png"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_Gdc6BUabwRo/Rs2NcoWE4sI/AAAAAAAABD4/Zm6roxPYAE8/s400/kip.png" alt="" id="BLOGGER_PHOTO_ID_5101889475886506690" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-style: italic;"&gt;We all know that Kip only has eyes for Lafawnduh.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;A fat model, um, carries its weight. Not slacking off, it does everything it’s capable of doing for itself. This makes a fat model responsible for itself. It’s responsible for managing its own associations; for wiring its dependent objects to itself. It’s responsible for any business logic that it should be taking care of, including validation of its state. A fat model is more likely to be reusable; at a minimum it will be easier to use. Fat models are not deadbeats.&lt;br /&gt;&lt;br /&gt;Fat models, consuming all that responsibility for themselves, result in skinny controllers. The role of a controller is to drive the application flow, not baby-sit the model. Controllers respond to input, use the model to get state and behavior, and render the right responses. When the controller is doing work that the model should be taking care of itself, it’s much harder to reuse that behavior.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Encapsulation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To any experienced OO advocate this sounds like basic encapsulation, and it is. In an MVC world, it sounds like basic &lt;a href="http://en.wikipedia.org/wiki/Separation_of_concerns"&gt;separation of concerns&lt;/a&gt;, and it is. Fat models encapsulate their state (typically what’s stored in the database) and behavior (business logic, relationships, validation). However, I think it bears repeating for a couple of reasons:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It’s easy, and therefore tempting, to hack some business logic into a controller, even &lt;gasp&gt; a view. Not even Rails, which embraces MVC like few other frameworks, can do much to prevent laziness and sloppiness.&lt;br /&gt;&lt;br /&gt;&lt;/gasp&gt;&lt;/li&gt;&lt;li&gt;&lt;gasp&gt;Not everyone is an experienced OO advocate.&lt;/gasp&gt;&lt;/li&gt;&lt;/ol&gt;&lt;gasp&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Benefits&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Besides reusability, another benefit of fat models, and encapsulation in general, is testability. It’s easier to write a unit test for a model than to write a functional test for a controller. It’s just less work; there are fewer dependencies/collaborators/fingers in the pie, and you can test the model in relative isolation. You’ll still want to write a functional test for every controller, but you should have to test little more than the responses to different requests, not the business logic, because business logic is covered by your simpler unit tests.&lt;br /&gt;&lt;br /&gt;You’ll also find that your controllers, where much of the complicated interaction between the crazy user and your application is handled, are much simpler. They’ll be easier to read and maintain, which at least as important as how easy they were to create. When responsibilities are focused, noise is reduced, and we’re more capable of understanding intent. That makes us better at identifying patterns and where we can improve design by refactoring away repetition.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/gasp&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-4195233892247732354?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/UHgxWyIWoGo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/UHgxWyIWoGo/fat-models.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp1.blogger.com/_Gdc6BUabwRo/Rs2NcoWE4sI/AAAAAAAABD4/Zm6roxPYAE8/s72-c/kip.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/08/fat-models.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-8304514261258266814</guid><pubDate>Wed, 22 Aug 2007 21:22:00 +0000</pubDate><atom:updated>2007-08-22T17:38:32.148-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ruby</category><title>Lesser of two weebles</title><description>In Java, if you want to determine the lesser of two numbers, you can do this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;min = (x &lt; y)? x : y;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: georgia;"&gt;...which is shorthand for:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;if (x &lt; y) {&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  min = x;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;} else {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-family:courier new;"&gt;  min = y;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: georgia;"&gt;For better code clarity, you can use the overloaded static utility methods in the Math class:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;min = Math.min(x, y);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: georgia;"&gt;In Ruby, you can do it with similar longhand:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;min = (x &lt; y) ? x : y&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# or&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;if x &lt; y&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  min = x&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;else&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  min = y&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: georgia;"&gt;Ruby's Math class doesn't have min/max utility methods. Instead, you contain the values in an array and use Array#min/max:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;min = [x, y].min&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: georgia;"&gt;I think that's pretty terse and elegant. I suppose you could complain about the allocation of an extra array, but memory is cheap and fast.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-8304514261258266814?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/oLLaXWr2rc0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/oLLaXWr2rc0/lesser-of-two-weebles.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/08/lesser-of-two-weebles.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37875365.post-4449360876292620301</guid><pubDate>Wed, 22 Aug 2007 00:37:00 +0000</pubDate><atom:updated>2007-08-21T21:04:28.612-04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ruby</category><category domain="http://www.blogger.com/atom/ns#">rails</category><category domain="http://www.blogger.com/atom/ns#">date</category><title>Days and Seconds</title><description>I've been bit more than once by something like this bit of Ruby (with Rails' ActiveSupport extensions to Core):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;yesterday = Date.today - 1.day&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Seems innocent enough. We're going to subtract one day from today to get yesterday's date. But as of today, August 21, 2007, this code tells us that yesterday is 1771-01-30. 19th Century: I hardly knew ya.&lt;br /&gt;&lt;br /&gt;Obviously 1.day isn't a day. In fact, it's the number of seconds in a day, which happens to be 86400. Go ahead, multiple 24 * 60 * 60 if you don't believe me. The Date#- (minus) operator expects, sanely, a number of days. So we're subtracting 86400 days. (Time#- expects seconds, but can't go back too far in time, so it's not always usable.)&lt;br /&gt;&lt;br /&gt;Here's the (obvious) answer:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;yesterday = Date.today - 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If I'm dealing with a larger number of days, say 3 years, I could multiple 365 (ignoring leap year rounding issues) by 3:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;ye_good_old_days = Date.today - (365 * 3)&lt;br /&gt;# or I do the math...&lt;br /&gt;ye_good_old_days = Date.today - 1095 #3 years&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here's another way, which is a little more verbose, but also a little more expressive:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;ye_good_old_days = Date.today - (3.years / 1.day)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This gets the number of seconds in 3 years, then divides by the number of seconds in a day, to get the number of days in 3 years. I can take it or leave it.&lt;br /&gt;&lt;br /&gt;Thoughts?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37875365-4449360876292620301?l=blog.weiskotten.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JerOnRails/~4/zL390wHpizU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/JerOnRails/~3/zL390wHpizU/days-and-seconds.html</link><author>noreply@blogger.com (Jeremy Weiskotten)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.weiskotten.com/2007/08/days-and-seconds.html</feedburner:origLink></item></channel></rss>
