Close and Go BackBack to Viget

Tips and Tricks

Git with remotes and SVN

Ben Scofield
Ben Scofield, Development Director, April 01, 2008 4

The laziness plugin I’ve been working on is hosted in two different SCM systems: Subversion, at Viget’s SVN repository, and git, at GitHub. I use git in development, which means that pushing out to GitHub is easy - but maintaining a git-svn connection at the same time is a little tricky.

Starting from this post, I finally got things moving. Basically, I created a local branch to interact with SVN independently of the main development; when new features (like the specific exception handling I added recently) are done, I push ‘em out to GitHub normally. Then I switch into the aforementioned local branch, merge from master, and dcommit them up.

The only problem I’ve run into is the occasional conflict on merging from master - and that’s easily resolvable by editing the conflicted file and git adding it before committing and dcommitting.

I long for the day when I can get by on git alone, but until then this appears to be a workable solution.

cURL and Your Rails 2 App

David Eisinger
David Eisinger, Web Developer, March 28, 2008 5

If you’re anything like me, you’ve used cURL to download a batch of MP3 files from the web, or to move a TAR file from one remote server to another. It might come as a surprise, then, that cURL is a full-featured HTTP client, which makes it perfect for interacting with RESTful web services like the ones encouraged by Rails 2. To illustrate, let’s create a small Rails app called ‘tv_show’:

rails tv_show
cd tv_show
script/generate scaffold character name:string action:string
rake db:migrate
script/server

Fire up your web browser and create a few characters. Once you’ve done that, open a new terminal window and try the following:

curl http://localhost:3000/characters.xml

You’ll get a nice XML representation of your characters:

<?xml version"1.0" encoding="UTF-8"?>
<characters type="array">
  <character>
    <id type="integer">1</id>
    <name>George Sr.</name>
    <action>goes to jail</action>
    <created-at type="datetime">2008-03-28T11:01:57-04:00</created-at>
    <updated-at type="datetime">2008-03-28T11:01:57-04:00</updated-at>
  </character>
  <character>
    <id type="integer">2</id>
    <name>Gob</name> 
    <action>rides a Segway</action>
    <created-at type="datetime">2008-03-28T11:02:07-04:00</created-at>
    <updated-at type="datetime">2008-03-28T11:02:12-04:00</updated-at>
  </character>
  <character>
    <id type="integer">3</id>
    <name>Tobias</name>
    <action>wears cutoffs</action>
    <created-at type="datetime">2008-03-28T11:02:20-04:00</created-at>
    <updated-at type="datetime">2008-03-28T11:02:20-04:00</updated-at>
  </character>
</characters>

You can retrieve the representation of a specific character by specifying his ID in the URL:

dce@roflcopter ~ > curl http://localhost:3000/characters/1.xml <?xml version="1.0" encoding="UTF-8"?> <character> <id type="integer">1</id> <name>George Sr.</name> <action>goes to jail</action> <created-at type="datetime">2008-03-28T11:01:57-04:00</created-at> <updated-at type="datetime">2008-03-28T11:01:57-04:00</updated-at> </character>

To create a new character, issue a POST request, use the -X flag to specify the action, and the -d flag to define the request body:

curl -X POST -d "character[name]=Lindsay&character[action]=does+nothing" http://localhost:3000/characters.xml

Here’s where things get interesting: unlike most web browsers, which only support GET and POST, cURL supports the complete set of HTTP actions. If we want to update one of our existing characters, we can issue a PUT request to the URL of that character’s representation, like so:

curl -X PUT -d "character[action]=works+at+clothing+store" http://localhost:3000/characters/4.xml

If we want to delete a character, issue a DELETE request:

curl -X DELETE http://localhost:3000/characters/1.xml

For some more sophisticated uses of REST and Rails, check out rest-client and ActiveResource.

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. 

A Development Community for Viget Labs and Beyond

Every team member here at Viget Labs strives to be an innovator. We members of the development team are no different - that's why we're constantly engaging in community discussions and exploring the unknown that is the next generation of open-source web applications.

Viget Is Hiring!

Viget has job openings for Ruby Developers, Interns, and Front-End Developers. Learn More »

Upcoming Events

Jack Speaks at WebExpo - September 16
Jack's Talking Agile Sept. 16-19

Recent Comments

I have used several different strategies for achieving this, and have settled on the plugin seed_fu to accomplish this task. Because it uses plain ruby files you have quite a few more options for manipulating your seed data as opposed to using fixtures. I have found that this lets me create seed data that is much less brittle than fixtures can be. You can find seed_fu on...