Close and Go BackBack to Viget

Tips and Tricks

Rails 2 and test/spec

Clinton R. Nixon
Clinton R. Nixon, Senior Developer, March 24, 2008 1

We’ve been flirting with behavior-driven development here at the Labs recently, and have tried out RSpec and test/spec. Both have advantages, but I like test/spec a little more: it works well with existing Test::Unit tests and has a syntax I find more natural.

Somewhere along the path to Rails 2, test/spec stopped working well for me. New test classes — ActiveSupport::TestCase, ActionController::TestCase, and ActionMailer::TestCase — were introduced to eliminate repeated code in Rails tests, and test/spec classes, which inherit from Test::Unit::TestCase, suddenly didn’t transparently work.

Working on a personal project this weekend, I decided to figure out how to use test/spec again. Digging through its code, I found this gem:

def context(name, superclass=Test::Unit::TestCase, klass=Test::Spec::TestCase, &block)
  (Test::Spec::CONTEXTS[self.name + "\t" + name] ||= klass.new(name, self, superclass)).add(&block)
end

So now, if I want to use test/spec in my Rails tests, I just put the superclass after the context name, like so:

# For models
context "User", ActiveSupport::TestCase
  ...
end

# For controllers
context "User Controller", ActionController::TestCase
  ...
end

# For mailers
context "User Mailer", ActionMailer::TestCase
  ...
end

No Query String? No Problem.

Patrick Reagan
Patrick Reagan, Development Director, March 19, 2008 5

When rewriting URLs on your site with mod_rewrite, you’ll typically want to preserve the query string while redirecting your users to the new location.  You can accomplish this by either clobbering the existing query string:

RewriteRule ^category/tech/?$ http://www.viget.com/extend/ [R=301,L]

Or appending to an existing one (with the QSA option):

RewriteRule ^category/tech/?$ http://www.viget.com/extend/ [QSA,R=301,L]

Both of these rules assume that you want to preserve the query string, but what if you want to remove it entirely?  We needed to do exactly that when moving the existing Viget site over to its new incarnation.  Instead of our news items having antiquated ID-based URLs, each post now has a nice SEO-friendly “slug.” A quick attempt at redirecting one of these news items looked like this:

RewriteCond %{QUERY_STRING} ^id=1889
RewriteRule ^news-detail.html$ http://www.viget.com/blog/nixon_reagan_williams_featured_in_computerworld/ [R=301,L]

If we dissect this, the rule states: for a query string containing ‘id=1889’, permanently redirect the ‘news-detail.html’ URL to this new one and don’t process any other rules.  When it came time to test it out, it mostly worked, but would append ?id=1899 to the URL.  Even though we didn’t specify how to handle query strings, the original was added to the end of the URL – something we definitely didn’t want.  The official mod_rewrite documentation yielded this tip:

By default, the query string is passed through unchanged. You can, however, create URLs in the substitution string containing a query string part. Simply use a question mark inside the substitution string to indicate that the following text should be re-injected into the query string. When you want to erase an existing query string, end the substitution string with just a question mark. To combine new and old query strings, use the [QSA] flag.

After simply adding a question mark to the rewritten URL, we now had the desired behavior:

RewriteCond %{QUERY_STRING} ^id=1889
RewriteRule ^news-detail.html$ http://www.viget.com/blog/nixon_reagan_williams_featured_in_computerworld/? [R=301,L]

Try it out for yourself.

Migration Consolidation for Fun and Profit

Ben Scofield
Ben Scofield, Development Director, March 18, 2008 4

Migrations are a huge step forward for many developers new to Rails – versioning the database is not all that common in, say, PHP. Over time, however, migrations can get unwieldy – as you accumulate more and more they can slow down, and early migrations can create conflicts with later code changes.

We’ve been experimenting with one approach to deal with this with some success of late: consolidation. Basically, we develop with migrations normally, creating (for example) files numbered 001-010. Once the iteration is solid, we then push everything out to stage for testing, and assuming that passes we push it all to production. After the production release, we then consolidate the existing migrations into a single file that has the same number as the last migration file. In the example already mentioned, we’d create 010_consolidated_migration_for_[date].rb. All of the individual changes in the migrations get rolled up into the consolidated file, so that it represents a single step to initialize a new database.

Subsequent iterations work much the same – new migrations are created in development on top of the consolidated migration (say, 011-015). Once they’re tested and released, we consolidated again, merging 010-015 into a new 015 file.

There’s an obvious downside here – rolling back in production is made much more difficult after a consolidation. For our processes, that’s not a huge problem, since we place a high priority on never rolling back the production DB. You could minimize the risk, however, by keeping a rolling window of consolidated migrations (so you’d have two or three consolidated files on production, each with the appropriate self.down methods). Even then you wouldn’t need to keep too many files, however, since you should rarely need to roll back more than one release. 

Campfire Culture

Patrick Reagan
Patrick Reagan, Development Director, June 27, 2007 3

By now you’ve heard the news that we’re growing our team and branching out into the Durham, NC area.  This is potentially a big challenge for us as we strive to maintain constant communication within our project teams and between staff across our two locations.  While we’re still deciding on whether to use iChat or Skype for our daily Scrum meetings, we’ve settled on Campfire to stay in contact while we’re cranking out functionality for our latest app.

Our initial solution was to use an internal IRC server to keep the development team up-to-date and provide a forum for questions.  We had mixed success with this approach and knew that it wouldn’t work if we tried to roll it out to the rest of the company for a few reasons:


  • Geek factor – Sure, we cut our teeth on IRC back in our college dorms, but that’s no reason to force our non-technical employees to /navigate the sea of commands.  If we wanted company-wide adoption, we needed a more user-friendly solution.

  • Some assembly required – Since we hosted the IRC server internally, it was firewalled off from anyone outside the office.  This required everyone in the Durham office to connect to the VPN to chat with the rest of us in DC - not fun!

  • History – There are ways to log history in IRC and provide a searchable interface, but we’re too busy to bother with setting that up.  We needed something that worked “out of the box” to give joining users insight into what happened throughout the day.

We were skeptical at first, but after a month of constant use Campfire has proven to be a better tool than we initially anticipated:

  • Communication has improved – Instead of just the core development team, we now have everyone involved in discussions throughout the day.  Project Managers can alert us of new issues that crop up in production (that we don’t see in our exception notifications) and Developers can get clarification on a proposed feature.

  • Cool bots – When they’re not hanging out in the VL “Bot Tub,” our Capistrano and Subversion bots send messages to the team whenever a deployment or commit happens.

  • Context included – When people sign in, they can see what has been happening throughout the morning.  This means that a Project Manager can see what features have been implemented and are ready for review on our autobuild site.

I’m increasingly optimistic about our ability to maintain our “offline” culture in this on-line meeting space.  We always strive to keep a good sense of humor even in stressful situations and Campfire has allowed us to maintain the same levity in our daily communication.  Sure, sometimes we have to force some conversations back on track, but we’re able to have fun and get work done at the same time.

Cursor Keys in Vim: You Mac’ing Me Crazy!

Kevin McFadden
Kevin McFadden, Former Staffer, June 18, 2007 1

Switch from PHP to Ruby on Rails? Check!
Switch from Windows to OS X? Check!
Switch from Vim to TextMate? Check! Well, most of the time.

Vim is useful in many situations, particularly for quick edits (especially as root), and editing files on a remote server. One annoyance I've had during my nine months with a Mac Book Pro is cursor keys in Vim ringing the bell in both insert or command mode instead of changing lines. Upgrading to version 7 via MacPorts yielded the same results. The cursor keys worked after switching my TERM to VT220 from rxvt! The downside was syntax highlighting stopped working.

Luckily, adding VT220 to my Google search parameters turned up a reference to vt100-cursor-keys in the Vim documentation. Add the following code to your .vimrc file to fix the problem:

:set notimeout          " don't timeout on mappings
:set ttimeout           " do timeout on terminal key codes
:set timeoutlen=100     " timeout after 100 msec

But, this only works on your computer since the settings do not propagate when you ssh to another server. A better solution is to change the default value for TERM to either linux or dtterm since the TERM value will be picked up on the remote server. In this day and age, I would bet most servers support both these terminal types.

The easiest way to set this value across all terminal instances is to update /etc/bashrc and restart the terminal session. OS X already has the framework for you to support this change, just add the export line as listed below.

case "$TERM" in
xterm*|rxvt*)
    export TERM=linux # ADD THIS LINE!
    PROMPT_COMMAND='echo -ne "�33]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}�07"'
    ;;
*)
    ;;
esac

This will cause terminal instances reporting itself as rxvt or xterm to use linux instead.

Let me know if you find instances where this does not work or know why Apple decided to default to a TERM that restricts normal functionality.

We're the Developers

at Viget Labs. We write about web development trends, tips, best practices, industry events, and our projects — all with an emphasis on Ruby on Rails.

Upcoming Events

Twin Tech III - 6 PM, January 22 - 22, 9 PM

Refresh the Triangle - 6:30 PM, January 22

Recent Comments

:D

thats exactly what i have been looking for, though i do not need it so badly since memoize arrived…